diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d8d1eed8c..2c7d17083 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,3 @@ updates: directory: "/" schedule: interval: "daily" - ignore: - # Official actions have moving tags like v1 - - dependency-name: "actions/*" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57dd7e066..e356074b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,7 @@ jobs: - '3.6' - '3.9' - '3.10' + - '3.11-dev' - 'pypy-3.7' - 'pypy-3.8' - 'pypy-3.9' @@ -66,7 +67,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -186,8 +187,8 @@ jobs: - python-version: "3.9" python-debug: true valgrind: true - # - python-version: "3.11-dev" - # python-debug: false + - python-version: "3.11-dev" + python-debug: false name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" runs-on: ubuntu-latest @@ -687,7 +688,9 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" - name: Install Doxygen run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04 @@ -735,7 +738,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x86 @@ -788,7 +791,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x86 @@ -836,7 +839,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} diff --git a/.github/workflows/ci_sh_def.yml b/.github/workflows/ci_sh_def.yml index 8719b36d5..c565941b5 100644 --- a/.github/workflows/ci_sh_def.yml +++ b/.github/workflows/ci_sh_def.yml @@ -43,6 +43,7 @@ jobs: - '3.6' - '3.9' - '3.10' + - '3.11-dev' - 'pypy-3.7' - 'pypy-3.8' - 'pypy-3.9' @@ -78,7 +79,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -201,8 +202,8 @@ jobs: - python-version: "3.9" python-debug: true valgrind: true - # - python-version: "3.11-dev" - # python-debug: false + - python-version: "3.11-dev" + python-debug: false name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" runs-on: ubuntu-latest @@ -710,7 +711,9 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" - name: Install Doxygen run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04 @@ -758,7 +761,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x86 @@ -812,7 +815,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x86 @@ -861,7 +864,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} diff --git a/.github/workflows/ci_sh_def.yml.patch b/.github/workflows/ci_sh_def.yml.patch index ec16c0ac2..647bd0d31 100644 --- a/.github/workflows/ci_sh_def.yml.patch +++ b/.github/workflows/ci_sh_def.yml.patch @@ -1,5 +1,5 @@ ---- ci.yml 2022-06-27 22:24:23.744079410 -0700 -+++ ci_sh_def.yml 2022-06-27 22:25:19.500125320 -0700 +--- ci.yml 2022-07-09 21:47:49.308397659 -0700 ++++ ci_sh_def.yml 2022-07-09 21:48:50.876897366 -0700 @@ -1,4 +1,16 @@ -name: CI +# PLEASE KEEP THIS GROUP OF FILES IN SYNC AT ALL TIMES: @@ -27,7 +27,7 @@ cancel-in-progress: true env: -@@ -109,6 +121,7 @@ +@@ -110,6 +122,7 @@ -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=11 @@ -35,7 +35,7 @@ ${{ matrix.args }} - name: Build C++11 -@@ -136,6 +149,7 @@ +@@ -137,6 +150,7 @@ -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=17 @@ -43,7 +43,7 @@ ${{ matrix.args }} - name: Build -@@ -157,6 +171,7 @@ +@@ -158,6 +172,7 @@ -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=17 @@ -51,7 +51,7 @@ -DPYBIND11_INTERNALS_VERSION=10000000 "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" ${{ matrix.args }} -@@ -244,6 +259,7 @@ +@@ -245,6 +260,7 @@ -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=17 @@ -59,7 +59,7 @@ - name: Build run: cmake --build build -j 2 -@@ -298,6 +314,7 @@ +@@ -299,6 +315,7 @@ -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=${{ matrix.std }} @@ -67,7 +67,7 @@ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - name: Build -@@ -327,7 +344,8 @@ +@@ -328,7 +345,8 @@ run: apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y cmake git python3-dev python3-pytest python3-numpy - name: Configure @@ -77,7 +77,7 @@ - name: Build run: cmake --build build -j2 --verbose -@@ -407,7 +425,7 @@ +@@ -408,7 +426,7 @@ cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \ -DCMAKE_CXX_STANDARD=11 \ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \ @@ -86,7 +86,7 @@ -DPYBIND11_TEST_FILTER="test_smart_ptr.cpp;test_virtual_functions.cpp" # Building before installing Pip should produce a warning but not an error -@@ -466,6 +484,7 @@ +@@ -467,6 +485,7 @@ -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=${{ matrix.std }} @@ -94,7 +94,7 @@ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - name: Build -@@ -521,6 +540,7 @@ +@@ -522,6 +541,7 @@ -DDOWNLOAD_CATCH=ON \ -DDOWNLOAD_EIGEN=OFF \ -DCMAKE_CXX_STANDARD=11 \ @@ -102,7 +102,7 @@ -DCMAKE_CXX_COMPILER=$(which icpc) \ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") -@@ -553,6 +573,7 @@ +@@ -554,6 +574,7 @@ -DDOWNLOAD_CATCH=ON \ -DDOWNLOAD_EIGEN=OFF \ -DCMAKE_CXX_STANDARD=17 \ @@ -110,7 +110,7 @@ -DCMAKE_CXX_COMPILER=$(which icpc) \ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") -@@ -620,6 +641,7 @@ +@@ -621,6 +642,7 @@ -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=11 @@ -118,7 +118,7 @@ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - name: Build -@@ -670,6 +692,7 @@ +@@ -671,6 +693,7 @@ cmake ../pybind11-tests -DDOWNLOAD_CATCH=ON -DPYBIND11_WERROR=ON @@ -126,7 +126,7 @@ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") working-directory: /build-tests -@@ -760,6 +783,7 @@ +@@ -763,6 +786,7 @@ -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON @@ -134,7 +134,7 @@ ${{ matrix.args }} - name: Build C++11 run: cmake --build build -j 2 -@@ -814,6 +838,7 @@ +@@ -817,6 +841,7 @@ -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON @@ -142,7 +142,7 @@ ${{ matrix.args }} - name: Build C++11 run: cmake --build build --config Debug -j 2 -@@ -854,6 +879,7 @@ +@@ -857,6 +882,7 @@ -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=20 @@ -150,7 +150,7 @@ - name: Build C++20 run: cmake --build build -j 2 -@@ -901,7 +927,7 @@ +@@ -904,7 +930,7 @@ - name: Configure C++11 # LTO leads to many undefined reference like # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&) @@ -159,7 +159,7 @@ - name: Build C++11 run: cmake --build build -j 2 -@@ -919,7 +945,7 @@ +@@ -922,7 +948,7 @@ run: git clean -fdx - name: Configure C++14 @@ -168,7 +168,7 @@ - name: Build C++14 run: cmake --build build2 -j 2 -@@ -937,7 +963,7 @@ +@@ -940,7 +966,7 @@ run: git clean -fdx - name: Configure C++17 diff --git a/.github/workflows/configure.yml b/.github/workflows/configure.yml index df8180cb9..e2957dfde 100644 --- a/.github/workflows/configure.yml +++ b/.github/workflows/configure.yml @@ -41,7 +41,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python 3.7 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.7 architecture: ${{ matrix.arch }} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 2bd50877c..8631086e0 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -22,7 +22,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" - name: Add matchers run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 419799340..21acc371a 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup 🐍 3.6 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.6 @@ -50,7 +50,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup 🐍 3.8 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.8 @@ -91,7 +91,9 @@ jobs: needs: [packaging] steps: - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" # Downloads all to directories matching the artifact names - uses: actions/download-artifact@v3 diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml index 174dc2496..a40a6c7d7 100644 --- a/.github/workflows/upstream.yml +++ b/.github/workflows/upstream.yml @@ -14,7 +14,7 @@ env: jobs: standard: - name: "🐍 3.11 dev • ubuntu-latest • x64" + name: "🐍 3.11 latest internals • ubuntu-latest • x64" runs-on: ubuntu-latest if: "contains(github.event.pull_request.labels.*.name, 'python dev')" @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Python 3.11 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: "3.11-dev" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 86f71def8..a2db9290b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,7 +46,7 @@ repos: # Black, the code formatter, natively supports pre-commit - repo: https://github.com/psf/black - rev: "22.3.0" # Keep in sync with blacken-docs + rev: "22.6.0" # Keep in sync with blacken-docs hooks: - id: black @@ -56,11 +56,11 @@ repos: hooks: - id: blacken-docs additional_dependencies: - - black==22.3.0 # keep in sync with black hook + - black==22.6.0 # keep in sync with black hook # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: "v1.2.0" + rev: "v1.3.0" hooks: - id: remove-tabs exclude: (^docs/.*|\.patch)?$ @@ -105,12 +105,12 @@ repos: rev: "4.0.1" hooks: - id: flake8 - exclude: ^(docs/.*|tools/.*)$ + exclude: ^(docs/.*|tools/.*|ubench/.*)$ additional_dependencies: *flake8_dependencies # PyLint has native support - not always usable, but works for us - repo: https://github.com/PyCQA/pylint - rev: "v2.14.3" + rev: "v2.14.4" hooks: - id: pylint files: ^pybind11 @@ -167,7 +167,7 @@ repos: # Clang format the codebase automatically - repo: https://github.com/pre-commit/mirrors-clang-format - rev: "v14.0.5" + rev: "v14.0.6" hooks: - id: clang-format types_or: [c++, c, cuda] diff --git a/docs/changelog.rst b/docs/changelog.rst index 67ac79cdd..9d0f217ad 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -14,44 +14,164 @@ IN DEVELOPMENT Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC 2017 is limited due to availability of CI runners; we highly recommend MSVC -2019 or 2022 be used. +2019 or 2022 be used. Initial support added for Python 3.11. + New features: +* ``py::anyset`` & ``py::frozenset`` were added, with copying (cast) to + ``std::set`` (similar to ``set``). + `#3901 `_ + +* Support bytearray casting to string. + `#3707 `_ + * ``type_caster`` was added. ``std::monostate`` is a tag type that allows ``std::variant`` to act as an optional, or allows default construction of a ``std::variant`` holding a non-default constructible type. `#3818 `_ -* Support bytearray casting to string. - `#3707 `_ +* ``pybind11::capsule::set_name`` added to mutate the name of the capsule instance. + `#3866 `_ + +* NumPy: dtype constructor from type number added, accessors corresponding to + Python API ``dtype.num``, ``dtype.byteorder``, ``dtype.flags`` and + ``dtype.alignment`` added. + `#3868 `_ + Changes: -* Python 2 support was removed completely. +* Python 3.6 is now the minimum supported version. `#3688 `_ + `#3719 `_ * The minimum version for MSVC is now 2017. `#3722 `_ +* Fix issues with CPython 3.11 betas and add to supported test matrix. + `#3923 `_ + +* ``error_already_set`` is now safer and more performant, especially for + exceptions with long tracebacks, by delaying computation. + `#1895 `_ + * Improve exception handling in python ``str`` bindings. `#3826 `_ * The bindings for capsules now have more consistent exception handling. `#3825 `_ -* Fix exception handling when ``pybind11::weakref()`` fails. - `#3739 `_ +* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can now be + used to define classes in namespaces other than pybind11. + `#3797 `_ + +* Error printing code now uses ``PYBIND11_DETAILED_ERROR_MESSAGES`` instead of + requiring ``NDEBUG``, allowing use with release builds if desired. + `#3913 `_ Bug fixes: -* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can be used - to define classes in namespaces other than pybind11. - `#3797 `_ +* Fix exception handling when ``pybind11::weakref()`` fails. + `#3739 `_ + +* ``module_::def_submodule`` was missing proper error handling. This is fixed now. + `#3973 `_ + +* The behavior or ``error_already_set`` was made safer and the highly opaque + "Unknown internal error occurred" message was replaced with a more helpful + message. + `#3982 `_ + +* ``error_already_set::what()`` now handles non-normalized exceptions correctly. + `#3971 `_ + +* Support older C++ compilers where filesystem is not yet part of the standard + library and is instead included in ``std::experimental::filesystem``. + `#3840 `_ + +* Fix ``-Wfree-nonheap-object`` warnings produced by GCC by avoiding returning + pointers to static objects with ``return_value_policy::take_ownership``. + `#3946 `_ + +* Fix cast from pytype rvalue to another pytype. + `#3949 `_ + +* ``pybind11::detail::get_internals()`` is now resilient to in-flight Python + exceptions. + `#3981 `_ + +* Arrays with a dimension of size 0 are now properly converted to dynamic Eigen + matrices (more common in NumPy 1.23). + `#4038 `_ + +* Avoid catching unrelated errors when importing NumPy. + `#3974 `_ + +Performance and style: + +* Added an accessor overload of ``(object &&key)`` to reference steal the + object when using python types as keys. This prevents unnecessary reference + count overhead for attr, dictionary, tuple, and sequence look ups. Added + additional regression tests. Fixed a performance bug the caused accessor + assignments to potentially perform unnecessary copies. + `#3970 `_ + +* Perfect forward all args of ``make_iterator``. + `#3980 `_ + +* Avoid potential bug in pycapsule destructor by adding an ``error_guard`` to + one of the dtors. + `#3958 `_ + +* Optimize dictionary access in ``strip_padding`` for numpy. + `#3994 `_ + +* ``stl_bind.h`` bindings now take slice args as a const-ref. + `#3852 `_ + +* Made slice constructor more consistent, and improve performance of some + casters by allowing reference stealing. + `#3845 `_ + +* Change numpy dtype from_args method to use const ref. + `#3878 `_ + +* Follow rule of three to ensure ``PyErr_Restore`` is called only once. + `#3872 `_ + +* Added missing perfect forwarding for ``make_iterator`` functions. + `#3860 `_ + +* Optimize c++ to python function casting by using the rvalue caster. + `#3966 `_ + +* Avoid potential implicit copy/assignment constructors causing double free in + ``strdup_gaurd``. + `#3905 `_ + +* Enable clang-tidy checks ``misc-definitions-in-headers``, + ``modernize-loop-convert``, and ``modernize-use-nullptr``. + `#3881 `_ + `#3988 `_ + Build system improvements: +* CMake: Fix file extension on Windows with cp36 and cp37 using FindPython. + `#3919 `_ + +* CMake: Support multiple Python targets (such as on vcpkg). + `#3948 `_ + +* CMake: Fix issue with NVCC on Windows. + `#3947 `_ + +* CMake: Drop the bitness check on cross compiles (like targeting WebAssembly + via Emscripten). + `#3959 `_ + * Add MSVC builds in debug mode to CI. `#3784 `_ @@ -59,15 +179,20 @@ Build system improvements: `#3732 `_, `#3741 `_ -* Avoid ``setup.py `` usage in internal tests. - `#3734 `_ - Backend and tidying up: -* Remove idioms in code comments. Use inclusive language. +* Remove idioms in code comments. Use more inclusive language. `#3809 `_ +* ``#include `` was removed from the ``pybind11/stl.h`` header. Your + project may break if it has a transitive dependency on this include. The fix + is to "Include What You Use". + `#3928 `_ + +* Avoid ``setup.py `` usage in internal tests. + `#3734 `_ + Version 2.9.2 (Mar 29, 2022) ---------------------------- diff --git a/docs/classes.rst b/docs/classes.rst index 0c687b7c5..c0c53135b 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -482,7 +482,7 @@ The binding code for this example looks as follows: .value("Cat", Pet::Kind::Cat) .export_values(); - py::class_ attributes(pet, "Attributes") + py::class_(pet, "Attributes") .def(py::init<>()) .def_readwrite("age", &Pet::Attributes::age); diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 14600e9bb..db7cd8eff 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -345,9 +345,11 @@ struct type_record { bases.append((PyObject *) base_info->type); - if (base_info->type->tp_dictoffset != 0) { - dynamic_attr = true; - } +#if PY_VERSION_HEX < 0x030B0000 + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif if (caster) { base_info->implicit_casts.emplace_back(type, caster); diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 1d38f3a47..43b22ec55 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -884,7 +884,7 @@ struct always_construct_holder { /// Create a specialization for custom holder types (silently ignores std::shared_ptr) #define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ - namespace pybind11 { \ + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \ namespace detail { \ template \ struct always_construct_holder : always_construct_holder { \ @@ -893,7 +893,7 @@ struct always_construct_holder { class type_caster::value>> \ : public type_caster_holder {}; \ } \ - } + PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) // PYBIND11_DECLARE_HOLDER_TYPE holder types: template @@ -1693,12 +1693,12 @@ handle type::handle_of() { } #define PYBIND11_MAKE_OPAQUE(...) \ - namespace pybind11 { \ + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \ namespace detail { \ template <> \ class type_caster<__VA_ARGS__> : public type_caster_for_class_<__VA_ARGS__> {}; \ } \ - } + PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) /// Lets you pass a type containing a `,` through a macro parameter without needing a separate /// typedef, e.g.: diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index f652fd104..9612d5c6c 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -533,6 +533,10 @@ extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { PyObject *&dict = *_PyObject_GetDictPtr(self); Py_VISIT(dict); +// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse +#if PY_VERSION_HEX >= 0x03090000 + Py_VISIT(Py_TYPE(self)); +#endif return 0; } @@ -547,8 +551,12 @@ extern "C" inline int pybind11_clear(PyObject *self) { inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { auto *type = &heap_type->ht_type; type->tp_flags |= Py_TPFLAGS_HAVE_GC; +#if PY_VERSION_HEX < 0x030B0000 type->tp_dictoffset = type->tp_basicsize; // place dict at the end type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it +#else + type->tp_flags |= Py_TPFLAGS_MANAGED_DICT; +#endif type->tp_traverse = pybind11_traverse; type->tp_clear = pybind11_clear; diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 7a2aaeae1..aea1987bf 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -38,6 +38,7 @@ # define PYBIND11_CPP17 # if __cplusplus >= 202002L # define PYBIND11_CPP20 +// Please update tests/pybind11_tests.cpp `cpp_std()` when adding a macro here. # endif # endif # endif diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index 18adc40b1..96db75cff 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -498,4 +498,4 @@ struct pickle_factory { PYBIND11_NAMESPACE_END(initimpl) PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index beb50266e..969b11cf3 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -111,10 +111,16 @@ struct EigenConformable { bool stride_compatible() const { // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, // matching strides, or a dimension size of 1 (in which case the stride value is - // irrelevant) - return !negativestrides - && (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() - || (EigenRowMajor ? cols : rows) == 1) + // irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant + // (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly). + if (negativestrides) { + return false; + } + if (rows == 0 || cols == 0) { + return true; + } + return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() + || (EigenRowMajor ? cols : rows) == 1) && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || (EigenRowMajor ? rows : cols) == 1); } diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 98cc3e466..d6999cd77 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -86,37 +86,6 @@ inline wchar_t *widen_chars(const char *safe_arg) { return widened_arg; } -/// Python 2.x/3.x-compatible version of `PySys_SetArgv` -inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) { - // Before it was special-cased in python 3.8, passing an empty or null argv - // caused a segfault, so we have to reimplement the special case ourselves. - bool special_case = (argv == nullptr || argc <= 0); - - const char *const empty_argv[]{"\0"}; - const char *const *safe_argv = special_case ? empty_argv : argv; - if (special_case) { - argc = 1; - } - - auto argv_size = static_cast(argc); - // SetArgv* on python 3 takes wchar_t, so we have to convert. - std::unique_ptr widened_argv(new wchar_t *[argv_size]); - std::vector> widened_argv_entries; - widened_argv_entries.reserve(argv_size); - for (size_t ii = 0; ii < argv_size; ++ii) { - widened_argv_entries.emplace_back(widen_chars(safe_argv[ii])); - if (!widened_argv_entries.back()) { - // A null here indicates a character-encoding failure or the python - // interpreter out of memory. Give up. - return; - } - widened_argv[ii] = widened_argv_entries.back().get(); - } - - auto *pysys_argv = widened_argv.get(); - PySys_SetArgvEx(argc, pysys_argv, static_cast(add_program_dir_to_path)); -} - PYBIND11_NAMESPACE_END(detail) /** \rst @@ -146,9 +115,64 @@ inline void initialize_interpreter(bool init_signal_handlers = true, pybind11_fail("The interpreter is already running"); } +#if PY_VERSION_HEX < 0x030B0000 + Py_InitializeEx(init_signal_handlers ? 1 : 0); - detail::set_interpreter_argv(argc, argv, add_program_dir_to_path); + // Before it was special-cased in python 3.8, passing an empty or null argv + // caused a segfault, so we have to reimplement the special case ourselves. + bool special_case = (argv == nullptr || argc <= 0); + + const char *const empty_argv[]{"\0"}; + const char *const *safe_argv = special_case ? empty_argv : argv; + if (special_case) { + argc = 1; + } + + auto argv_size = static_cast(argc); + // SetArgv* on python 3 takes wchar_t, so we have to convert. + std::unique_ptr widened_argv(new wchar_t *[argv_size]); + std::vector> widened_argv_entries; + widened_argv_entries.reserve(argv_size); + for (size_t ii = 0; ii < argv_size; ++ii) { + widened_argv_entries.emplace_back(detail::widen_chars(safe_argv[ii])); + if (!widened_argv_entries.back()) { + // A null here indicates a character-encoding failure or the python + // interpreter out of memory. Give up. + return; + } + widened_argv[ii] = widened_argv_entries.back().get(); + } + + auto *pysys_argv = widened_argv.get(); + + PySys_SetArgvEx(argc, pysys_argv, static_cast(add_program_dir_to_path)); +#else + PyConfig config; + PyConfig_InitIsolatedConfig(&config); + config.install_signal_handlers = init_signal_handlers ? 1 : 0; + + PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast(argv)); + if (PyStatus_Exception(status)) { + // A failure here indicates a character-encoding failure or the python + // interpreter out of memory. Give up. + PyConfig_Clear(&config); + throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg + : "Failed to prepare CPython"); + } + status = Py_InitializeFromConfig(&config); + PyConfig_Clear(&config); + if (PyStatus_Exception(status)) { + throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg + : "Failed to init CPython"); + } + if (add_program_dir_to_path) { + PyRun_SimpleString("import sys, os.path; " + "sys.path.insert(0, " + "os.path.abspath(os.path.dirname(sys.argv[0])) " + "if sys.argv and os.path.exists(sys.argv[0]) else '')"); + } +#endif } /** \rst diff --git a/setup.cfg b/setup.cfg index 4fd1e7a6f..36fbfed4f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 License :: OSI Approved :: BSD License Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: CPython diff --git a/tests/conftest.py b/tests/conftest.py index d64899734..02ce263af 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -196,15 +196,18 @@ def gc_collect(): def pytest_configure(): - print( - "C++ Info:", - pybind11_tests.compiler_info, - pybind11_tests.cpp_std, - pybind11_tests.PYBIND11_INTERNALS_ID, - flush=True, - ) + pytest.suppress = suppress + pytest.gc_collect = gc_collect + + +def pytest_report_header(config): + del config # Unused. assert ( pybind11_tests.compiler_info is not None ), "Please update pybind11_tests.cpp if this assert fails." - pytest.suppress = suppress - pytest.gc_collect = gc_collect + return ( + "C++ Info:" + f" {pybind11_tests.compiler_info}" + f" {pybind11_tests.cpp_std}" + f" {pybind11_tests.PYBIND11_INTERNALS_ID}" + ) diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp index 6933dd328..aa3095594 100644 --- a/tests/pybind11_tests.cpp +++ b/tests/pybind11_tests.cpp @@ -78,6 +78,8 @@ const char *cpp_std() { PYBIND11_MODULE(pybind11_tests, m) { m.doc() = "pybind11 test module"; + // Intentionally kept minimal to not create a maintenance chore + // ("just enough" to be conclusive). #if defined(_MSC_FULL_VER) m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER); #elif defined(__VERSION__) diff --git a/tests/requirements.txt b/tests/requirements.txt index 1159287fa..04aafa8cf 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ -build==0.7.0 +build==0.8.0 numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7" numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6" numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10" diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp index 74e76eb7e..591dacc62 100644 --- a/tests/test_eigen.cpp +++ b/tests/test_eigen.cpp @@ -345,10 +345,13 @@ TEST_SUBMODULE(eigen, m) { }, py::arg{}.noconvert()); - // test_issue738 - // Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an + // test_issue738, test_zero_length + // Issue #738: 1×N or N×1 2D matrices were neither accepted nor properly copied with an // incompatible stride value on the length-1 dimension--but that should be allowed (without // requiring a copy!) because the stride value can be safely ignored on a size-1 dimension. + // Similarly, 0×N or N×0 matrices were not accepted--again, these should be allowed since + // they contain no data. This particularly affects numpy ≥ 1.23, which sets the strides to + // 0 if any dimension size is 0. m.def("iss738_f1", &adjust_matrix &>, py::arg{}.noconvert()); diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 093773a51..a1c114aed 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -744,6 +744,13 @@ def test_issue738(): ) +@pytest.mark.parametrize("func", [m.iss738_f1, m.iss738_f2]) +@pytest.mark.parametrize("sizes", [(0, 2), (2, 0)]) +def test_zero_length(func, sizes): + """Ignore strides on a length-0 dimension (even if they would be incompatible length > 1)""" + assert np.all(func(np.zeros(sizes)) == np.zeros(sizes)) + + def test_issue1105(): """Issue 1105: 1xN or Nx1 input arrays weren't accepted for eigen compile-time row vectors or column vector""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 9eb123b1b..26313c22c 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -1,9 +1,21 @@ +import sys + import pytest import env # noqa: F401 from pybind11_tests import ConstructorStats from pybind11_tests import methods_and_attributes as m +NO_GETTER_MSG = ( + "unreadable attribute" if sys.version_info < (3, 11) else "object has no getter" +) +NO_SETTER_MSG = ( + "can't set attribute" if sys.version_info < (3, 11) else "object has no setter" +) +NO_DELETER_MSG = ( + "can't delete attribute" if sys.version_info < (3, 11) else "object has no deleter" +) + def test_methods_and_attributes(): instance1 = m.ExampleMandA() @@ -102,32 +114,32 @@ def test_properties(): with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_writeonly # unused var - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) instance.def_property_writeonly = 4 assert instance.def_property_readonly == 4 with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_impossible # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) with pytest.raises(AttributeError) as excinfo: instance.def_property_impossible = 5 - assert "can't set attribute" in str(excinfo.value) + assert NO_SETTER_MSG in str(excinfo.value) def test_static_properties(): assert m.TestProperties.def_readonly_static == 1 with pytest.raises(AttributeError) as excinfo: m.TestProperties.def_readonly_static = 2 - assert "can't set attribute" in str(excinfo.value) + assert NO_SETTER_MSG in str(excinfo.value) m.TestProperties.def_readwrite_static = 2 assert m.TestProperties.def_readwrite_static == 2 with pytest.raises(AttributeError) as excinfo: dummy = m.TestProperties.def_writeonly_static # unused var - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) m.TestProperties.def_writeonly_static = 3 assert m.TestProperties.def_readonly_static == 3 @@ -135,14 +147,14 @@ def test_static_properties(): assert m.TestProperties.def_property_readonly_static == 3 with pytest.raises(AttributeError) as excinfo: m.TestProperties.def_property_readonly_static = 99 - assert "can't set attribute" in str(excinfo.value) + assert NO_SETTER_MSG in str(excinfo.value) m.TestProperties.def_property_static = 4 assert m.TestProperties.def_property_static == 4 with pytest.raises(AttributeError) as excinfo: dummy = m.TestProperties.def_property_writeonly_static - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) m.TestProperties.def_property_writeonly_static = 5 assert m.TestProperties.def_property_static == 5 @@ -160,7 +172,7 @@ def test_static_properties(): with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_writeonly_static # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo.value) + assert NO_GETTER_MSG in str(excinfo.value) instance.def_property_writeonly_static = 4 assert instance.def_property_static == 4 @@ -180,7 +192,7 @@ def test_static_properties(): properties_override = m.TestPropertiesOverride() with pytest.raises(AttributeError) as excinfo: del properties_override.def_readonly - assert "can't delete attribute" in str(excinfo.value) + assert NO_DELETER_MSG in str(excinfo.value) def test_static_cls():