diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 4d22727cb4..8d0b516ee2 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -92,23 +92,27 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' needs: [packaging] + environment: pypi + permissions: + id-token: write + attestations: write + contents: read steps: - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - # Downloads all to directories matching the artifact names - uses: actions/download-artifact@v4 + - name: Generate artifact attestation for sdist and wheel + uses: actions/attest-build-provenance@173725a1209d09b31f9d30a3890cf2757ebbff0d # v1.1.2 + with: + subject-path: "*/pybind11*" + - name: Publish standard package uses: pypa/gh-action-pypi-publish@release/v1 with: - password: ${{ secrets.pypi_password }} packages-dir: standard/ - name: Publish global package uses: pypa/gh-action-pypi-publish@release/v1 with: - password: ${{ secrets.pypi_password_global }} packages-dir: global/ diff --git a/docs/changelog.rst b/docs/changelog.rst index e1b7667fae..7135e65311 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -15,6 +15,107 @@ IN DEVELOPMENT Changes will be summarized here periodically. +Version 2.13.0 (June 25, 2024) +------------------------------ + +New Features: + +* Support free-threaded CPython (3.13t). Add ``py::mod_gil_not_used()`` tag to + indicate if a module supports running with the GIL disabled. + `#5148 `_ + +* Support for Python 3.6 was removed. (Official end-of-life: 2021-12-23). + `#5177 `_ + +* ``py::list`` gained a ``.clear()`` method. + `#5153 `_ + + +.. feat(types) + +* Support for ``Union``, ``Optional``, ``type[T]``, ``typing.TypeGuard``, + ``typing.TypeIs``, ``typing.Never``, ``typing.NoReturn`` and + ``typing.Literal`` was added to ``pybind11/typing.h``. + `#5166 `_ + `#5165 `_ + `#5194 `_ + `#5193 `_ + `#5192 `_ + + +.. feat(cmake) + +* In CMake, if ``PYBIND11_USE_CROSSCOMPILING`` is enabled, then + ``CMAKE_CROSSCOMPILING`` will be respected and will keep pybind11 from + accessing the interpreter during configuration. Several CMake variables will + be required in this case, but can be deduced from the environment variable + ``SETUPTOOLS_EXT_SUFFIX``. The default (currently ``OFF``) may be changed in + the future. + `#5083 `_ + + +Bug fixes: + +* A refcount bug (leading to heap-use-after-free) involving trampoline + functions with ``PyObject *`` return type was fixed. + `#5156 `_ + +* Return ``py::ssize_t`` from ``.ref_count()`` instead of ``int``. + `#5139 `_ + +* A subtle bug involving C++ types with unusual ``operator&`` overrides + was fixed. + `#5189 `_ + +* Support Python 3.13 with minor fix, add to CI. + `#5127 `_ + + +.. fix(cmake) + +* Fix mistake affecting old cmake and old boost. + `#5149 `_ + + +Documentation: + +* Build docs updated to feature scikit-build-core and meson-python, and updated + setuptools instructions. + `#5168 `_ + + +Tests: + +* Avoid immortal objects in tests. + `#5150 `_ + + +CI: + +* Compile against Python 3.13t in CI. + +* Use ``macos-13`` (Intel) for CI jobs for now (will drop Python 3.7 soon). + `#5109 `_ + +* Releases now have artifact attestations, visible at + https://github.com/pybind/pybind11/attestations. + `#5196 `_ + +Other: + +* Some cleanup in preparation for 3.13 support. + `#5137 `_ + +* Avoid a warning by ensuring an iterator end check is included in release mode. + `#5129 `_ + +* Bump max cmake to 3.29. + `#5075 `_ + +* Update docs and noxfile. + `#5071 `_ + + Version 2.12.0 (March 27, 2024) ------------------------------- diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 698421d840..fc66e6f756 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -10,12 +10,12 @@ #pragma once #define PYBIND11_VERSION_MAJOR 2 -#define PYBIND11_VERSION_MINOR 13 +#define PYBIND11_VERSION_MINOR 14 #define PYBIND11_VERSION_PATCH 0.dev1 // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html // Additional convention: 0xD = dev -#define PYBIND11_VERSION_HEX 0x020D00D1 +#define PYBIND11_VERSION_HEX 0x020E00D1 // Define some generic pybind11 helper macros for warning management. // diff --git a/include/pybind11/typing.h b/include/pybind11/typing.h index e0d0e45c45..cf70b739ed 100644 --- a/include/pybind11/typing.h +++ b/include/pybind11/typing.h @@ -70,14 +70,55 @@ class Type : public type { template class Union : public object { + PYBIND11_OBJECT_DEFAULT(Union, object, PyObject_Type) using object::object; }; template class Optional : public object { + PYBIND11_OBJECT_DEFAULT(Optional, object, PyObject_Type) using object::object; }; +template +class TypeGuard : public bool_ { + using bool_::bool_; +}; + +template +class TypeIs : public bool_ { + using bool_::bool_; +}; + +class NoReturn : public none { + using none::none; +}; + +class Never : public none { + using none::none; +}; + +#if defined(__cpp_nontype_template_parameter_class) +template +struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); } + char name[N]; +}; + +template +class Literal : public object { + PYBIND11_OBJECT_DEFAULT(Literal, object, PyObject_Type) +}; + +// Example syntax for creating a TypeVar. +// typedef typing::TypeVar<"T"> TypeVarT; +template +class TypeVar : public object { + PYBIND11_OBJECT_DEFAULT(TypeVar, object, PyObject_Type) + using object::object; +}; +#endif + PYBIND11_NAMESPACE_END(typing) PYBIND11_NAMESPACE_BEGIN(detail) @@ -153,5 +194,38 @@ struct handle_type_name> { static constexpr auto name = const_name("Optional[") + make_caster::name + const_name("]"); }; +template +struct handle_type_name> { + static constexpr auto name = const_name("TypeGuard[") + make_caster::name + const_name("]"); +}; + +template +struct handle_type_name> { + static constexpr auto name = const_name("TypeIs[") + make_caster::name + const_name("]"); +}; + +template <> +struct handle_type_name { + static constexpr auto name = const_name("NoReturn"); +}; + +template <> +struct handle_type_name { + static constexpr auto name = const_name("Never"); +}; + +#if defined(__cpp_nontype_template_parameter_class) +template +struct handle_type_name> { + static constexpr auto name = const_name("Literal[") + + pybind11::detail::concat(const_name(Literals.name)...) + + const_name("]"); +}; +template +struct handle_type_name> { + static constexpr auto name = const_name(StrLit.name); +}; +#endif + PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/noxfile.py b/noxfile.py index be75def5d4..e9a2fa8fee 100644 --- a/noxfile.py +++ b/noxfile.py @@ -45,7 +45,7 @@ def tests_packaging(session: nox.Session) -> None: Run the packaging tests. """ - session.install("-r", "tests/requirements.txt") + session.install("-r", "tests/requirements.txt", "pip") session.run("pytest", "tests/extra_python_package", *session.posargs) diff --git a/pybind11/_version.py b/pybind11/_version.py index f71abbcdb8..c5cc1a0b09 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -8,5 +8,5 @@ def _to_int(s: str) -> int | str: return s -__version__ = "2.13.0.dev1" +__version__ = "2.14.0.dev1" version_info = tuple(_to_int(s) for s in __version__.split(".")) diff --git a/setup.cfg b/setup.cfg index 79d75d0cdb..2beca780f9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,7 @@ classifiers = Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 License :: OSI Approved :: BSD License Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: CPython diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index e5a318ad84..6b347894b0 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -109,6 +109,26 @@ void m_defs(py::module_ &m) { } // namespace handle_from_move_only_type_with_operator_PyObject +#if defined(__cpp_nontype_template_parameter_class) +namespace literals { +enum Color { RED = 0, BLUE = 1 }; + +typedef py::typing::Literal<"26", + "0x1A", + "\"hello world\"", + "b\"hello world\"", + "u\"hello world\"", + "True", + "Color.RED", + "None"> + LiteralFoo; +} // namespace literals +namespace typevar { +typedef py::typing::TypeVar<"T"> TypeVarT; +typedef py::typing::TypeVar<"V"> TypeVarV; +} // namespace typevar +#endif + TEST_SUBMODULE(pytypes, m) { m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); }); @@ -844,7 +864,8 @@ TEST_SUBMODULE(pytypes, m) { m.def("annotate_iterator_int", [](const py::typing::Iterator &) {}); m.def("annotate_fn", [](const py::typing::Callable, py::str)> &) {}); - m.def("annotate_type", [](const py::typing::Type &) {}); + + m.def("annotate_type", [](const py::typing::Type &t) -> py::type { return t; }); m.def("annotate_union", [](py::typing::List> l, @@ -861,10 +882,44 @@ TEST_SUBMODULE(pytypes, m) { [](py::typing::List> &l) -> py::typing::List> { return l; }); + m.def("annotate_union_to_object", + [](py::typing::Union &o) -> py::object { return o; }); + m.def("annotate_optional", [](py::list &list) -> py::typing::List> { list.append(py::str("hi")); list.append(py::none()); return list; }); + + m.def("annotate_type_guard", [](py::object &o) -> py::typing::TypeGuard { + return py::isinstance(o); + }); + m.def("annotate_type_is", + [](py::object &o) -> py::typing::TypeIs { return py::isinstance(o); }); + + m.def("annotate_no_return", []() -> py::typing::NoReturn { throw 0; }); + m.def("annotate_never", []() -> py::typing::Never { throw 0; }); + + m.def("annotate_optional_to_object", + [](py::typing::Optional &o) -> py::object { return o; }); + +#if defined(__cpp_nontype_template_parameter_class) + py::enum_(m, "Color") + .value("RED", literals::Color::RED) + .value("BLUE", literals::Color::BLUE); + + m.def("annotate_literal", [](literals::LiteralFoo &o) -> py::object { return o; }); + m.def("annotate_generic_containers", + [](const py::typing::List &l) -> py::typing::List { + return l; + }); + + m.def("annotate_listT_to_T", + [](const py::typing::List &l) -> typevar::TypeVarT { return l[0]; }); + m.def("annotate_object_to_T", [](const py::object &o) -> typevar::TypeVarT { return o; }); + m.attr("if_defined__cpp_nontype_template_parameter_class") = true; +#else + m.attr("if_defined__cpp_nontype_template_parameter_class") = false; +#endif } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 8e35c70733..92a3a36bf4 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -960,7 +960,7 @@ def test_fn_annotations(doc): def test_type_annotation(doc): - assert doc(m.annotate_type) == "annotate_type(arg0: type[int]) -> None" + assert doc(m.annotate_type) == "annotate_type(arg0: type[int]) -> type" def test_union_annotations(doc): @@ -977,8 +977,67 @@ def test_union_typing_only(doc): ) +def test_union_object_annotations(doc): + assert ( + doc(m.annotate_union_to_object) + == "annotate_union_to_object(arg0: Union[int, str]) -> object" + ) + + def test_optional_annotations(doc): assert ( doc(m.annotate_optional) == "annotate_optional(arg0: list) -> list[Optional[str]]" ) + + +def test_type_guard_annotations(doc): + assert ( + doc(m.annotate_type_guard) + == "annotate_type_guard(arg0: object) -> TypeGuard[str]" + ) + + +def test_type_is_annotations(doc): + assert doc(m.annotate_type_is) == "annotate_type_is(arg0: object) -> TypeIs[str]" + + +def test_no_return_annotation(doc): + assert doc(m.annotate_no_return) == "annotate_no_return() -> NoReturn" + + +def test_never_annotation(doc): + assert doc(m.annotate_never) == "annotate_never() -> Never" + + +def test_optional_object_annotations(doc): + assert ( + doc(m.annotate_optional_to_object) + == "annotate_optional_to_object(arg0: Optional[int]) -> object" + ) + + +@pytest.mark.skipif( + not m.if_defined__cpp_nontype_template_parameter_class, + reason="C++20 feature not available.", +) +def test_literal(doc): + assert ( + doc(m.annotate_literal) + == 'annotate_literal(arg0: Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]) -> object' + ) + + +@pytest.mark.skipif( + not m.if_defined__cpp_nontype_template_parameter_class, + reason="C++20 feature not available.", +) +def test_typevar(doc): + assert ( + doc(m.annotate_generic_containers) + == "annotate_generic_containers(arg0: list[T]) -> list[V]" + ) + + assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T" + + assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T" diff --git a/tools/make_changelog.py b/tools/make_changelog.py index 89cf664835..daa966f204 100755 --- a/tools/make_changelog.py +++ b/tools/make_changelog.py @@ -32,6 +32,8 @@ missing = [] cats_descr = { "feat": "New Features", + "feat(types)": "", + "feat(cmake)": "", "fix": "Bug fixes", "fix(types)": "", "fix(cmake)": "",