Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(types): update render for buffer sequence and handle #4831

Merged

Conversation

sizmailov
Copy link
Contributor

@sizmailov sizmailov commented Sep 1, 2023

Description

Change docstring render for py::buffer, py::sequence, py::handle and py::object

py::buffer -> Buffer
py::sequence -> Sequence
py::handle ->  Any

Suggested changelog entry:

Change docstring render for ``py::buffer``, ``py::sequence`` and ``py::handle`` (to ``Buffer``, ``Sequence``, ``Any``).

Follow up to #4829

@sizmailov sizmailov changed the title Update render for buffer sequence handle Update render for buffer sequence handle and object Sep 1, 2023
Copy link
Collaborator

@rwgk rwgk left a comment

Choose a reason for hiding this comment

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

Thanks, looks great!
I ran this by a typing expert on my team and they confirmed that Sequence and Buffer are good choices.

@wjakob
Copy link
Member

wjakob commented Sep 7, 2023

AFAIK, the main difference between Any and object is that the former is unconstrained, and the latter is maximally constrained.

For example, when a function returns Any, the caller is allowed to call any method on it, and MyPy will accept this. When it returns object, the caller can only assume that this is an object but no more. In other words, it can invoke methods provided by all object instances (e.g., .__hash__()) but no more specific ones.

So Any basically turns off the type checker. To me it's not a clear-case situation that pybind11 should always use Any instead of Object (for py::handle and py::object), which is what this PR proposes. It seems to me like a developer/project-specific decision that would need to be decided on a case-by-case basis.

The Sequence and Buffer changes seem less controversial.

@rwgk
Copy link
Collaborator

rwgk commented Sep 7, 2023

AFAIK, the main difference between Any and object is that the former is unconstrained, and the latter is maximally constrained.

Correct.

Did you see my other comment?

#4829 (comment)

The lesson learned was:

object didn't serve any practical purpose; it's just a "you cannot do anything with this" road block.

Any turns out to be much more useful in practice, although yes, it's maximally permissive.

@sizmailov
Copy link
Contributor Author

The usage and behavior of py::object resemble Python's Any more than object. Thus, Any is a better annotation for py::object.

I find it hard to envision a use case where I'd prefer an object annotation. In my opinion, bare object instances are primarily useful as sentinels.

@rwgk
Copy link
Collaborator

rwgk commented Sep 8, 2023

controversial.

What we have here is an ambiguity that needs to be resolved by a judgement call.

The mapping from py::object to object is initially intuitive, but is it actually useful?

It seems to me like a developer/project-specific decision that would need to be decided on a case-by-case basis.

Yes, that would be ideal. But we do not have that technology. Until someone figures out how to deliver that ideal solution, we have to choose between mapping to object or Any.

@sizmailov and I are approaching this empirically, from a practical viewpoint:

@wjakob
Copy link
Member

wjakob commented Sep 8, 2023

I agree it's convenient for the person writing bindings because MyPy checks will pass. But this also sounds extremely dangerous. If this was MyPy's decision, I doubt that they would be happy if we use Any as a default here.

Another route would be to make docstring generation more flexible. To contrast, let me show what's implemented in nanobind:

  1. Functions can take nb::handle_t as input. That means: do type checking for type T and use that type annotation in the binding docstrings. But when accessed, this is actually a Python handle (i.e. a wrapper type). This mainly makes sense for arguments rather than return values.

  2. Functions can take/return nb::typed, which provides complete control over the type annotation.

  3. Functions can be bound with nb::raw_doc, which replaces the function docstring with the given text. This includes the overload listing and can, again, be used to take complete control over type annotations.

To me, these seem better solutions than taking pybind11's default annotation and switching it to a dangerous default. The developer should have to actually think about the types and specify something that makes sense. If they aren't happy with the strict results, then Any is still available via options 2 and 3.

@rwgk
Copy link
Collaborator

rwgk commented Sep 8, 2023

I wasn't aware of the nanobind features, that is nice!

I guess keeping object could act as a forcing/motivating function for someone to port the nanobind features into pybind11 sooner rather than later.

Waiting for @sizmailov to comment.

@sizmailov
Copy link
Contributor Author

I suggest removing the controversial py::object -> Any renaming from this PR, keeping the other three changes.

Let's create a separate issue for py::object - > Any renaming. There we could summon people from MyPy / Pybind community.

For me, the issue is not crucial since I'm already post-processing docstrings in pybind11-stubgen to generate stubs and documentation.

@sizmailov
Copy link
Contributor Author

I've reverted 7861dcf to clear the path to the rest of the changes in the PR.

