Skip to content

Conversation

@deustis
Copy link
Contributor

@deustis deustis commented Jan 28, 2026

Add pybind11 bindings for S1Interval.

Also add interface notes to the python README

(Part of a series of addressing #524)

@deustis deustis force-pushed the deustis/s1interval_bindings branch 4 times, most recently from 596f5f6 to 85ae427 Compare January 28, 2026 00:42
Add pybind11 bindings for S1Interval.

Also add interface notes to the python README
@deustis deustis force-pushed the deustis/s1interval_bindings branch from 85ae427 to 5bb24fa Compare January 28, 2026 00:49
@deustis
Copy link
Contributor Author

deustis commented Jan 28, 2026

@jmr, next installment here. I've added some updates to the README that explain some of the user-facing interface choices for the python bindings. That might be a good place to start for the review. Feel free to suggest any changes.

pybind_extension(
name = "s2geometry_bindings",
srcs = ["module.cc"],
copts = ["-DNDEBUG"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this. Definitely needs a comment.

The Python bindings follow the C++ API closely but with Pythonic conventions:

**Naming Conventions:**
- Core classes exist within the top-level module; we intend to define submodules for utility classes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would these submodules look like? Maybe leave it as "may define", since I don't really know.

**Properties vs. Methods:**
- Simple coordinate accessors are properties: `point.x`, `point.y`, `interval.lo`, `interval.hi`
- If the C++ class has a trivial set_foo method for the property, then the Python property is mutable (otherwise it is a read-only property).
- Other functions are not properties: `angle.radians()`, `angle.degrees()`, `interval.get_length()`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add get_ prefix is removed.

- `S1Interval(0.0, 4.0)` with `4.0 > π` creates interval where `is_valid()` is `False`
- `S2Point(3.0, 4.0, 0.0)` creates an unnormalized point outside of the unit sphere; use `normalize()` to normalize.
- In **C++** invalid values will typically trigger (`ABSL_DCHECK`) when compiled with debug options.
- In **Python bindings** debug assertions (`ABSL_DCHECK`) are disabled, matching the production optimized C++ behavior.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Why? Isn't this an ODR violation waiting to happen?

- In **Python bindings** debug assertions (`ABSL_DCHECK`) are disabled, matching the production optimized C++ behavior.

**Type Conversions:**
- Python floats map to C++ `double`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems obvious.


Use the following sections to organize functions within the bindings files and tests:

1. **Constructors** - Default constructors and constructors with parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems reasonable, but it's also helpful if the order reflects the C++ order.

"Check if interval is inverted (lo > hi)")

// Geometric operations
.def("get_center", &S1Interval::GetCenter, "Return center of interval")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no get_

"Return directed Hausdorff distance to another interval")
.def("approx_equals", &S1Interval::ApproxEquals,
py::arg("other"), py::arg("max_error") = 1e-15,
"Check if approximately equal")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make the docstrings more informative. max_error is radians, etc.

Can you tell your LLM to copy the docstrings from C++?

py::arg("other"),
"Return directed Hausdorff distance to another interval")
.def("approx_equals", &S1Interval::ApproxEquals,
py::arg("other"), py::arg("max_error") = 1e-15,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way for pybind to get the default from C++?

std::to_string(i.hi()) + ")";
})
.def("__str__", [](const S1Interval& i) {
if (i.is_empty()) return std::string("[∅]");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "[empty]"/"[full]" are more consistent with other places.

I also wonder if we should just delegate to operator<< in C++.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants