From f3e0602802c7840992c97f4960515777cad6a5c7 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 23 Jul 2023 11:10:10 -0700 Subject: [PATCH 1/5] Add command to check for vX.Y.Z tag vs pybind11/_version.py consistency. (#4757) * Add command to check for vX.Y.Z tag vs pybind11/_version.py consistency. Piggy-backing hints for converting changelog to release message. * Simpler way of double-checking version number in sources vs. git tag * Even simpler. * Fix rst rendering (hopefully) and remove stray dots. * [ci skip] Trying more to make GitHub rst renderer show this nicely. * [ci skip] Fix up RST rendering issues. Lesson learned: This is NOT GOOD: ``` - Bullet nesting level 1. - Bullet nesting level 2. ``` This is BETTER: ``` - Bullet nesting level 1. - Bullet nesting level 2. ``` Also consistently adding empty lines between bullet points, to make the .rst file easier to read. Also piggy-backing a few very minor enhancements. --- docs/release.rst | 131 +++++++++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 45 deletions(-) diff --git a/docs/release.rst b/docs/release.rst index 4d390a07c..20b53a355 100644 --- a/docs/release.rst +++ b/docs/release.rst @@ -15,8 +15,8 @@ For example: For beta, ``PYBIND11_VERSION_PATCH`` should be ``Z.b1``. RC's can be ``Z.rc1``. Always include the dot (even though PEP 440 allows it to be dropped). For a -final release, this must be a simple integer. There is also a HEX version of -the version just below. +final release, this must be a simple integer. There is also +``PYBIND11_VERSION_HEX`` just below that needs to be updated. To release a new version of pybind11: @@ -26,56 +26,93 @@ If you don't have nox, you should either use ``pipx run nox`` instead, or use ``pipx install nox`` or ``brew install nox`` (Unix). - Update the version number - - Update ``PYBIND11_VERSION_MAJOR`` etc. in - ``include/pybind11/detail/common.h``. PATCH should be a simple integer. - - Update the version HEX just below, as well. - - Update ``pybind11/_version.py`` (match above) - - Run ``nox -s tests_packaging`` to ensure this was done correctly. - - Ensure that all the information in ``setup.cfg`` is up-to-date, like - supported Python versions. - - Add release date in ``docs/changelog.rst`` and integrate the output of - ``nox -s make_changelog``. - - Note that the ``make_changelog`` command inspects - `needs changelog `_. - - Manually clear the ``needs changelog`` labels using the GitHub web - interface (very easy: start by clicking the link above). - - ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it - fails due to a known flake issue, either ignore or restart CI.) -- Add a release branch if this is a new MINOR version, or update the existing release branch if it is a patch version - - New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y`` - - Update branch: ``git checkout vX.Y``, ``git merge ``, ``git push`` + + - Update ``PYBIND11_VERSION_MAJOR`` etc. in + ``include/pybind11/detail/common.h``. PATCH should be a simple integer. + + - Update ``PYBIND11_VERSION_HEX`` just below as well. + + - Update ``pybind11/_version.py`` (match above). + + - Run ``nox -s tests_packaging`` to ensure this was done correctly. + + - Ensure that all the information in ``setup.cfg`` is up-to-date, like + supported Python versions. + + - Add release date in ``docs/changelog.rst`` and integrate the output of + ``nox -s make_changelog``. + + - Note that the ``make_changelog`` command inspects + `needs changelog `_. + + - Manually clear the ``needs changelog`` labels using the GitHub web + interface (very easy: start by clicking the link above). + + - ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it + fails due to a known flake issue, either ignore or restart CI.) + +- Add a release branch if this is a new MINOR version, or update the existing + release branch if it is a patch version + + - New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y`` + + - Update branch: ``git checkout vX.Y``, ``git merge ``, ``git push`` + - Update tags (optional; if you skip this, the GitHub release makes a - non-annotated tag for you) - - ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``. - - ``git push --tags``. + non-annotated tag for you) + + - ``git tag -a vX.Y.Z -m 'vX.Y.Z release'`` + + - ``grep ^__version__ pybind11/_version.py`` + + - Last-minute consistency check: same as tag? + + - ``git push --tags`` + - Update stable - - ``git checkout stable`` - - ``git merge -X theirs vX.Y.Z`` - - ``git diff vX.Y.Z`` - - Carefully review and reconcile any diffs. There should be none. - - ``git push`` + + - ``git checkout stable`` + + - ``git merge -X theirs vX.Y.Z`` + + - ``git diff vX.Y.Z`` + + - Carefully review and reconcile any diffs. There should be none. + + - ``git push`` + - Make a GitHub release (this shows up in the UI, sends new release notifications to users watching releases, and also uploads PyPI packages). (Note: if you do not use an existing tag, this creates a new lightweight tag for you, so you could skip the above step.) - - GUI method: Under `releases `_ - click "Draft a new release" on the far right, fill in the tag name - (if you didn't tag above, it will be made here), fill in a release name - like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog - into the description (usually ``cat docs/changelog.rst | pandoc -f rst -t gfm``). - Check "pre-release" if this is a beta/RC. - - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` - If this is a pre-release, add ``-p``. + + - GUI method: Under `releases `_ + click "Draft a new release" on the far right, fill in the tag name + (if you didn't tag above, it will be made here), fill in a release name + like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog + into the description. You can use ``cat docs/changelog.rst | pandoc -f rst -t gfm``, + then manually remove line breaks and strip links to PRs and issues, + e.g. to a bare ``#1234``, without the surrounding ``<...>_`` hyperlink markup. + Check "pre-release" if this is a beta/RC. + + - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` + If this is a pre-release, add ``-p``. - Get back to work - - Make sure you are on master, not somewhere else: ``git checkout master`` - - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to - ``0.dev1`` and increment MINOR). - - Update ``pybind11/_version.py`` to match - - Run ``nox -s tests_packaging`` to ensure this was done correctly. - - If the release was a new MINOR version, add a new `IN DEVELOPMENT` - section in ``docs/changelog.rst``. - - ``git add``, ``git commit``, ``git push`` + + - Make sure you are on master, not somewhere else: ``git checkout master`` + + - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to + ``0.dev1`` and increment MINOR). + + - Update ``pybind11/_version.py`` to match. + + - Run ``nox -s tests_packaging`` to ensure this was done correctly. + + - If the release was a new MINOR version, add a new ``IN DEVELOPMENT`` + section in ``docs/changelog.rst``. + + - ``git add``, ``git commit``, ``git push`` If a version branch is updated, remember to set PATCH to ``1.dev1``. @@ -92,7 +129,11 @@ merge it if there are no issues. Manual packaging ^^^^^^^^^^^^^^^^ -If you need to manually upload releases, you can download the releases from the job artifacts and upload them with twine. You can also make the files locally (not recommended in general, as your local directory is more likely to be "dirty" and SDists love picking up random unrelated/hidden files); this is the procedure: +If you need to manually upload releases, you can download the releases from +the job artifacts and upload them with twine. You can also make the files +locally (not recommended in general, as your local directory is more likely +to be "dirty" and SDists love picking up random unrelated/hidden files); +this is the procedure: .. code-block:: bash From 17b614303f2176398ba3bbcec3c237628447bde5 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 3 Aug 2023 13:36:29 -0700 Subject: [PATCH 2/5] clang 17 compatibility fixes (#4767) * Copy clang 17 compatibility fixes from PR #4762 to a separate PR. * Add gcc:13 C++20 * Add silkeh/clang:16-bullseye C++20 --- .github/workflows/ci.yml | 4 ++++ include/pybind11/cast.h | 10 +++++++++- include/pybind11/pytypes.h | 10 +++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48f7c5e93..46e88132c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -302,6 +302,9 @@ jobs: - clang: 15 std: 20 container_suffix: "-bullseye" + - clang: 16 + std: 20 + container_suffix: "-bullseye" name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}" @@ -465,6 +468,7 @@ jobs: - { gcc: 10, std: 17 } - { gcc: 11, std: 20 } - { gcc: 12, std: 20 } + - { gcc: 13, std: 20 } name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64" container: "gcc:${{ matrix.gcc }}" diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index db3934118..b3c8ebe17 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1377,7 +1377,15 @@ inline namespace literals { /** \rst String literal version of `arg` \endrst */ -constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +constexpr arg +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5 +operator"" _a // gcc 4.8.5 insists on having a space (hard error). +#else +operator""_a // clang 17 generates a deprecation warning if there is a space. +#endif + (const char *name, size_t) { + return arg(name); +} } // namespace literals PYBIND11_NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 64aad6347..580a4658e 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1612,7 +1612,15 @@ inline namespace literals { /** \rst String literal version of `str` \endrst */ -inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +inline str +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5 +operator"" _s // gcc 4.8.5 insists on having a space (hard error). +#else +operator""_s // clang 17 generates a deprecation warning if there is a space. +#endif + (const char *s, size_t size) { + return {s, size}; +} } // namespace literals /// \addtogroup pytypes From 413e6328dc0879179f1477421aaec03b700fd121 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Aug 2023 22:11:25 -0700 Subject: [PATCH 3/5] chore(deps): update pre-commit hooks (#4770) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.3.0 → 23.7.0](https://github.com/psf/black/compare/23.3.0...23.7.0) - [github.com/astral-sh/ruff-pre-commit: v0.0.276 → v0.0.281](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.276...v0.0.281) - [github.com/asottile/blacken-docs: 1.14.0 → 1.15.0](https://github.com/asottile/blacken-docs/compare/1.14.0...1.15.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 86ac965d9..c7f596342 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,13 +32,13 @@ repos: # Black, the code formatter, natively supports pre-commit - repo: https://github.com/psf/black - rev: "23.3.0" # Keep in sync with blacken-docs + rev: "23.7.0" # Keep in sync with blacken-docs hooks: - id: black # Ruff, the Python auto-correcting linter written in Rust - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.276 + rev: v0.0.281 hooks: - id: ruff args: ["--fix", "--show-fixes"] @@ -84,7 +84,7 @@ repos: # Also code format the docs - repo: https://github.com/asottile/blacken-docs - rev: "1.14.0" + rev: "1.15.0" hooks: - id: blacken-docs additional_dependencies: From 9ad7e827a2e78516321cde6976ec9b26101f2b02 Mon Sep 17 00:00:00 2001 From: "Keto D. Zhang" Date: Thu, 3 Aug 2023 22:14:23 -0700 Subject: [PATCH 4/5] docs: Remove upper bound on pybind11 in example pyproject.toml for setuptools (#4774) * docs: Remove upper bound on pybind11 in example pyproject.toml for setuptools * Update docs/compiling.rst --------- Co-authored-by: Henry Schreiner --- docs/compiling.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compiling.rst b/docs/compiling.rst index 1fd098bec..9d53904c4 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -143,7 +143,7 @@ Your ``pyproject.toml`` file will likely look something like this: .. code-block:: toml [build-system] - requires = ["setuptools>=42", "wheel", "pybind11~=2.6.1"] + requires = ["setuptools>=42", "pybind11>=2.6.1"] build-backend = "setuptools.build_meta" .. note:: From f8703154ece6ddc098db6a91c3e2f30ceacbe6b4 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 4 Aug 2023 01:48:57 -0400 Subject: [PATCH 5/5] Provide better type hints for a variety of generic types (#4259) * Provide better type hints for a variety of generic types * Makes better documentation * tuple, dict, list, set, function * Move to py::typing * style: pre-commit fixes * Update copyright line with correct year and actual author. The author information was copy-pasted from the git log output. --------- Co-authored-by: Ralf W. Grosse-Kunstleve Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- CMakeLists.txt | 3 +- docs/advanced/misc.rst | 29 +++++++ include/pybind11/typing.h | 97 ++++++++++++++++++++++++ tests/extra_python_package/test_files.py | 1 + tests/test_pytypes.cpp | 10 +++ tests/test_pytypes.py | 35 +++++++++ 6 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 include/pybind11/typing.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 87ec10346..15fa79908 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,8 @@ set(PYBIND11_HEADERS include/pybind11/stl.h include/pybind11/stl_bind.h include/pybind11/stl/filesystem.h - include/pybind11/type_caster_pyobject_ptr.h) + include/pybind11/type_caster_pyobject_ptr.h + include/pybind11/typing.h) # Compare with grep and warn if mismatched if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) diff --git a/docs/advanced/misc.rst b/docs/advanced/misc.rst index 805ec838f..ddd7f3937 100644 --- a/docs/advanced/misc.rst +++ b/docs/advanced/misc.rst @@ -398,3 +398,32 @@ before they are used as a parameter or return type of a function: pyFoo.def(py::init()); pyBar.def(py::init()); } + +Setting inner type hints in docstrings +====================================== + +When you use pybind11 wrappers for ``list``, ``dict``, and other generic python +types, the docstring will just display the generic type. You can convey the +inner types in the docstring by using a special 'typed' version of the generic +type. + +.. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.def("pass_list_of_str", [](py::typing::List arg) { + // arg can be used just like py::list + )); + } + +The resulting docstring will be ``pass_list_of_str(arg0: list[str]) -> None``. + +The following special types are available in ``pybind11/typing.h``: + +* ``py::Tuple`` +* ``py::Dict`` +* ``py::List`` +* ``py::Set`` +* ``py::Callable`` + +.. warning:: Just like in python, these are merely hints. They don't actually + enforce the types of their contents at runtime or compile time. diff --git a/include/pybind11/typing.h b/include/pybind11/typing.h new file mode 100644 index 000000000..74fd82eac --- /dev/null +++ b/include/pybind11/typing.h @@ -0,0 +1,97 @@ +/* + pybind11/typing.h: Convenience wrapper classes for basic Python types + with more explicit annotations. + + Copyright (c) 2023 Dustin Spicuzza + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "cast.h" +#include "pytypes.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(typing) + +/* + The following types can be used to direct pybind11-generated docstrings + to have have more explicit types (e.g., `list[str]` instead of `list`). + Just use these in place of existing types. + + There is no additional enforcement of types at runtime. +*/ + +template +class Tuple : public tuple { + using tuple::tuple; +}; + +template +class Dict : public dict { + using dict::dict; +}; + +template +class List : public list { + using list::list; +}; + +template +class Set : public set { + using set::set; +}; + +template +class Callable; + +template +class Callable : public function { + using function::function; +}; + +PYBIND11_NAMESPACE_END(typing) + +PYBIND11_NAMESPACE_BEGIN(detail) + +template +struct handle_type_name> { + static constexpr auto name + = const_name("tuple[") + concat(make_caster::name...) + const_name("]"); +}; + +template <> +struct handle_type_name> { + // PEP 484 specifies this syntax for an empty tuple + static constexpr auto name = const_name("tuple[()]"); +}; + +template +struct handle_type_name> { + static constexpr auto name = const_name("dict[") + make_caster::name + const_name(", ") + + make_caster::name + const_name("]"); +}; + +template +struct handle_type_name> { + static constexpr auto name = const_name("list[") + make_caster::name + const_name("]"); +}; + +template +struct handle_type_name> { + static constexpr auto name = const_name("set[") + make_caster::name + const_name("]"); +}; + +template +struct handle_type_name> { + using retval_type = conditional_t::value, void_type, Return>; + static constexpr auto name = const_name("Callable[[") + concat(make_caster::name...) + + const_name("], ") + make_caster::name + + const_name("]"); +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 57387dd8b..e3d881c0b 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -44,6 +44,7 @@ main_headers = { "include/pybind11/stl.h", "include/pybind11/stl_bind.h", "include/pybind11/type_caster_pyobject_ptr.h", + "include/pybind11/typing.h", } detail_headers = { diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index b4ee64289..cb2f76c03 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -7,6 +7,8 @@ BSD-style license that can be found in the LICENSE file. */ +#include + #include "pybind11_tests.h" #include @@ -820,4 +822,12 @@ TEST_SUBMODULE(pytypes, m) { a >>= b; return a; }); + + m.def("annotate_tuple_float_str", [](const py::typing::Tuple &) {}); + m.def("annotate_tuple_empty", [](const py::typing::Tuple<> &) {}); + m.def("annotate_dict_str_int", [](const py::typing::Dict &) {}); + m.def("annotate_list_int", [](const py::typing::List &) {}); + m.def("annotate_set_str", [](const py::typing::Set &) {}); + m.def("annotate_fn", + [](const py::typing::Callable, py::str)> &) {}); } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index eda7a20a9..e88a9328f 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -896,3 +896,38 @@ def test_inplace_lshift(a, b): def test_inplace_rshift(a, b): expected = a >> b assert m.inplace_rshift(a, b) == expected + + +def test_tuple_nonempty_annotations(doc): + assert ( + doc(m.annotate_tuple_float_str) + == "annotate_tuple_float_str(arg0: tuple[float, str]) -> None" + ) + + +def test_tuple_empty_annotations(doc): + assert ( + doc(m.annotate_tuple_empty) == "annotate_tuple_empty(arg0: tuple[()]) -> None" + ) + + +def test_dict_annotations(doc): + assert ( + doc(m.annotate_dict_str_int) + == "annotate_dict_str_int(arg0: dict[str, int]) -> None" + ) + + +def test_list_annotations(doc): + assert doc(m.annotate_list_int) == "annotate_list_int(arg0: list[int]) -> None" + + +def test_set_annotations(doc): + assert doc(m.annotate_set_str) == "annotate_set_str(arg0: set[str]) -> None" + + +def test_fn_annotations(doc): + assert ( + doc(m.annotate_fn) + == "annotate_fn(arg0: Callable[[list[str], str], int]) -> None" + )