diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6843a924..3694f163 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,13 @@ on: - stable - v* +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + +env: + PIP_ONLY_BINARY: numpy + jobs: # This is the "main" test suite, which tests a large number of different # versions of default compilers and Python versions in GitHub Actions. @@ -18,13 +25,14 @@ jobs: matrix: runs-on: [ubuntu-latest, windows-latest, macos-latest] python: - - 2.7 - - 3.5 - - 3.6 - - 3.9 - - 3.10-dev - - pypy2 - - pypy3 + - '2.7' + - '3.5' + - '3.6' + - '3.9' + - '3.10' + # - '3.11-dev' + - 'pypy-3.7-v7.3.5' + # - 'pypy-3.8' # Items in here will either be added to the build matrix (if not # present), or add new keys to an existing matrix element if all the @@ -42,18 +50,8 @@ jobs: python: 3.6 args: > -DPYBIND11_FINDPYTHON=ON - - # These items will be removed from the build matrix, keys must match. - exclude: - # Currently 32bit only, and we build 64bit - - runs-on: windows-latest - python: pypy2 - - runs-on: windows-latest - python: pypy3 - - # TODO: PyPy2 7.3.3 segfaults, while 7.3.2 was fine. - - runs-on: ubuntu-latest - python: pypy2 + - runs-on: macos-latest + python: pypy-2.7 name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" runs-on: ${{ matrix.runs-on }} @@ -89,7 +87,8 @@ jobs: key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }} - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Setup annotations on Linux if: runner.os == 'Linux' @@ -113,7 +112,7 @@ jobs: - name: C++11 tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11-dev' || matrix.python == 'pypy-3.8'))" run: cmake --build . --target cpptest -j 2 - name: Interface test C++11 @@ -141,7 +140,7 @@ jobs: - name: C++ tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11-dev' || matrix.python == 'pypy-3.8'))" run: cmake --build build2 --target cpptest # Third build - C++17 mode with unstable ABI @@ -169,7 +168,7 @@ jobs: # MSVC, but for now, this action works: - name: Prepare compiler environment for Windows 🐍 2.7 if: matrix.python == 2.7 && runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1.9.0 + uses: ilammy/msvc-dev-cmd@v1.10.0 with: arch: x64 @@ -192,11 +191,12 @@ jobs: fail-fast: false matrix: include: - - python-version: 3.9 + # TODO: Fails on 3.10, investigate + - python-version: "3.9" python-debug: true valgrind: true - - python-version: 3.10-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 @@ -240,7 +240,8 @@ jobs: sudo apt-get install libc6-dbg # Needed by Valgrind - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Configure run: > @@ -517,7 +518,7 @@ jobs: - name: Install dependencies run: | set +e; source /opt/intel/oneapi/setvars.sh; set -e - python3 -m pip install -r tests/requirements.txt --prefer-binary + python3 -m pip install -r tests/requirements.txt - name: Configure C++11 run: | @@ -608,7 +609,8 @@ jobs: run: python3 -m pip install --upgrade pip - name: Install dependencies - run: python3 -m pip install cmake -r tests/requirements.txt --prefer-binary + run: | + python3 -m pip install cmake -r tests/requirements.txt - name: VAR_BUILD_TYPE 7 if: matrix.centos == 7 @@ -735,8 +737,7 @@ jobs: - 3.7 - 3.8 - 3.9 - - pypy3 - # TODO: fix hang on pypy2 + - pypy-3.6 include: - python: 3.9 @@ -760,12 +761,13 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.11 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.9.0 + uses: ilammy/msvc-dev-cmd@v1.10.0 with: arch: x86 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - name: Configure ${{ matrix.args }} @@ -806,12 +808,13 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.11 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.9.0 + uses: ilammy/msvc-dev-cmd@v1.10.0 with: toolset: 14.0 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - name: Configure @@ -847,6 +850,10 @@ jobs: std: 17 args: > -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" + - python: 3.7 + std: 17 + args: > + -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" steps: - uses: actions/checkout@v2 @@ -860,7 +867,8 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.11 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - name: Configure @@ -892,7 +900,8 @@ jobs: - { sys: mingw64, env: x86_64 } - { sys: mingw32, env: i686 } steps: - - uses: msys2/setup-msys2@v2 + # Force version because of https://github.com/msys2/setup-msys2/issues/167 + - uses: msys2/setup-msys2@v2.4.2 with: msystem: ${{matrix.sys}} install: >- diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 5547a5e2..203f350b 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -12,6 +12,9 @@ on: types: - published +env: + PIP_ONLY_BINARY: numpy + jobs: # This builds the sdists and wheels and makes sure the files are exactly as # expected. Using Windows and Python 2.7, since that is often the most @@ -29,7 +32,8 @@ jobs: python-version: 2.7 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Python Packaging tests run: pytest tests/extra_python_package/ @@ -50,7 +54,8 @@ jobs: python-version: 3.8 - name: Prepare env - run: python -m pip install -r tests/requirements.txt build twine --prefer-binary + run: | + python -m pip install -r tests/requirements.txt build twine - name: Python Packaging tests run: pytest tests/extra_python_package/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2df31460..b3e51790 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -61,20 +61,32 @@ repos: hooks: - id: remove-tabs -# Autoremoves unused imports -- repo: https://github.com/hadialqattan/pycln - rev: v1.0.3 +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 hooks: - - id: pycln + - id: python-check-blanket-noqa + - id: python-check-blanket-type-ignore + - id: python-no-log-warn + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal # Flake8 also supports pre-commit natively (same author) - repo: https://github.com/PyCQA/flake8 - rev: 3.9.2 + rev: 4.0.1 hooks: - id: flake8 - additional_dependencies: [flake8-bugbear, pep8-naming] + additional_dependencies: &flake8_dependencies + - flake8-bugbear + - pep8-naming exclude: ^(docs/.*|tools/.*)$ +- repo: https://github.com/asottile/yesqa + rev: v1.3.0 + hooks: + - id: yesqa + additional_dependencies: *flake8_dependencies + # CMake formatting - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 @@ -86,12 +98,9 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.910 + rev: v0.910-1 hooks: - id: mypy - # The default Python type ignores .pyi files, so let's rerun if detected - types: [text] - files: ^pybind11.*\.pyi?$ # Running per-file misbehaves a bit, so just run on all files, it's fast pass_filenames: false additional_dependencies: [typed_ast] diff --git a/CMakeLists.txt b/CMakeLists.txt index 25cfcec2..2e81869c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,13 +7,13 @@ cmake_minimum_required(VERSION 3.4) -# The `cmake_minimum_required(VERSION 3.4...3.21)` syntax does not work with +# The `cmake_minimum_required(VERSION 3.4...3.22)` syntax does not work with # some versions of VS that have a patched CMake 3.11. This forces us to emulate # the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.21) +if(${CMAKE_VERSION} VERSION_LESS 3.22) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) else() - cmake_policy(VERSION 3.21) + cmake_policy(VERSION 3.22) endif() # Extract project version from source diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 5f01a2f1..6330af5e 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -1266,7 +1266,7 @@ Custom type setup ================= For advanced use cases, such as enabling garbage collection support, you may -wish to directly manipulate the `PyHeapTypeObject` corresponding to a +wish to directly manipulate the ``PyHeapTypeObject`` corresponding to a ``py::class_`` definition. You can do that using ``py::custom_type_setup``: diff --git a/docs/advanced/embedding.rst b/docs/advanced/embedding.rst index a435b8a7..dd980d48 100644 --- a/docs/advanced/embedding.rst +++ b/docs/advanced/embedding.rst @@ -40,15 +40,15 @@ The essential structure of the ``main.cpp`` file looks like this: } The interpreter must be initialized before using any Python API, which includes -all the functions and classes in pybind11. The RAII guard class `scoped_interpreter` +all the functions and classes in pybind11. The RAII guard class ``scoped_interpreter`` takes care of the interpreter lifetime. After the guard is destroyed, the interpreter shuts down and clears its memory. No Python functions can be called after this. Executing Python code ===================== -There are a few different ways to run Python code. One option is to use `eval`, -`exec` or `eval_file`, as explained in :ref:`eval`. Here is a quick example in +There are a few different ways to run Python code. One option is to use ``eval``, +``exec`` or ``eval_file``, as explained in :ref:`eval`. Here is a quick example in the context of an executable with an embedded interpreter: .. code-block:: cpp @@ -108,7 +108,7 @@ The two approaches can also be combined: Importing modules ================= -Python modules can be imported using `module_::import()`: +Python modules can be imported using ``module_::import()``: .. code-block:: cpp @@ -134,7 +134,7 @@ embedding the interpreter. This makes it easy to import local Python files: int n = result.cast(); assert(n == 3); -Modules can be reloaded using `module_::reload()` if the source is modified e.g. +Modules can be reloaded using ``module_::reload()`` if the source is modified e.g. by an external process. This can be useful in scenarios where the application imports a user defined data processing script which needs to be updated after changes by the user. Note that this function does not reload modules recursively. @@ -144,7 +144,7 @@ changes by the user. Note that this function does not reload modules recursively Adding embedded modules ======================= -Embedded binary modules can be added using the `PYBIND11_EMBEDDED_MODULE` macro. +Embedded binary modules can be added using the ``PYBIND11_EMBEDDED_MODULE`` macro. Note that the definition must be placed at global scope. They can be imported like any other module. @@ -170,7 +170,7 @@ like any other module. Unlike extension modules where only a single binary module can be created, on the embedded side an unlimited number of modules can be added using multiple -`PYBIND11_EMBEDDED_MODULE` definitions (as long as they have unique names). +``PYBIND11_EMBEDDED_MODULE`` definitions (as long as they have unique names). These modules are added to Python's list of builtins, so they can also be imported in pure Python files loaded by the interpreter. Everything interacts @@ -216,9 +216,9 @@ naturally: Interpreter lifetime ==================== -The Python interpreter shuts down when `scoped_interpreter` is destroyed. After +The Python interpreter shuts down when ``scoped_interpreter`` is destroyed. After this, creating a new instance will restart the interpreter. Alternatively, the -`initialize_interpreter` / `finalize_interpreter` pair of functions can be used +``initialize_interpreter`` / ``finalize_interpreter`` pair of functions can be used to directly set the state at any time. Modules created with pybind11 can be safely re-initialized after the interpreter @@ -230,8 +230,8 @@ global data. All the details can be found in the CPython documentation. .. warning:: - Creating two concurrent `scoped_interpreter` guards is a fatal error. So is - calling `initialize_interpreter` for a second time after the interpreter + Creating two concurrent ``scoped_interpreter`` guards is a fatal error. So is + calling ``initialize_interpreter`` for a second time after the interpreter has already been initialized. Do not use the raw CPython API functions ``Py_Initialize`` and @@ -242,7 +242,7 @@ global data. All the details can be found in the CPython documentation. Sub-interpreter support ======================= -Creating multiple copies of `scoped_interpreter` is not possible because it +Creating multiple copies of ``scoped_interpreter`` is not possible because it represents the main Python interpreter. Sub-interpreters are something different and they do permit the existence of multiple interpreters. This is an advanced feature of the CPython API and should be handled with care. pybind11 does not @@ -258,5 +258,5 @@ We'll just mention a couple of caveats the sub-interpreters support in pybind11: 2. Managing multiple threads, multiple interpreters and the GIL can be challenging and there are several caveats here, even within the pure CPython API (please refer to the Python docs for details). As for - pybind11, keep in mind that `gil_scoped_release` and `gil_scoped_acquire` + pybind11, keep in mind that ``gil_scoped_release`` and ``gil_scoped_acquire`` do not take sub-interpreters into account. diff --git a/docs/advanced/exceptions.rst b/docs/advanced/exceptions.rst index 2aaa0ad3..40f67d7b 100644 --- a/docs/advanced/exceptions.rst +++ b/docs/advanced/exceptions.rst @@ -56,7 +56,9 @@ at its exception handler. +--------------------------------------+--------------------------------------+ | :class:`pybind11::buffer_error` | ``BufferError`` | +--------------------------------------+--------------------------------------+ -| :class:`pybind11::import_error` | ``import_error`` | +| :class:`pybind11::import_error` | ``ImportError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::attribute_error` | ``AttributeError`` | +--------------------------------------+--------------------------------------+ | Any other exception | ``RuntimeError`` | +--------------------------------------+--------------------------------------+ @@ -96,18 +98,18 @@ A matching function is available for registering a local exception translator: It is possible to specify base class for the exception using the third -parameter, a `handle`: +parameter, a ``handle``: .. code-block:: cpp py::register_exception(module, "PyExp", PyExc_RuntimeError); py::register_local_exception(module, "PyExp", PyExc_RuntimeError); -Then `PyExp` can be caught both as `PyExp` and `RuntimeError`. +Then ``PyExp`` can be caught both as ``PyExp`` and ``RuntimeError``. The class objects of the built-in Python exceptions are listed in the Python documentation on `Standard Exceptions `_. -The default base class is `PyExc_Exception`. +The default base class is ``PyExc_Exception``. When more advanced exception translation is needed, the functions ``py::register_exception_translator(translator)`` and diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index 1178d072..abd1084a 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -232,7 +232,7 @@ is equivalent to the following pseudocode: }); The only requirement is that ``T`` is default-constructible, but otherwise any -scope guard will work. This is very useful in combination with `gil_scoped_release`. +scope guard will work. This is very useful in combination with ``gil_scoped_release``. See :ref:`gil`. Multiple guards can also be specified as ``py::call_guard``. The diff --git a/docs/advanced/misc.rst b/docs/advanced/misc.rst index b3f3b226..edab15fc 100644 --- a/docs/advanced/misc.rst +++ b/docs/advanced/misc.rst @@ -84,7 +84,7 @@ could be realized as follows (important changes highlighted): }); } -The ``call_go`` wrapper can also be simplified using the `call_guard` policy +The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy (see :ref:`call_policies`) which yields the same result: .. code-block:: cpp diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index 8bffb83e..93e1a94d 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -41,24 +41,17 @@ A tuple of python objects can be instantiated using :func:`py::make_tuple`: Each element is converted to a supported Python type. A `simple namespace`_ can be instantiated using -:func:`py::make_simple_namespace`: .. code-block:: cpp - using namespace pybind11::literals; // to bring in the `_a` literal - py::object ns = py::make_simple_namespace("spam"_a=py::none(), "eggs"_a=42); + using namespace pybind11::literals; // to bring in the `_a` literal + py::object SimpleNamespace = py::module_::import("types").attr("SimpleNamespace"); + py::object ns = SimpleNamespace("spam"_a=py::none(), "eggs"_a=42); Attributes on a namespace can be modified with the :func:`py::delattr`, :func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can be useful as lightweight stand-ins for class instances. -.. note:: - - ``make_simple_namespace`` is not available in Python 2. - -.. versionchanged:: 2.8 - ``make_simple_namespace`` added. - .. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace .. _casting_back_and_forth: diff --git a/docs/advanced/pycpp/utilities.rst b/docs/advanced/pycpp/utilities.rst index bf90a62f..af0f9cb2 100644 --- a/docs/advanced/pycpp/utilities.rst +++ b/docs/advanced/pycpp/utilities.rst @@ -28,7 +28,7 @@ Capturing standard output from ostream Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print, but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr`` -redirection. Replacing a library's printing with `py::print ` may not +redirection. Replacing a library's printing with ``py::print `` may not be feasible. This can be fixed using a guard around the library function that redirects output to the corresponding Python streams: @@ -62,9 +62,9 @@ This method respects flushes on the output streams and will flush if needed when the scoped guard is destroyed. This allows the output to be redirected in real time, such as to a Jupyter notebook. The two arguments, the C++ stream and the Python output, are optional, and default to standard output if not given. An -extra type, `py::scoped_estream_redirect `, is identical +extra type, ``py::scoped_estream_redirect ``, is identical except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with -`py::call_guard`, which allows multiple items, but uses the default constructor: +``py::call_guard``, which allows multiple items, but uses the default constructor: .. code-block:: cpp @@ -74,7 +74,7 @@ except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful wi py::scoped_estream_redirect>()); The redirection can also be done in Python with the addition of a context -manager, using the `py::add_ostream_redirect() ` function: +manager, using the ``py::add_ostream_redirect() `` function: .. code-block:: cpp @@ -103,7 +103,7 @@ arguments to disable one of the streams if needed. Evaluating Python expressions from strings and files ==================================================== -pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate +pybind11 provides the ``eval``, ``exec`` and ``eval_file`` functions to evaluate Python expressions and statements. The following example illustrates how they can be used. diff --git a/docs/changelog.rst b/docs/changelog.rst index 4a58f2d5..bb5457ee 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,54 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. + +IN DEVELOPMENT +-------------- + +v2.8.1 (Oct 27, 2021) +--------------------- + +Changes and additions: + +* The simple namespace creation shortcut added in 2.8.0 was deprecated due to + usage of CPython internal API, and will be removed soon. Use + ``py::module_::import("types").attr("SimpleNamespace")``. + `#3374 `_ + +* Add C++ Exception type to throw and catch ``AttributeError``. Useful for + defining custom ``__setattr__`` and ``__getattr__`` methods. + `#3387 `_ + +Fixes: + +* Fixed the potential for dangling references when using properties with + ``std::optional`` types. + `#3376 `_ + +* Modernize usage of ``PyCodeObject`` on Python 3.9+ (moving toward support for + Python 3.11a1) + `#3368 `_ + +* A long-standing bug in ``eigen.h`` was fixed (originally PR #3343). The bug + was unmasked by newly added ``static_assert``'s in the Eigen 3.4.0 release. + `#3352 `_ + +* Support multiple raw inclusion of CMake helper files (Conan.io does this for + multi-config generators). + `#3420 `_ + +* Fix harmless warning on upcoming CMake 3.22. + `#3368 `_ + +* Fix 2.8.0 regression with MSVC 2017 + C++17 mode + Python 3. + `#3407 `_ + +* Fix 2.8.0 regression that caused undefined behavior (typically + segfaults) in ``make_key_iterator``/``make_value_iterator`` if dereferencing + the iterator returned a temporary value instead of a reference. + `#3348 `_ + + v2.8.0 (Oct 4, 2021) -------------------- @@ -19,10 +67,10 @@ New features: ``register_local_exception_translator(ExceptionTranslator&& translator)`` instead of ``register_exception_translator(ExceptionTranslator&& translator)`` to keep your exception remapping code local to the module. - `#2650 `_ + `#2650 `_ * Add ``make_simple_namespace`` function for instantiating Python - ``SimpleNamespace`` objects. + ``SimpleNamespace`` objects. **Deprecated in 2.8.1.** `#2840 `_ * ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new @@ -47,9 +95,10 @@ New features: `#3293 `_ * Improve the classes generated by ``bind_map``: `#3310 `_ - * Change ``.items`` from an iterator to a dictionary view. - * Add ``.keys`` and ``.values`` (both dictionary views). - * Allow ``__contains__`` to take any object. + + * Change ``.items`` from an iterator to a dictionary view. + * Add ``.keys`` and ``.values`` (both dictionary views). + * Allow ``__contains__`` to take any object. * ``pybind11::custom_type_setup`` was added, for customizing the ``PyHeapTypeObject`` corresponding to a class, which may be useful for @@ -244,8 +293,8 @@ New features: Changes: -* ``py::str`` changed to exclusively hold `PyUnicodeObject`. Previously - ``py::str`` could also hold `bytes`, which is probably surprising, was +* ``py::str`` changed to exclusively hold ``PyUnicodeObject``. Previously + ``py::str`` could also hold ``bytes``, which is probably surprising, was never documented, and can mask bugs (e.g. accidental use of ``py::str`` instead of ``py::bytes``). `#2409 `_ @@ -1398,7 +1447,7 @@ v2.2.0 (August 31, 2017) * Intel C++ compiler compatibility fixes. `#937 `_. -* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7. +* Fixed implicit conversion of ``py::enum_`` to integer types on Python 2.7. `#821 `_. * Added ``py::hash`` to fetch the hash value of Python objects, and diff --git a/docs/release.rst b/docs/release.rst index e4f3d190..b5de60f4 100644 --- a/docs/release.rst +++ b/docs/release.rst @@ -27,6 +27,7 @@ To release a new version of pybind11: ``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``. @@ -49,13 +50,15 @@ To release a new version of pybind11: - 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: click "Create 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 optionally copy-and-paste the changelog into - the description (processed as markdown by Pandoc). Check "pre-release" if - this is a beta/RC. You can get partway there with - ``cat docs/changelog.rst | pandoc -f rst -t gfm``. + 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``. @@ -64,6 +67,7 @@ To release a new version of pybind11: - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to ``0.dev1`` and increment MINOR). - Update ``_version.py`` to match + - Run ``nox -s tests_packaging`` to ensure this was done correctly. - Add a spot for in-development updates in ``docs/changelog.rst``. - ``git add``, ``git commit``, ``git push`` @@ -71,7 +75,7 @@ If a version branch is updated, remember to set PATCH to ``1.dev1``. If you'd like to bump homebrew, run: -.. code-block:: +.. code-block:: console brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz diff --git a/docs/upgrade.rst b/docs/upgrade.rst index b2b6b124..69609ca2 100644 --- a/docs/upgrade.rst +++ b/docs/upgrade.rst @@ -8,7 +8,17 @@ to a new version. But it goes into more detail. This includes things like deprecated APIs and their replacements, build system changes, general code modernization and other useful information. -.. _upgrade-guide-2.6: +.. _upgrade-guide-2.9: + +v2.9 +==== + +* Any usage of the recently added ``py::make_simple_namespace`` should be + converted to using ``py::module_::import("types").attr("SimpleNamespace")`` + instead. + + +.. _upgrade-guide-2.7: v2.7 ==== @@ -34,6 +44,7 @@ to be common: careful review and custom fixes. +.. _upgrade-guide-2.6: v2.6 ==== diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 1ec2080f..20fbb325 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1036,16 +1036,6 @@ template = 0x03030000 -template ()>> -object make_simple_namespace(Args&&... args_) { - PyObject *ns = _PyNamespace_New(dict(std::forward(args_)...).ptr()); - if (!ns) throw error_already_set(); - return reinterpret_steal(ns); -} -#endif - /// \ingroup annotations /// Annotation for arguments struct arg { diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 31716e5b..713de94b 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -11,11 +11,11 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 8 -#define PYBIND11_VERSION_PATCH 0 +#define PYBIND11_VERSION_PATCH 1 // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html // Additional convention: 0xD = dev -#define PYBIND11_VERSION_HEX 0x02080000 +#define PYBIND11_VERSION_HEX 0x02080100 #define PYBIND11_NAMESPACE_BEGIN(name) namespace name { #define PYBIND11_NAMESPACE_END(name) } @@ -831,6 +831,7 @@ PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError) +PYBIND11_RUNTIME_EXCEPTION(attribute_error, PyExc_AttributeError) PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index f804d9d1..00ce1a7a 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -468,12 +468,19 @@ PYBIND11_NOINLINE std::string error_string() { PyFrameObject *frame = trace->tb_frame; errorString += "\n\nAt:\n"; while (frame) { +#if PY_VERSION_HEX >= 0x03090000 + PyCodeObject *f_code = PyFrame_GetCode(frame); +#else + PyCodeObject *f_code = frame->f_code; + Py_INCREF(f_code); +#endif int lineno = PyFrame_GetLineNumber(frame); errorString += - " " + handle(frame->f_code->co_filename).cast() + + " " + handle(f_code->co_filename).cast() + "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; + handle(f_code->co_name).cast() + "\n"; frame = frame->f_back; + Py_DECREF(f_code); } } #endif diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index c0363827..97b1d96b 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -17,9 +17,23 @@ #include "numpy.h" +// The C4127 suppression was introduced for Eigen 3.4.0. In theory we could +// make it version specific, or even remove it later, but considering that +// 1. C4127 is generally far more distracting than useful for modern template code, and +// 2. we definitely want to ignore any MSVC warnings originating from Eigen code, +// it is probably best to keep this around indefinitely. +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // C4127: conditional expression is constant +#endif + #include #include +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + // Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit // move constructors that break things. We could detect this an explicitly copy, but an extra copy // of matrices seems highly undesirable. @@ -559,7 +573,9 @@ struct type_caster::value>> { if (!values || !innerIndices || !outerIndices) return false; - value = Eigen::MappedSparseMatrix( + value = Eigen::MappedSparseMatrix( shape[0].cast(), shape[1].cast(), nnz, outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index e0f58bcf..6cc672e2 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -1,5 +1,5 @@ /* - pybind11/exec.h: Support for evaluating Python expressions and statements + pybind11/eval.h: Support for evaluating Python expressions and statements from strings and files Copyright (c) 2016 Klemens Morgenstern and diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 24141ce3..ad5608c2 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -69,7 +69,7 @@ public: // ensure GIL is held during functor destruction struct func_handle { function f; -#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17) && PY_MAJOR_VERSION < 3) +#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17)) // This triggers a syntax error under very special conditions (very weird indeed). explicit #endif diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index b7747fae..b43a7716 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -518,7 +518,7 @@ public: } /// Single-character for dtype's type. - /// For example, ``float`` is 'f', ``double`` 'd', ``int`` 'i', and ``long`` 'd'. + /// For example, ``float`` is 'f', ``double`` 'd', ``int`` 'i', and ``long`` 'l'. char char_() const { // Note: The signature, `dtype::char_` follows the naming of NumPy's // public Python API (i.e., ``dtype.char``), rather than its internal diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 370e52cf..bfc1c368 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1124,6 +1124,15 @@ inline dict globals() { return reinterpret_borrow(p ? p : module_::import("__main__").attr("__dict__").ptr()); } +#if PY_VERSION_HEX >= 0x03030000 +template ()>> +PYBIND11_DEPRECATED("make_simple_namespace should be replaced with py::module_::import(\"types\").attr(\"SimpleNamespace\") ") +object make_simple_namespace(Args&&... args_) { + return module_::import("types").attr("SimpleNamespace")(std::forward(args_)...); +} +#endif + PYBIND11_NAMESPACE_BEGIN(detail) /// Generic support for creating new Python heap types class generic_type : public object { @@ -1967,29 +1976,54 @@ struct iterator_state { }; // Note: these helpers take the iterator by non-const reference because some -// iterators in the wild can't be dereferenced when const. C++ needs the extra parens in decltype -// to enforce an lvalue. The & after Iterator is required for MSVC < 16.9. SFINAE cannot be -// reused for result_type due to bugs in ICC, NVCC, and PGI compilers. See PR #3293. -template ()))> +// iterators in the wild can't be dereferenced when const. The & after Iterator +// is required for MSVC < 16.9. SFINAE cannot be reused for result_type due to +// bugs in ICC, NVCC, and PGI compilers. See PR #3293. +template ())> struct iterator_access { - using result_type = decltype((*std::declval())); + using result_type = decltype(*std::declval()); // NOLINTNEXTLINE(readability-const-return-type) // PR #3263 result_type operator()(Iterator &it) const { return *it; } }; -template ()).first)) > -struct iterator_key_access { - using result_type = decltype(((*std::declval()).first)); +template ()).first) > +class iterator_key_access { +private: + using pair_type = decltype(*std::declval()); + +public: + /* If either the pair itself or the element of the pair is a reference, we + * want to return a reference, otherwise a value. When the decltype + * expression is parenthesized it is based on the value category of the + * expression; otherwise it is the declared type of the pair member. + * The use of declval in the second branch rather than directly + * using *std::declval() is a workaround for nvcc + * (it's not used in the first branch because going via decltype and back + * through declval does not perfectly preserve references). + */ + using result_type = conditional_t< + std::is_reference())>::value, + decltype(((*std::declval()).first)), + decltype(std::declval().first) + >; result_type operator()(Iterator &it) const { return (*it).first; } }; -template ()).second))> -struct iterator_value_access { - using result_type = decltype(((*std::declval()).second)); +template ()).second)> +class iterator_value_access { +private: + using pair_type = decltype(*std::declval()); + +public: + using result_type = conditional_t< + std::is_reference())>::value, + decltype(((*std::declval()).second)), + decltype(std::declval().second) + >; result_type operator()(Iterator &it) const { return (*it).second; } @@ -2301,6 +2335,29 @@ inline function get_type_override(const void *this_ptr, const type_info *this_ty /* Don't call dispatch code if invoked from overridden function. Unfortunately this doesn't work on PyPy. */ #if !defined(PYPY_VERSION) + +#if PY_VERSION_HEX >= 0x03090000 + PyFrameObject *frame = PyThreadState_GetFrame(PyThreadState_Get()); + if (frame != nullptr) { + PyCodeObject *f_code = PyFrame_GetCode(frame); + // f_code is guaranteed to not be NULL + if ((std::string) str(f_code->co_name) == name && f_code->co_argcount > 0) { + PyObject* locals = PyEval_GetLocals(); + if (locals != nullptr) { + PyObject *self_caller = dict_getitem( + locals, PyTuple_GET_ITEM(f_code->co_varnames, 0) + ); + if (self_caller == self.ptr()) { + Py_DECREF(f_code); + Py_DECREF(frame); + return function(); + } + } + } + Py_DECREF(f_code); + Py_DECREF(frame); + } +#else PyFrameObject *frame = PyThreadState_Get()->frame; if (frame != nullptr && (std::string) str(frame->f_code->co_name) == name && frame->f_code->co_argcount > 0) { @@ -2310,6 +2367,8 @@ inline function get_type_override(const void *this_ptr, const type_info *this_ty if (self_caller == self.ptr()) return function(); } +#endif + #else /* PyPy currently doesn't provide a detailed cpyext emulation of frame objects, so we have to emulate this using Python. This diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 2c017b4f..3608d298 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -245,17 +245,17 @@ template , Key, Value> { }; // This type caster is intended to be used for std::optional and std::experimental::optional -template struct optional_caster { - using value_conv = make_caster; +template struct optional_caster { + using value_conv = make_caster; - template - static handle cast(T_ &&src, return_value_policy policy, handle parent) { + template + static handle cast(T &&src, return_value_policy policy, handle parent) { if (!src) return none().inc_ref(); if (!std::is_lvalue_reference::value) { - policy = return_value_policy_override::policy(policy); + policy = return_value_policy_override::policy(policy); } - return value_conv::cast(*std::forward(src), policy, parent); + return value_conv::cast(*std::forward(src), policy, parent); } bool load(handle src, bool convert) { @@ -269,11 +269,11 @@ template struct optional_caster { if (!inner_caster.load(src, convert)) return false; - value.emplace(cast_op(std::move(inner_caster))); + value.emplace(cast_op(std::move(inner_caster))); return true; } - PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); + PYBIND11_TYPE_CASTER(Type, _("Optional[") + value_conv::name + _("]")); }; #if defined(PYBIND11_HAS_OPTIONAL) diff --git a/noxfile.py b/noxfile.py index 23417982..757a5384 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,7 +2,7 @@ import nox nox.options.sessions = ["lint", "tests", "tests_packaging"] -PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"] +PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] @nox.session(reuse_venv=True) @@ -20,7 +20,8 @@ def tests(session: nox.Session) -> None: Run the tests (requires a compiler). """ tmpdir = session.create_tmp() - session.install("pytest", "cmake") + session.install("cmake") + session.install("-r", "tests/requirements.txt") session.run( "cmake", "-S", diff --git a/pybind11/_version.py b/pybind11/_version.py index 704191b9..7cc10028 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -8,5 +8,5 @@ def _to_int(s): return s -__version__ = "2.8.0" +__version__ = "2.8.1" version_info = tuple(_to_int(s) for s in __version__.split(".")) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6ebcba9b..6dc67a11 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -174,10 +174,14 @@ set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" CACHE STRING "Eigen repository to use for tests") -# This hash is for 3.3.8, using a hash for security reasons -set(PYBIND11_EIGEN_VERSION - "dc252fbf00079ccab57948a164b1421703fe4361" - CACHE STRING "Eigen version to use for tests") +# Always use a hash for reconfigure speed and security reasons +# Include the version number for pretty printing (keep in sync) +set(PYBIND11_EIGEN_VERSION_AND_HASH + "3.4.0;929bc0e191d0927b1735b9a1ddc0e8b77e3a25ec" + CACHE STRING "Eigen version to use for tests, format: VERSION;HASH") + +list(GET PYBIND11_EIGEN_VERSION_AND_HASH 0 PYBIND11_EIGEN_VERSION_STRING) +list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH) # Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but # keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" @@ -196,16 +200,22 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) FetchContent_Declare( eigen GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}" - GIT_TAG "${PYBIND11_EIGEN_VERSION}") + GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}") FetchContent_GetProperties(eigen) if(NOT eigen_POPULATED) - message(STATUS "Downloading Eigen") + message( + STATUS + "Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}" + ) FetchContent_Populate(eigen) endif() set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) set(EIGEN3_FOUND TRUE) + # When getting locally, the version is not visible from a superprojet, + # so just force it. + set(EIGEN3_VERSION "${PYBIND11_EIGEN_VERSION_STRING}") else() find_package(Eigen3 3.2.7 QUIET CONFIG) @@ -256,7 +266,9 @@ if(Boost_FOUND) endif() # Check if we need to add -lstdc++fs or -lc++fs or nothing -if(MSVC) +if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 17) + set(STD_FS_NO_LIB_NEEDED TRUE) +elseif(MSVC) set(STD_FS_NO_LIB_NEEDED TRUE) else() file( @@ -286,7 +298,7 @@ elseif(${STD_FS_NEEDS_CXXFS}) elseif(${STD_FS_NO_LIB_NEEDED}) set(STD_FS_LIB "") else() - message(WARNING "Unknown compiler - not passing -lstdc++fs") + message(WARNING "Unknown C++17 compiler - not passing -lstdc++fs") set(STD_FS_LIB "") endif() diff --git a/tests/requirements.txt b/tests/requirements.txt index 069122b8..8d2742a7 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,12 +1,12 @@ ---extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010/ -numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" -numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6" -numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version=="3.6" -numpy==1.21.2; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.7" and python_version<"3.10" -numpy==1.21.2; platform_python_implementation!="PyPy" and sys_platform=="linux" and python_version=="3.10" +numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy" +numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6" +numpy==1.20.0; 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.3; platform_python_implementation!="PyPy" and python_version>="3.7" +py @ git+https://github.com/pytest-dev/py; python_version>="3.11" pytest==4.6.9; python_version<"3.5" pytest==6.1.2; python_version=="3.5" pytest==6.2.4; python_version>="3.6" pytest-timeout -scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" -scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" +scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6" +scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10" diff --git a/tests/test_buffers.py b/tests/test_buffers.py index e3df7e04..adf7cadf 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -5,7 +5,7 @@ import struct import pytest -import env # noqa: F401 +import env from pybind11_tests import ConstructorStats from pybind11_tests import buffers as m diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 09b4b08c..2a061c19 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import env # noqa: F401 +import env from pybind11_tests import IncType, UserType from pybind11_tests import builtin_casters as m diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index edbb1890..f41ad86e 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -4,7 +4,7 @@ from threading import Thread import pytest -import env # NOQA: F401 +import env # noqa: F401 from pybind11_tests import callbacks as m diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp index ad572b2a..d22a94a1 100644 --- a/tests/test_eigen.cpp +++ b/tests/test_eigen.cpp @@ -13,6 +13,9 @@ #include #if defined(_MSC_VER) +#if _MSC_VER < 1910 // VS 2015's MSVC +# pragma warning(disable: 4127) // C4127: conditional expression is constant +#endif # pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated #endif diff --git a/tests/test_enum.py b/tests/test_enum.py index 85302b08..14c754e7 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -96,13 +96,13 @@ Members: y >= object() # noqa: B015 with pytest.raises(TypeError): - y | object() # noqa: B015 + y | object() with pytest.raises(TypeError): - y & object() # noqa: B015 + y & object() with pytest.raises(TypeError): - y ^ object() # noqa: B015 + y ^ object() assert int(m.UnscopedEnum.ETwo) == 2 assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 21909654..866b3cea 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -102,7 +102,7 @@ def test_properties(): assert instance.def_property == 3 with pytest.raises(AttributeError) as excinfo: - dummy = instance.def_property_writeonly # noqa: F841 unused var + dummy = instance.def_property_writeonly # unused var assert "unreadable attribute" in str(excinfo.value) instance.def_property_writeonly = 4 @@ -127,7 +127,7 @@ def test_static_properties(): assert m.TestProperties.def_readwrite_static == 2 with pytest.raises(AttributeError) as excinfo: - dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var + dummy = m.TestProperties.def_writeonly_static # unused var assert "unreadable attribute" in str(excinfo.value) m.TestProperties.def_writeonly_static = 3 diff --git a/tests/test_pickling.py b/tests/test_pickling.py index e39463d2..9f68f37d 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import env # noqa: F401 +import env from pybind11_tests import pickling as m try: diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 2157dc09..9a1e9188 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -84,7 +84,7 @@ TEST_SUBMODULE(pytypes, m) { #if PY_VERSION_HEX >= 0x03030000 // test_simple_namespace m.def("get_simple_namespace", []() { - auto ns = py::make_simple_namespace("attr"_a=42, "x"_a="foo", "wrong"_a=1); + auto ns = py::module_::import("types").attr("SimpleNamespace")("attr"_a=42, "x"_a="foo", "wrong"_a=1); py::delattr(ns, "wrong"); py::setattr(ns, "right", py::int_(2)); return ns; diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index cc27e60a..5215b79b 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -5,7 +5,7 @@ import sys import pytest -import env # noqa: F401 +import env from pybind11_tests import debug_enabled from pybind11_tests import pytypes as m @@ -610,7 +610,7 @@ def test_weakref(create_weakref, create_weakref_with_callback): obj = WeaklyReferenced() assert getweakrefcount(obj) == 0 - wr = create_weakref(obj) # noqa: F841 + wr = create_weakref(obj) assert getweakrefcount(obj) == 1 obj = WeaklyReferenced() diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index 9de69338..a378128a 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -38,6 +38,17 @@ bool operator==(const NonZeroIterator>& it, const NonZeroSentine return !(*it).first || !(*it).second; } +/* Iterator where dereferencing returns prvalues instead of references. */ +template +class NonRefIterator { + const T* ptr_; +public: + explicit NonRefIterator(const T *ptr) : ptr_(ptr) {} + T operator*() const { return T(*ptr_); } + NonRefIterator& operator++() { ++ptr_; return *this; } + bool operator==(const NonRefIterator &other) const { return ptr_ == other.ptr_; } +}; + class NonCopyableInt { public: explicit NonCopyableInt(int value) : value_(value) {} @@ -331,7 +342,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) { py::class_(m, "IntPairs") .def(py::init>>()) .def("nonzero", [](const IntPairs& s) { - return py::make_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); + return py::make_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); }, py::keep_alive<0, 1>()) .def("nonzero_keys", [](const IntPairs& s) { return py::make_key_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); @@ -340,6 +351,20 @@ TEST_SUBMODULE(sequences_and_iterators, m) { return py::make_value_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); }, py::keep_alive<0, 1>()) + // test iterator that returns values instead of references + .def("nonref", [](const IntPairs& s) { + return py::make_iterator(NonRefIterator>(s.begin()), + NonRefIterator>(s.end())); + }, py::keep_alive<0, 1>()) + .def("nonref_keys", [](const IntPairs& s) { + return py::make_key_iterator(NonRefIterator>(s.begin()), + NonRefIterator>(s.end())); + }, py::keep_alive<0, 1>()) + .def("nonref_values", [](const IntPairs& s) { + return py::make_value_iterator(NonRefIterator>(s.begin()), + NonRefIterator>(s.end())); + }, py::keep_alive<0, 1>()) + // test single-argument make_iterator .def("simple_iterator", [](IntPairs& self) { return py::make_iterator(self); diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index 38e2ab5b..6985918a 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -52,6 +52,13 @@ def test_generalized_iterators(): next(it) +def test_nonref_iterators(): + pairs = m.IntPairs([(1, 2), (3, 4), (0, 5)]) + assert list(pairs.nonref()) == [(1, 2), (3, 4), (0, 5)] + assert list(pairs.nonref_keys()) == [1, 3, 0] + assert list(pairs.nonref_values()) == [2, 4, 5] + + def test_generalized_iterators_simple(): assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_iterator()) == [ (1, 2), diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 7e3363c5..bc5c6553 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -19,6 +19,18 @@ #include #include +#if defined(PYBIND11_TEST_BOOST) +#include + +namespace pybind11 { namespace detail { +template +struct type_caster> : optional_caster> {}; + +template <> +struct type_caster : void_caster {}; +}} // namespace pybind11::detail +#endif + // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 #if defined(PYBIND11_HAS_VARIANT) using std::variant; @@ -59,7 +71,8 @@ namespace std { template