Note, that the opt-in to py::object -> Any rename is straightforward (unlike opt-out) if you want to use it in your own bindings:

// Include this before any pybind code execution in all translation units
// to render `pybind11::object` as `Any` in docstrings
namespace pybind11::detail {
template <>
struct handle_type_name<object> {
    static constexpr auto name = const_name("Any");
};
}

@sizmailov sizmailov changed the title Update render for buffer sequence handle and object Update render for buffer sequence and handle Sep 11, 2023
Copy link
Collaborator

@rwgk rwgk left a comment

Choose a reason for hiding this comment

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

@wjakob Is this good to merge now?

@rwgk rwgk merged commit b457367 into pybind:master Sep 12, 2023
85 checks passed
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Sep 12, 2023
@henryiii henryiii changed the title Update render for buffer sequence and handle fix(types): update render for buffer sequence and handle Nov 15, 2023
@henryiii henryiii removed the needs changelog Possibly needs a changelog entry label Mar 27, 2024
rwgk pushed a commit to rwgk/pybind11 that referenced this pull request Jun 12, 2024
* fix: Use lowercase builtin collection names (pybind#4833)

* Update render for buffer sequence and handle  (pybind#4831)

* fix: Add capitalize render name of `py::buffer` and `py::sequence`

* fix: Render `py::handle` same way as `py::object`

* tests: Fix tests `handle` -> `object`

* tests: Test capitaliation of `py::sequence` and `py::buffer`

* style: pre-commit fixes

* fix: Render `py::object` as `Any`

* Revert "fix: Render `py::object` as `Any`"

This reverts commit 7861dcf.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]>

* fix: Missing typed variants of `iterator` and `iterable` (pybind#4832)

* Fix small bug introduced with PR pybind#4735 (pybind#4845)

* Bug fix: `result[0]` called if `result.empty()`

* Add unit test that fails without the fix.

* fix(cmake): correctly detect FindPython policy and better warning (pybind#4806)

* fix(cmake): support DEBUG_POSTFIX correctly (pybind#4761)

* cmake: split extension

Into suffix and debug postfix. Pybind11 is currently treating both as
suffix, which is problematic when something else defines the
DEBUG_POSTFIX because they will be concatenated.

pybind11_extension sets SUFFIX to _d.something and if DEBUG_POSTFIX is
set to _d.

    _d + _d.something = _d_d.something

The issue has been reported at:

pybind#4699

* style: pre-commit fixes

* fix(cmake): support postfix for old FindPythonInterp mode too

Signed-off-by: Henry Schreiner <[email protected]>

---------

Signed-off-by: Henry Schreiner <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <[email protected]>

* Avoid copy in iteration by using const auto & (pybind#4861)

This change is fixing a Coverity AUTO_CAUSES_COPY issues.

* Add 2 missing `throw error_already_set();` (pybind#4863)

Fixes oversights in PR pybind#4570.

* MAINT: Include `numpy._core` imports (pybind#4857)

* MAINT: Include numpy._core imports

* style: pre-commit fixes

* Apply review comments

* style: pre-commit fixes

* Add no-inline attribute

* Select submodule name based on numpy version

* style: pre-commit fixes

* Update pre-commit check

* Add error_already_set and simplify if statement

* Update .pre-commit-config.yaml

Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]>

* MAINT: Remove np.int_ (pybind#4867)

* chore(deps): update pre-commit hooks (pybind#4868)

* chore(deps): update pre-commit hooks

updates:
- [github.com/psf/black-pre-commit-mirror: 23.7.0 → 23.9.1](psf/black-pre-commit-mirror@23.7.0...23.9.1)
- [github.com/astral-sh/ruff-pre-commit: v0.0.287 → v0.0.292](astral-sh/ruff-pre-commit@v0.0.287...v0.0.292)
- [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](codespell-project/codespell@v2.2.5...v2.2.6)
- [github.com/shellcheck-py/shellcheck-py: v0.9.0.5 → v0.9.0.6](shellcheck-py/shellcheck-py@v0.9.0.5...v0.9.0.6)
- [github.com/PyCQA/pylint: v3.0.0a7 → v3.0.0](pylint-dev/pylint@v3.0.0a7...v3.0.0)

* Update .pre-commit-config.yaml

* style: pre-commit fixes

* Update .pre-commit-config.yaml

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <[email protected]>

---------

Signed-off-by: Henry Schreiner <[email protected]>
Co-authored-by: Sergei Izmailov <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <[email protected]>
Co-authored-by: László Papp <[email protected]>
Co-authored-by: Oleksandr Pavlyk <[email protected]>
Co-authored-by: Mateusz Sokół <[email protected]>
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.

4 participants