Merge branch 'master' into stable

This commit is contained in:
Ralf W. Grosse-Kunstleve 2021-10-27 14:59:25 -07:00
commit acae930123
46 changed files with 716 additions and 182 deletions

View File

@ -9,6 +9,13 @@ on:
- stable - stable
- v* - v*
concurrency:
group: test-${{ github.ref }}
cancel-in-progress: true
env:
PIP_ONLY_BINARY: numpy
jobs: jobs:
# This is the "main" test suite, which tests a large number of different # This is the "main" test suite, which tests a large number of different
# versions of default compilers and Python versions in GitHub Actions. # versions of default compilers and Python versions in GitHub Actions.
@ -18,13 +25,14 @@ jobs:
matrix: matrix:
runs-on: [ubuntu-latest, windows-latest, macos-latest] runs-on: [ubuntu-latest, windows-latest, macos-latest]
python: python:
- 2.7 - '2.7'
- 3.5 - '3.5'
- 3.6 - '3.6'
- 3.9 - '3.9'
- 3.10-dev - '3.10'
- pypy2 # - '3.11-dev'
- pypy3 - 'pypy-3.7-v7.3.5'
# - 'pypy-3.8'
# Items in here will either be added to the build matrix (if not # 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 # present), or add new keys to an existing matrix element if all the
@ -42,18 +50,8 @@ jobs:
python: 3.6 python: 3.6
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
- runs-on: macos-latest
# These items will be removed from the build matrix, keys must match. python: pypy-2.7
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
name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}"
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
@ -89,7 +87,8 @@ jobs:
key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }} key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }}
- name: Prepare env - 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 - name: Setup annotations on Linux
if: runner.os == 'Linux' if: runner.os == 'Linux'
@ -113,7 +112,7 @@ jobs:
- name: C++11 tests - name: C++11 tests
# TODO: Figure out how to load the DLL on Python 3.8+ # 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 run: cmake --build . --target cpptest -j 2
- name: Interface test C++11 - name: Interface test C++11
@ -141,7 +140,7 @@ jobs:
- name: C++ tests - name: C++ tests
# TODO: Figure out how to load the DLL on Python 3.8+ # 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 run: cmake --build build2 --target cpptest
# Third build - C++17 mode with unstable ABI # Third build - C++17 mode with unstable ABI
@ -169,7 +168,7 @@ jobs:
# MSVC, but for now, this action works: # MSVC, but for now, this action works:
- name: Prepare compiler environment for Windows 🐍 2.7 - name: Prepare compiler environment for Windows 🐍 2.7
if: matrix.python == 2.7 && runner.os == 'Windows' 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: with:
arch: x64 arch: x64
@ -192,11 +191,12 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- python-version: 3.9 # TODO: Fails on 3.10, investigate
- python-version: "3.9"
python-debug: true python-debug: true
valgrind: true valgrind: true
- python-version: 3.10-dev # - python-version: "3.11-dev"
python-debug: false # python-debug: false
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -240,7 +240,8 @@ jobs:
sudo apt-get install libc6-dbg # Needed by Valgrind sudo apt-get install libc6-dbg # Needed by Valgrind
- name: Prepare env - 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 - name: Configure
run: > run: >
@ -517,7 +518,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e 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 - name: Configure C++11
run: | run: |
@ -608,7 +609,8 @@ jobs:
run: python3 -m pip install --upgrade pip run: python3 -m pip install --upgrade pip
- name: Install dependencies - 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 - name: VAR_BUILD_TYPE 7
if: matrix.centos == 7 if: matrix.centos == 7
@ -735,8 +737,7 @@ jobs:
- 3.7 - 3.7
- 3.8 - 3.8
- 3.9 - 3.9
- pypy3 - pypy-3.6
# TODO: fix hang on pypy2
include: include:
- python: 3.9 - python: 3.9
@ -760,12 +761,13 @@ jobs:
uses: jwlawson/actions-setup-cmake@v1.11 uses: jwlawson/actions-setup-cmake@v1.11
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.9.0 uses: ilammy/msvc-dev-cmd@v1.10.0
with: with:
arch: x86 arch: x86
- name: Prepare env - 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 # First build - C++11 mode and inplace
- name: Configure ${{ matrix.args }} - name: Configure ${{ matrix.args }}
@ -806,12 +808,13 @@ jobs:
uses: jwlawson/actions-setup-cmake@v1.11 uses: jwlawson/actions-setup-cmake@v1.11
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.9.0 uses: ilammy/msvc-dev-cmd@v1.10.0
with: with:
toolset: 14.0 toolset: 14.0
- name: Prepare env - 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 # First build - C++11 mode and inplace
- name: Configure - name: Configure
@ -847,6 +850,10 @@ jobs:
std: 17 std: 17
args: > args: >
-DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR"
- python: 3.7
std: 17
args: >
-DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -860,7 +867,8 @@ jobs:
uses: jwlawson/actions-setup-cmake@v1.11 uses: jwlawson/actions-setup-cmake@v1.11
- name: Prepare env - 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 # First build - C++11 mode and inplace
- name: Configure - name: Configure
@ -892,7 +900,8 @@ jobs:
- { sys: mingw64, env: x86_64 } - { sys: mingw64, env: x86_64 }
- { sys: mingw32, env: i686 } - { sys: mingw32, env: i686 }
steps: 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: with:
msystem: ${{matrix.sys}} msystem: ${{matrix.sys}}
install: >- install: >-

View File

@ -12,6 +12,9 @@ on:
types: types:
- published - published
env:
PIP_ONLY_BINARY: numpy
jobs: jobs:
# This builds the sdists and wheels and makes sure the files are exactly as # 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 # expected. Using Windows and Python 2.7, since that is often the most
@ -29,7 +32,8 @@ jobs:
python-version: 2.7 python-version: 2.7
- name: Prepare env - 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 - name: Python Packaging tests
run: pytest tests/extra_python_package/ run: pytest tests/extra_python_package/
@ -50,7 +54,8 @@ jobs:
python-version: 3.8 python-version: 3.8
- name: Prepare env - 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 - name: Python Packaging tests
run: pytest tests/extra_python_package/ run: pytest tests/extra_python_package/

View File

@ -61,20 +61,32 @@ repos:
hooks: hooks:
- id: remove-tabs - id: remove-tabs
# Autoremoves unused imports - repo: https://github.com/pre-commit/pygrep-hooks
- repo: https://github.com/hadialqattan/pycln rev: v1.9.0
rev: v1.0.3
hooks: 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) # Flake8 also supports pre-commit natively (same author)
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 3.9.2 rev: 4.0.1
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: [flake8-bugbear, pep8-naming] additional_dependencies: &flake8_dependencies
- flake8-bugbear
- pep8-naming
exclude: ^(docs/.*|tools/.*)$ exclude: ^(docs/.*|tools/.*)$
- repo: https://github.com/asottile/yesqa
rev: v1.3.0
hooks:
- id: yesqa
additional_dependencies: *flake8_dependencies
# CMake formatting # CMake formatting
- repo: https://github.com/cheshirekow/cmake-format-precommit - repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13 rev: v0.6.13
@ -86,12 +98,9 @@ repos:
# Check static types with mypy # Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.910 rev: v0.910-1
hooks: hooks:
- id: mypy - 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 # Running per-file misbehaves a bit, so just run on all files, it's fast
pass_filenames: false pass_filenames: false
additional_dependencies: [typed_ast] additional_dependencies: [typed_ast]

View File

@ -7,13 +7,13 @@
cmake_minimum_required(VERSION 3.4) 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 # some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround: # 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}) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else() else()
cmake_policy(VERSION 3.21) cmake_policy(VERSION 3.22)
endif() endif()
# Extract project version from source # Extract project version from source

View File

@ -1266,7 +1266,7 @@ Custom type setup
================= =================
For advanced use cases, such as enabling garbage collection support, you may 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. ``py::class_`` definition.
You can do that using ``py::custom_type_setup``: You can do that using ``py::custom_type_setup``:

View File

@ -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 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 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. shuts down and clears its memory. No Python functions can be called after this.
Executing Python code Executing Python code
===================== =====================
There are a few different ways to run Python code. One option is to use `eval`, 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 ``exec`` or ``eval_file``, as explained in :ref:`eval`. Here is a quick example in
the context of an executable with an embedded interpreter: the context of an executable with an embedded interpreter:
.. code-block:: cpp .. code-block:: cpp
@ -108,7 +108,7 @@ The two approaches can also be combined:
Importing modules Importing modules
================= =================
Python modules can be imported using `module_::import()`: Python modules can be imported using ``module_::import()``:
.. code-block:: cpp .. code-block:: cpp
@ -134,7 +134,7 @@ embedding the interpreter. This makes it easy to import local Python files:
int n = result.cast<int>(); int n = result.cast<int>();
assert(n == 3); 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 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 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. 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 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 Note that the definition must be placed at global scope. They can be imported
like any other module. 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 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 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 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 imported in pure Python files loaded by the interpreter. Everything interacts
@ -216,9 +216,9 @@ naturally:
Interpreter lifetime 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 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. to directly set the state at any time.
Modules created with pybind11 can be safely re-initialized after the interpreter 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:: .. warning::
Creating two concurrent `scoped_interpreter` guards is a fatal error. So is Creating two concurrent ``scoped_interpreter`` guards is a fatal error. So is
calling `initialize_interpreter` for a second time after the interpreter calling ``initialize_interpreter`` for a second time after the interpreter
has already been initialized. has already been initialized.
Do not use the raw CPython API functions ``Py_Initialize`` and 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 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 represents the main Python interpreter. Sub-interpreters are something different
and they do permit the existence of multiple interpreters. This is an advanced 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 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 2. Managing multiple threads, multiple interpreters and the GIL can be
challenging and there are several caveats here, even within the pure challenging and there are several caveats here, even within the pure
CPython API (please refer to the Python docs for details). As for 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. do not take sub-interpreters into account.

View File

@ -56,7 +56,9 @@ at its exception handler.
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
| :class:`pybind11::buffer_error` | ``BufferError`` | | :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`` | | 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 It is possible to specify base class for the exception using the third
parameter, a `handle`: parameter, a ``handle``:
.. code-block:: cpp .. code-block:: cpp
py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError); py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
py::register_local_exception<CppExp>(module, "PyExp", PyExc_RuntimeError); py::register_local_exception<CppExp>(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 The class objects of the built-in Python exceptions are listed in the Python
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_. documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#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 When more advanced exception translation is needed, the functions
``py::register_exception_translator(translator)`` and ``py::register_exception_translator(translator)`` and

View File

@ -232,7 +232,7 @@ is equivalent to the following pseudocode:
}); });
The only requirement is that ``T`` is default-constructible, but otherwise any 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`. See :ref:`gil`.
Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The

View File

@ -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: (see :ref:`call_policies`) which yields the same result:
.. code-block:: cpp .. code-block:: cpp

View File

@ -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. Each element is converted to a supported Python type.
A `simple namespace`_ can be instantiated using A `simple namespace`_ can be instantiated using
:func:`py::make_simple_namespace`:
.. code-block:: cpp .. code-block:: cpp
using namespace pybind11::literals; // to bring in the `_a` literal using namespace pybind11::literals; // to bring in the `_a` literal
py::object ns = py::make_simple_namespace("spam"_a=py::none(), "eggs"_a=42); 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`, Attributes on a namespace can be modified with the :func:`py::delattr`,
:func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can :func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can
be useful as lightweight stand-ins for class instances. 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 .. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
.. _casting_back_and_forth: .. _casting_back_and_forth:

View File

@ -28,7 +28,7 @@ Capturing standard output from ostream
Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print, 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`` but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr``
redirection. Replacing a library's printing with `py::print <print>` may not redirection. Replacing a library's printing with ``py::print <print>`` may not
be feasible. This can be fixed using a guard around the library function that be feasible. This can be fixed using a guard around the library function that
redirects output to the corresponding Python streams: 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 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 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 the Python output, are optional, and default to standard output if not given. An
extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identical extra type, ``py::scoped_estream_redirect <scoped_estream_redirect>``, is identical
except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with 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 .. 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>()); py::scoped_estream_redirect>());
The redirection can also be done in Python with the addition of a context The redirection can also be done in Python with the addition of a context
manager, using the `py::add_ostream_redirect() <add_ostream_redirect>` function: manager, using the ``py::add_ostream_redirect() <add_ostream_redirect>`` function:
.. code-block:: cpp .. code-block:: cpp
@ -103,7 +103,7 @@ arguments to disable one of the streams if needed.
Evaluating Python expressions from strings and files 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 Python expressions and statements. The following example illustrates how they
can be used. can be used.

View File

@ -6,6 +6,54 @@ Changelog
Starting with version 1.8.0, pybind11 releases use a `semantic versioning Starting with version 1.8.0, pybind11 releases use a `semantic versioning
<http://semver.org>`_ policy. <http://semver.org>`_ 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 <https://github.com/pybinyyd/pybind11/pull/3374>`_
* Add C++ Exception type to throw and catch ``AttributeError``. Useful for
defining custom ``__setattr__`` and ``__getattr__`` methods.
`#3387 <https://github.com/pybind/pybind11/pull/3387>`_
Fixes:
* Fixed the potential for dangling references when using properties with
``std::optional`` types.
`#3376 <https://github.com/pybind/pybind11/pull/3376>`_
* Modernize usage of ``PyCodeObject`` on Python 3.9+ (moving toward support for
Python 3.11a1)
`#3368 <https://github.com/pybind/pybind11/pull/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 <https://github.com/pybind/pybind11/pull/3352>`_
* Support multiple raw inclusion of CMake helper files (Conan.io does this for
multi-config generators).
`#3420 <https://github.com/pybind/pybind11/pull/3420>`_
* Fix harmless warning on upcoming CMake 3.22.
`#3368 <https://github.com/pybind/pybind11/pull/3368>`_
* Fix 2.8.0 regression with MSVC 2017 + C++17 mode + Python 3.
`#3407 <https://github.com/pybind/pybind11/pull/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 <https://github.com/pybind/pybind11/pull/3348>`_
v2.8.0 (Oct 4, 2021) v2.8.0 (Oct 4, 2021)
-------------------- --------------------
@ -19,10 +67,10 @@ New features:
``register_local_exception_translator(ExceptionTranslator&& translator)`` ``register_local_exception_translator(ExceptionTranslator&& translator)``
instead of ``register_exception_translator(ExceptionTranslator&& instead of ``register_exception_translator(ExceptionTranslator&&
translator)`` to keep your exception remapping code local to the module. translator)`` to keep your exception remapping code local to the module.
`#2650 <https://github.com/pybind/pybind11/pull/2650>`_ `#2650 <https://github.com/pybinyyd/pybind11/pull/2650>`_
* Add ``make_simple_namespace`` function for instantiating Python * Add ``make_simple_namespace`` function for instantiating Python
``SimpleNamespace`` objects. ``SimpleNamespace`` objects. **Deprecated in 2.8.1.**
`#2840 <https://github.com/pybind/pybind11/pull/2840>`_ `#2840 <https://github.com/pybind/pybind11/pull/2840>`_
* ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new * ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new
@ -47,9 +95,10 @@ New features:
`#3293 <https://github.com/pybind/pybind11/pull/3293>`_ `#3293 <https://github.com/pybind/pybind11/pull/3293>`_
* Improve the classes generated by ``bind_map``: `#3310 <https://github.com/pybind/pybind11/pull/3310>`_ * Improve the classes generated by ``bind_map``: `#3310 <https://github.com/pybind/pybind11/pull/3310>`_
* Change ``.items`` from an iterator to a dictionary view.
* Add ``.keys`` and ``.values`` (both dictionary views). * Change ``.items`` from an iterator to a dictionary view.
* Allow ``__contains__`` to take any object. * Add ``.keys`` and ``.values`` (both dictionary views).
* Allow ``__contains__`` to take any object.
* ``pybind11::custom_type_setup`` was added, for customizing the * ``pybind11::custom_type_setup`` was added, for customizing the
``PyHeapTypeObject`` corresponding to a class, which may be useful for ``PyHeapTypeObject`` corresponding to a class, which may be useful for
@ -244,8 +293,8 @@ New features:
Changes: Changes:
* ``py::str`` changed to exclusively hold `PyUnicodeObject`. Previously * ``py::str`` changed to exclusively hold ``PyUnicodeObject``. Previously
``py::str`` could also hold `bytes`, which is probably surprising, was ``py::str`` could also hold ``bytes``, which is probably surprising, was
never documented, and can mask bugs (e.g. accidental use of ``py::str`` never documented, and can mask bugs (e.g. accidental use of ``py::str``
instead of ``py::bytes``). instead of ``py::bytes``).
`#2409 <https://github.com/pybind/pybind11/pull/2409>`_ `#2409 <https://github.com/pybind/pybind11/pull/2409>`_
@ -1398,7 +1447,7 @@ v2.2.0 (August 31, 2017)
* Intel C++ compiler compatibility fixes. * Intel C++ compiler compatibility fixes.
`#937 <https://github.com/pybind/pybind11/pull/937>`_. `#937 <https://github.com/pybind/pybind11/pull/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 <https://github.com/pybind/pybind11/pull/821>`_. `#821 <https://github.com/pybind/pybind11/pull/821>`_.
* Added ``py::hash`` to fetch the hash value of Python objects, and * Added ``py::hash`` to fetch the hash value of Python objects, and

View File

@ -27,6 +27,7 @@ To release a new version of pybind11:
``include/pybind11/detail/common.h``. PATCH should be a simple integer. ``include/pybind11/detail/common.h``. PATCH should be a simple integer.
- Update the version HEX just below, as well. - Update the version HEX just below, as well.
- Update ``pybind11/_version.py`` (match above) - 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 - Ensure that all the information in ``setup.cfg`` is up-to-date, like
supported Python versions. supported Python versions.
- Add release date in ``docs/changelog.rst``. - 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 - Make a GitHub release (this shows up in the UI, sends new release
notifications to users watching releases, and also uploads PyPI packages). 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 (Note: if you do not use an existing tag, this creates a new lightweight tag
for you, so you could skip the above step). 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 - GUI method: Under `releases <https://github.com/pybind/pybind11/releases>`_
name like "Version X.Y.Z", and optionally copy-and-paste the changelog into click "Draft a new release" on the far right, fill in the tag name
the description (processed as markdown by Pandoc). Check "pre-release" if (if you didn't tag above, it will be made here), fill in a release name
this is a beta/RC. You can get partway there with like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog
``cat docs/changelog.rst | pandoc -f rst -t gfm``. 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"`` - 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``. 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 - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to
``0.dev1`` and increment MINOR). ``0.dev1`` and increment MINOR).
- Update ``_version.py`` to match - 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``. - Add a spot for in-development updates in ``docs/changelog.rst``.
- ``git add``, ``git commit``, ``git push`` - ``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: 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 brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz

View File

@ -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 deprecated APIs and their replacements, build system changes, general code
modernization and other useful information. 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 v2.7
==== ====
@ -34,6 +44,7 @@ to be common:
careful review and custom fixes. careful review and custom fixes.
.. _upgrade-guide-2.6:
v2.6 v2.6
==== ====

View File

@ -1036,16 +1036,6 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
return result; return result;
} }
#if PY_VERSION_HEX >= 0x03030000
template <typename... Args,
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
object make_simple_namespace(Args&&... args_) {
PyObject *ns = _PyNamespace_New(dict(std::forward<Args>(args_)...).ptr());
if (!ns) throw error_already_set();
return reinterpret_steal<object>(ns);
}
#endif
/// \ingroup annotations /// \ingroup annotations
/// Annotation for arguments /// Annotation for arguments
struct arg { struct arg {

View File

@ -11,11 +11,11 @@
#define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 8 #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 // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
// Additional convention: 0xD = dev // Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x02080000 #define PYBIND11_VERSION_HEX 0x02080100
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name { #define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
#define PYBIND11_NAMESPACE_END(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(type_error, PyExc_TypeError)
PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError) 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(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 PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally

View File

@ -468,12 +468,19 @@ PYBIND11_NOINLINE std::string error_string() {
PyFrameObject *frame = trace->tb_frame; PyFrameObject *frame = trace->tb_frame;
errorString += "\n\nAt:\n"; errorString += "\n\nAt:\n";
while (frame) { 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); int lineno = PyFrame_GetLineNumber(frame);
errorString += errorString +=
" " + handle(frame->f_code->co_filename).cast<std::string>() + " " + handle(f_code->co_filename).cast<std::string>() +
"(" + std::to_string(lineno) + "): " + "(" + std::to_string(lineno) + "): " +
handle(frame->f_code->co_name).cast<std::string>() + "\n"; handle(f_code->co_name).cast<std::string>() + "\n";
frame = frame->f_back; frame = frame->f_back;
Py_DECREF(f_code);
} }
} }
#endif #endif

View File

@ -17,9 +17,23 @@
#include "numpy.h" #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 <Eigen/Core> #include <Eigen/Core>
#include <Eigen/SparseCore> #include <Eigen/SparseCore>
#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 // 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 // move constructors that break things. We could detect this an explicitly copy, but an extra copy
// of matrices seems highly undesirable. // of matrices seems highly undesirable.
@ -559,7 +573,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
if (!values || !innerIndices || !outerIndices) if (!values || !innerIndices || !outerIndices)
return false; return false;
value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>( value = Eigen::MappedSparseMatrix<Scalar,
Type::Flags & (Eigen::RowMajor | Eigen::ColMajor),
StorageIndex>(
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz, shape[0].cast<Index>(), shape[1].cast<Index>(), nnz,
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data());

View File

@ -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 from strings and files
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and

View File

@ -69,7 +69,7 @@ public:
// ensure GIL is held during functor destruction // ensure GIL is held during functor destruction
struct func_handle { struct func_handle {
function f; 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). // This triggers a syntax error under very special conditions (very weird indeed).
explicit explicit
#endif #endif

View File

@ -518,7 +518,7 @@ public:
} }
/// Single-character for dtype's type. /// 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 { char char_() const {
// Note: The signature, `dtype::char_` follows the naming of NumPy's // Note: The signature, `dtype::char_` follows the naming of NumPy's
// public Python API (i.e., ``dtype.char``), rather than its internal // public Python API (i.e., ``dtype.char``), rather than its internal

View File

@ -1124,6 +1124,15 @@ inline dict globals() {
return reinterpret_borrow<dict>(p ? p : module_::import("__main__").attr("__dict__").ptr()); return reinterpret_borrow<dict>(p ? p : module_::import("__main__").attr("__dict__").ptr());
} }
#if PY_VERSION_HEX >= 0x03030000
template <typename... Args,
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
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>(args_)...);
}
#endif
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/// Generic support for creating new Python heap types /// Generic support for creating new Python heap types
class generic_type : public object { class generic_type : public object {
@ -1967,29 +1976,54 @@ struct iterator_state {
}; };
// Note: these helpers take the iterator by non-const reference because some // 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 // iterators in the wild can't be dereferenced when const. The & after Iterator
// to enforce an lvalue. The & after Iterator is required for MSVC < 16.9. SFINAE cannot be // is required for MSVC < 16.9. SFINAE cannot be reused for result_type due to
// reused for result_type due to bugs in ICC, NVCC, and PGI compilers. See PR #3293. // bugs in ICC, NVCC, and PGI compilers. See PR #3293.
template <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()))> template <typename Iterator, typename SFINAE = decltype(*std::declval<Iterator &>())>
struct iterator_access { struct iterator_access {
using result_type = decltype((*std::declval<Iterator &>())); using result_type = decltype(*std::declval<Iterator &>());
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263 // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
result_type operator()(Iterator &it) const { result_type operator()(Iterator &it) const {
return *it; return *it;
} }
}; };
template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).first)) > template <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()).first) >
struct iterator_key_access { class iterator_key_access {
using result_type = decltype(((*std::declval<Iterator &>()).first)); private:
using pair_type = decltype(*std::declval<Iterator &>());
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<pair_type> in the second branch rather than directly
* using *std::declval<Iterator &>() 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<decltype(*std::declval<Iterator &>())>::value,
decltype(((*std::declval<Iterator &>()).first)),
decltype(std::declval<pair_type>().first)
>;
result_type operator()(Iterator &it) const { result_type operator()(Iterator &it) const {
return (*it).first; return (*it).first;
} }
}; };
template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).second))> template <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()).second)>
struct iterator_value_access { class iterator_value_access {
using result_type = decltype(((*std::declval<Iterator &>()).second)); private:
using pair_type = decltype(*std::declval<Iterator &>());
public:
using result_type = conditional_t<
std::is_reference<decltype(*std::declval<Iterator &>())>::value,
decltype(((*std::declval<Iterator &>()).second)),
decltype(std::declval<pair_type>().second)
>;
result_type operator()(Iterator &it) const { result_type operator()(Iterator &it) const {
return (*it).second; 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. /* Don't call dispatch code if invoked from overridden function.
Unfortunately this doesn't work on PyPy. */ Unfortunately this doesn't work on PyPy. */
#if !defined(PYPY_VERSION) #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; PyFrameObject *frame = PyThreadState_Get()->frame;
if (frame != nullptr && (std::string) str(frame->f_code->co_name) == name if (frame != nullptr && (std::string) str(frame->f_code->co_name) == name
&& frame->f_code->co_argcount > 0) { && 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()) if (self_caller == self.ptr())
return function(); return function();
} }
#endif
#else #else
/* PyPy currently doesn't provide a detailed cpyext emulation of /* PyPy currently doesn't provide a detailed cpyext emulation of
frame objects, so we have to emulate this using Python. This frame objects, so we have to emulate this using Python. This

View File

@ -245,17 +245,17 @@ template <typename Key, typename Value, typename Hash, typename Equal, typename
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { }; : map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { };
// This type caster is intended to be used for std::optional and std::experimental::optional // This type caster is intended to be used for std::optional and std::experimental::optional
template<typename T> struct optional_caster { template<typename Type, typename Value = typename Type::value_type> struct optional_caster {
using value_conv = make_caster<typename T::value_type>; using value_conv = make_caster<Value>;
template <typename T_> template <typename T>
static handle cast(T_ &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
if (!src) if (!src)
return none().inc_ref(); return none().inc_ref();
if (!std::is_lvalue_reference<T>::value) { if (!std::is_lvalue_reference<T>::value) {
policy = return_value_policy_override<T>::policy(policy); policy = return_value_policy_override<Value>::policy(policy);
} }
return value_conv::cast(*std::forward<T_>(src), policy, parent); return value_conv::cast(*std::forward<T>(src), policy, parent);
} }
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
@ -269,11 +269,11 @@ template<typename T> struct optional_caster {
if (!inner_caster.load(src, convert)) if (!inner_caster.load(src, convert))
return false; return false;
value.emplace(cast_op<typename T::value_type &&>(std::move(inner_caster))); value.emplace(cast_op<Value &&>(std::move(inner_caster)));
return true; return true;
} }
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); PYBIND11_TYPE_CASTER(Type, _("Optional[") + value_conv::name + _("]"));
}; };
#if defined(PYBIND11_HAS_OPTIONAL) #if defined(PYBIND11_HAS_OPTIONAL)

View File

@ -2,7 +2,7 @@ import nox
nox.options.sessions = ["lint", "tests", "tests_packaging"] 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) @nox.session(reuse_venv=True)
@ -20,7 +20,8 @@ def tests(session: nox.Session) -> None:
Run the tests (requires a compiler). Run the tests (requires a compiler).
""" """
tmpdir = session.create_tmp() tmpdir = session.create_tmp()
session.install("pytest", "cmake") session.install("cmake")
session.install("-r", "tests/requirements.txt")
session.run( session.run(
"cmake", "cmake",
"-S", "-S",

View File

@ -8,5 +8,5 @@ def _to_int(s):
return s return s
__version__ = "2.8.0" __version__ = "2.8.1"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -174,10 +174,14 @@ set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py)
set(PYBIND11_EIGEN_REPO set(PYBIND11_EIGEN_REPO
"https://gitlab.com/libeigen/eigen.git" "https://gitlab.com/libeigen/eigen.git"
CACHE STRING "Eigen repository to use for tests") CACHE STRING "Eigen repository to use for tests")
# This hash is for 3.3.8, using a hash for security reasons # Always use a hash for reconfigure speed and security reasons
set(PYBIND11_EIGEN_VERSION # Include the version number for pretty printing (keep in sync)
"dc252fbf00079ccab57948a164b1421703fe4361" set(PYBIND11_EIGEN_VERSION_AND_HASH
CACHE STRING "Eigen version to use for tests") "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 # 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" # 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( FetchContent_Declare(
eigen eigen
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}" GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
GIT_TAG "${PYBIND11_EIGEN_VERSION}") GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
FetchContent_GetProperties(eigen) FetchContent_GetProperties(eigen)
if(NOT eigen_POPULATED) 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) FetchContent_Populate(eigen)
endif() endif()
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
set(EIGEN3_FOUND TRUE) 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() else()
find_package(Eigen3 3.2.7 QUIET CONFIG) find_package(Eigen3 3.2.7 QUIET CONFIG)
@ -256,7 +266,9 @@ if(Boost_FOUND)
endif() endif()
# Check if we need to add -lstdc++fs or -lc++fs or nothing # 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) set(STD_FS_NO_LIB_NEEDED TRUE)
else() else()
file( file(
@ -286,7 +298,7 @@ elseif(${STD_FS_NEEDS_CXXFS})
elseif(${STD_FS_NO_LIB_NEEDED}) elseif(${STD_FS_NO_LIB_NEEDED})
set(STD_FS_LIB "") set(STD_FS_LIB "")
else() else()
message(WARNING "Unknown compiler - not passing -lstdc++fs") message(WARNING "Unknown C++17 compiler - not passing -lstdc++fs")
set(STD_FS_LIB "") set(STD_FS_LIB "")
endif() endif()

View File

@ -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" and platform_python_implementation!="PyPy"
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" 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" or sys_platform=="linux") and python_version=="3.6" numpy==1.19.3; platform_python_implementation!="PyPy" 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.3; platform_python_implementation!="PyPy" and python_version>="3.7"
numpy==1.21.2; platform_python_implementation!="PyPy" and sys_platform=="linux" and python_version=="3.10" py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
pytest==4.6.9; python_version<"3.5" pytest==4.6.9; python_version<"3.5"
pytest==6.1.2; python_version=="3.5" pytest==6.1.2; python_version=="3.5"
pytest==6.2.4; python_version>="3.6" pytest==6.2.4; python_version>="3.6"
pytest-timeout pytest-timeout
scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" scipy==1.2.3; platform_python_implementation!="PyPy" 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.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"

View File

@ -5,7 +5,7 @@ import struct
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import buffers as m from pybind11_tests import buffers as m

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import IncType, UserType from pybind11_tests import IncType, UserType
from pybind11_tests import builtin_casters as m from pybind11_tests import builtin_casters as m

View File

@ -4,7 +4,7 @@ from threading import Thread
import pytest import pytest
import env # NOQA: F401 import env # noqa: F401
from pybind11_tests import callbacks as m from pybind11_tests import callbacks as m

View File

@ -13,6 +13,9 @@
#include <pybind11/stl.h> #include <pybind11/stl.h>
#if defined(_MSC_VER) #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 # pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated
#endif #endif

View File

@ -96,13 +96,13 @@ Members:
y >= object() # noqa: B015 y >= object() # noqa: B015
with pytest.raises(TypeError): with pytest.raises(TypeError):
y | object() # noqa: B015 y | object()
with pytest.raises(TypeError): with pytest.raises(TypeError):
y & object() # noqa: B015 y & object()
with pytest.raises(TypeError): with pytest.raises(TypeError):
y ^ object() # noqa: B015 y ^ object()
assert int(m.UnscopedEnum.ETwo) == 2 assert int(m.UnscopedEnum.ETwo) == 2
assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo" assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo"

View File

@ -102,7 +102,7 @@ def test_properties():
assert instance.def_property == 3 assert instance.def_property == 3
with pytest.raises(AttributeError) as excinfo: 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) assert "unreadable attribute" in str(excinfo.value)
instance.def_property_writeonly = 4 instance.def_property_writeonly = 4
@ -127,7 +127,7 @@ def test_static_properties():
assert m.TestProperties.def_readwrite_static == 2 assert m.TestProperties.def_readwrite_static == 2
with pytest.raises(AttributeError) as excinfo: 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) assert "unreadable attribute" in str(excinfo.value)
m.TestProperties.def_writeonly_static = 3 m.TestProperties.def_writeonly_static = 3

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import pickling as m from pybind11_tests import pickling as m
try: try:

View File

@ -84,7 +84,7 @@ TEST_SUBMODULE(pytypes, m) {
#if PY_VERSION_HEX >= 0x03030000 #if PY_VERSION_HEX >= 0x03030000
// test_simple_namespace // test_simple_namespace
m.def("get_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::delattr(ns, "wrong");
py::setattr(ns, "right", py::int_(2)); py::setattr(ns, "right", py::int_(2));
return ns; return ns;

View File

@ -5,7 +5,7 @@ import sys
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import debug_enabled from pybind11_tests import debug_enabled
from pybind11_tests import pytypes as m from pybind11_tests import pytypes as m
@ -610,7 +610,7 @@ def test_weakref(create_weakref, create_weakref_with_callback):
obj = WeaklyReferenced() obj = WeaklyReferenced()
assert getweakrefcount(obj) == 0 assert getweakrefcount(obj) == 0
wr = create_weakref(obj) # noqa: F841 wr = create_weakref(obj)
assert getweakrefcount(obj) == 1 assert getweakrefcount(obj) == 1
obj = WeaklyReferenced() obj = WeaklyReferenced()

View File

@ -38,6 +38,17 @@ bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentine
return !(*it).first || !(*it).second; return !(*it).first || !(*it).second;
} }
/* Iterator where dereferencing returns prvalues instead of references. */
template<typename T>
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 { class NonCopyableInt {
public: public:
explicit NonCopyableInt(int value) : value_(value) {} explicit NonCopyableInt(int value) : value_(value) {}
@ -331,7 +342,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
py::class_<IntPairs>(m, "IntPairs") py::class_<IntPairs>(m, "IntPairs")
.def(py::init<std::vector<std::pair<int, int>>>()) .def(py::init<std::vector<std::pair<int, int>>>())
.def("nonzero", [](const IntPairs& s) { .def("nonzero", [](const IntPairs& s) {
return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
}, py::keep_alive<0, 1>()) }, py::keep_alive<0, 1>())
.def("nonzero_keys", [](const IntPairs& s) { .def("nonzero_keys", [](const IntPairs& s) {
return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
@ -340,6 +351,20 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
}, py::keep_alive<0, 1>()) }, py::keep_alive<0, 1>())
// test iterator that returns values instead of references
.def("nonref", [](const IntPairs& s) {
return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
NonRefIterator<std::pair<int, int>>(s.end()));
}, py::keep_alive<0, 1>())
.def("nonref_keys", [](const IntPairs& s) {
return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
NonRefIterator<std::pair<int, int>>(s.end()));
}, py::keep_alive<0, 1>())
.def("nonref_values", [](const IntPairs& s) {
return py::make_value_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
NonRefIterator<std::pair<int, int>>(s.end()));
}, py::keep_alive<0, 1>())
// test single-argument make_iterator // test single-argument make_iterator
.def("simple_iterator", [](IntPairs& self) { .def("simple_iterator", [](IntPairs& self) {
return py::make_iterator(self); return py::make_iterator(self);

View File

@ -52,6 +52,13 @@ def test_generalized_iterators():
next(it) 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(): def test_generalized_iterators_simple():
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_iterator()) == [ assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_iterator()) == [
(1, 2), (1, 2),

View File

@ -19,6 +19,18 @@
#include <vector> #include <vector>
#include <string> #include <string>
#if defined(PYBIND11_TEST_BOOST)
#include <boost/optional.hpp>
namespace pybind11 { namespace detail {
template <typename T>
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
template <>
struct type_caster<boost::none_t> : void_caster<boost::none_t> {};
}} // namespace pybind11::detail
#endif
// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
#if defined(PYBIND11_HAS_VARIANT) #if defined(PYBIND11_HAS_VARIANT)
using std::variant; using std::variant;
@ -59,7 +71,8 @@ namespace std {
template <template <typename> class OptionalImpl, typename T> template <template <typename> class OptionalImpl, typename T>
struct OptionalHolder struct OptionalHolder
{ {
OptionalHolder() = default; // NOLINTNEXTLINE(modernize-use-equals-default): breaks GCC 4.8
OptionalHolder() {};
bool member_initialized() const { bool member_initialized() const {
return member && member->initialized; return member && member->initialized;
} }
@ -67,6 +80,95 @@ struct OptionalHolder
}; };
enum class EnumType {
kSet = 42,
kUnset = 85,
};
// This is used to test that return-by-ref and return-by-copy policies are
// handled properly for optional types. This is a regression test for a dangling
// reference issue. The issue seemed to require the enum value type to
// reproduce - it didn't seem to happen if the value type is just an integer.
template <template <typename> class OptionalImpl>
class OptionalProperties {
public:
using OptionalEnumValue = OptionalImpl<EnumType>;
OptionalProperties() : value(EnumType::kSet) {}
~OptionalProperties() {
// Reset value to detect use-after-destruction.
// This is set to a specific value rather than nullopt to ensure that
// the memory that contains the value gets re-written.
value = EnumType::kUnset;
}
OptionalEnumValue& access_by_ref() { return value; }
OptionalEnumValue access_by_copy() { return value; }
private:
OptionalEnumValue value;
};
// This type mimics aspects of boost::optional from old versions of Boost,
// which exposed a dangling reference bug in Pybind11. Recent versions of
// boost::optional, as well as libstdc++'s std::optional, don't seem to be
// affected by the same issue. This is meant to be a minimal implementation
// required to reproduce the issue, not fully standard-compliant.
// See issue #3330 for more details.
template <typename T>
class ReferenceSensitiveOptional {
public:
using value_type = T;
ReferenceSensitiveOptional() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
ReferenceSensitiveOptional(const T& value) : storage{value} {}
// NOLINTNEXTLINE(google-explicit-constructor)
ReferenceSensitiveOptional(T&& value) : storage{std::move(value)} {}
ReferenceSensitiveOptional& operator=(const T& value) {
storage = {value};
return *this;
}
ReferenceSensitiveOptional& operator=(T&& value) {
storage = {std::move(value)};
return *this;
}
template <typename... Args>
T& emplace(Args&&... args) {
storage.clear();
storage.emplace_back(std::forward<Args>(args)...);
return storage.back();
}
const T& value() const noexcept {
assert(!storage.empty());
return storage[0];
}
const T& operator*() const noexcept {
return value();
}
const T* operator->() const noexcept {
return &value();
}
explicit operator bool() const noexcept {
return !storage.empty();
}
private:
std::vector<T> storage;
};
namespace pybind11 { namespace detail {
template <typename T>
struct type_caster<ReferenceSensitiveOptional<T>> : optional_caster<ReferenceSensitiveOptional<T>> {};
} // namespace detail
} // namespace pybind11
TEST_SUBMODULE(stl, m) { TEST_SUBMODULE(stl, m) {
// test_vector // test_vector
m.def("cast_vector", []() { return std::vector<int>{1}; }); m.def("cast_vector", []() { return std::vector<int>{1}; });
@ -145,6 +247,10 @@ TEST_SUBMODULE(stl, m) {
return v; return v;
}); });
pybind11::enum_<EnumType>(m, "EnumType")
.value("kSet", EnumType::kSet)
.value("kUnset", EnumType::kUnset);
// test_move_out_container // test_move_out_container
struct MoveOutContainer { struct MoveOutContainer {
struct Value { int value; }; struct Value { int value; };
@ -213,6 +319,12 @@ TEST_SUBMODULE(stl, m) {
.def(py::init<>()) .def(py::init<>())
.def_readonly("member", &opt_holder::member) .def_readonly("member", &opt_holder::member)
.def("member_initialized", &opt_holder::member_initialized); .def("member_initialized", &opt_holder::member_initialized);
using opt_props = OptionalProperties<std::optional>;
pybind11::class_<opt_props>(m, "OptionalProperties")
.def(pybind11::init<>())
.def_property_readonly("access_by_ref", &opt_props::access_by_ref)
.def_property_readonly("access_by_copy", &opt_props::access_by_copy);
#endif #endif
#ifdef PYBIND11_HAS_EXP_OPTIONAL #ifdef PYBIND11_HAS_EXP_OPTIONAL
@ -239,8 +351,75 @@ TEST_SUBMODULE(stl, m) {
.def(py::init<>()) .def(py::init<>())
.def_readonly("member", &opt_exp_holder::member) .def_readonly("member", &opt_exp_holder::member)
.def("member_initialized", &opt_exp_holder::member_initialized); .def("member_initialized", &opt_exp_holder::member_initialized);
using opt_exp_props = OptionalProperties<std::experimental::optional>;
pybind11::class_<opt_exp_props>(m, "OptionalExpProperties")
.def(pybind11::init<>())
.def_property_readonly("access_by_ref", &opt_exp_props::access_by_ref)
.def_property_readonly("access_by_copy", &opt_exp_props::access_by_copy);
#endif #endif
#if defined(PYBIND11_TEST_BOOST)
// test_boost_optional
m.attr("has_boost_optional") = true;
using boost_opt_int = boost::optional<int>;
using boost_opt_no_assign = boost::optional<NoAssign>;
m.def("double_or_zero_boost", [](const boost_opt_int& x) -> int {
return x.value_or(0) * 2;
});
m.def("half_or_none_boost", [](int x) -> boost_opt_int {
return x != 0 ? boost_opt_int(x / 2) : boost_opt_int();
});
m.def("test_nullopt_boost", [](boost_opt_int x) {
return x.value_or(42);
}, py::arg_v("x", boost::none, "None"));
m.def("test_no_assign_boost", [](const boost_opt_no_assign &x) {
return x ? x->value : 42;
}, py::arg_v("x", boost::none, "None"));
using opt_boost_holder = OptionalHolder<boost::optional, MoveOutDetector>;
py::class_<opt_boost_holder>(m, "OptionalBoostHolder", "Class with optional member")
.def(py::init<>())
.def_readonly("member", &opt_boost_holder::member)
.def("member_initialized", &opt_boost_holder::member_initialized);
using opt_boost_props = OptionalProperties<boost::optional>;
pybind11::class_<opt_boost_props>(m, "OptionalBoostProperties")
.def(pybind11::init<>())
.def_property_readonly("access_by_ref", &opt_boost_props::access_by_ref)
.def_property_readonly("access_by_copy", &opt_boost_props::access_by_copy);
#endif
// test_refsensitive_optional
using refsensitive_opt_int = ReferenceSensitiveOptional<int>;
using refsensitive_opt_no_assign = ReferenceSensitiveOptional<NoAssign>;
m.def("double_or_zero_refsensitive", [](const refsensitive_opt_int& x) -> int {
return (x ? x.value() : 0) * 2;
});
m.def("half_or_none_refsensitive", [](int x) -> refsensitive_opt_int {
return x != 0 ? refsensitive_opt_int(x / 2) : refsensitive_opt_int();
});
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("test_nullopt_refsensitive", [](refsensitive_opt_int x) {
return x ? x.value() : 42;
}, py::arg_v("x", refsensitive_opt_int(), "None"));
m.def("test_no_assign_refsensitive", [](const refsensitive_opt_no_assign &x) {
return x ? x->value : 42;
}, py::arg_v("x", refsensitive_opt_no_assign(), "None"));
using opt_refsensitive_holder = OptionalHolder<ReferenceSensitiveOptional, MoveOutDetector>;
py::class_<opt_refsensitive_holder>(m, "OptionalRefSensitiveHolder", "Class with optional member")
.def(py::init<>())
.def_readonly("member", &opt_refsensitive_holder::member)
.def("member_initialized", &opt_refsensitive_holder::member_initialized);
using opt_refsensitive_props = OptionalProperties<ReferenceSensitiveOptional>;
pybind11::class_<opt_refsensitive_props>(m, "OptionalRefSensitiveProperties")
.def(pybind11::init<>())
.def_property_readonly("access_by_ref", &opt_refsensitive_props::access_by_ref)
.def_property_readonly("access_by_copy", &opt_refsensitive_props::access_by_copy);
#ifdef PYBIND11_HAS_FILESYSTEM #ifdef PYBIND11_HAS_FILESYSTEM
// test_fs_path // test_fs_path
m.attr("has_filesystem") = true; m.attr("has_filesystem") = true;
@ -280,8 +459,12 @@ TEST_SUBMODULE(stl, m) {
m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {}); m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
#if defined(PYBIND11_HAS_OPTIONAL) #if defined(PYBIND11_HAS_OPTIONAL)
m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {}); m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
#elif defined(PYBIND11_HAS_EXP_OPTIONAL) #endif
m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {}); #if defined(PYBIND11_HAS_EXP_OPTIONAL)
m.def("tpl_constr_optional_exp", [](std::experimental::optional<TplCtorClass> &) {});
#endif
#if defined(PYBIND11_TEST_BOOST)
m.def("tpl_constr_optional_boost", [](boost::optional<TplCtorClass> &) {});
#endif #endif
// test_vec_of_reference_wrapper // test_vec_of_reference_wrapper

View File

@ -132,6 +132,10 @@ def test_optional():
assert mvalue.initialized assert mvalue.initialized
assert holder.member_initialized() assert holder.member_initialized()
props = m.OptionalProperties()
assert int(props.access_by_ref) == 42
assert int(props.access_by_copy) == 42
@pytest.mark.skipif( @pytest.mark.skipif(
not hasattr(m, "has_exp_optional"), reason="no <experimental/optional>" not hasattr(m, "has_exp_optional"), reason="no <experimental/optional>"
@ -160,6 +164,69 @@ def test_exp_optional():
assert mvalue.initialized assert mvalue.initialized
assert holder.member_initialized() assert holder.member_initialized()
props = m.OptionalExpProperties()
assert int(props.access_by_ref) == 42
assert int(props.access_by_copy) == 42
@pytest.mark.skipif(not hasattr(m, "has_boost_optional"), reason="no <boost/optional>")
def test_boost_optional():
assert m.double_or_zero_boost(None) == 0
assert m.double_or_zero_boost(42) == 84
pytest.raises(TypeError, m.double_or_zero_boost, "foo")
assert m.half_or_none_boost(0) is None
assert m.half_or_none_boost(42) == 21
pytest.raises(TypeError, m.half_or_none_boost, "foo")
assert m.test_nullopt_boost() == 42
assert m.test_nullopt_boost(None) == 42
assert m.test_nullopt_boost(42) == 42
assert m.test_nullopt_boost(43) == 43
assert m.test_no_assign_boost() == 42
assert m.test_no_assign_boost(None) == 42
assert m.test_no_assign_boost(m.NoAssign(43)) == 43
pytest.raises(TypeError, m.test_no_assign_boost, 43)
holder = m.OptionalBoostHolder()
mvalue = holder.member
assert mvalue.initialized
assert holder.member_initialized()
props = m.OptionalBoostProperties()
assert int(props.access_by_ref) == 42
assert int(props.access_by_copy) == 42
def test_reference_sensitive_optional():
assert m.double_or_zero_refsensitive(None) == 0
assert m.double_or_zero_refsensitive(42) == 84
pytest.raises(TypeError, m.double_or_zero_refsensitive, "foo")
assert m.half_or_none_refsensitive(0) is None
assert m.half_or_none_refsensitive(42) == 21
pytest.raises(TypeError, m.half_or_none_refsensitive, "foo")
assert m.test_nullopt_refsensitive() == 42
assert m.test_nullopt_refsensitive(None) == 42
assert m.test_nullopt_refsensitive(42) == 42
assert m.test_nullopt_refsensitive(43) == 43
assert m.test_no_assign_refsensitive() == 42
assert m.test_no_assign_refsensitive(None) == 42
assert m.test_no_assign_refsensitive(m.NoAssign(43)) == 43
pytest.raises(TypeError, m.test_no_assign_refsensitive, 43)
holder = m.OptionalRefSensitiveHolder()
mvalue = holder.member
assert mvalue.initialized
assert holder.member_initialized()
props = m.OptionalRefSensitiveProperties()
assert int(props.access_by_ref) == 42
assert int(props.access_by_copy) == 42
@pytest.mark.skipif(not hasattr(m, "has_filesystem"), reason="no <filesystem>") @pytest.mark.skipif(not hasattr(m, "has_filesystem"), reason="no <filesystem>")
def test_fs_path(): def test_fs_path():

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import stl_binders as m from pybind11_tests import stl_binders as m

View File

@ -174,6 +174,25 @@ struct DispatchIssue : Base {
} }
}; };
// An abstract adder class that uses visitor pattern to add two data
// objects and send the result to the visitor functor
struct AdderBase {
struct Data {};
using DataVisitor = std::function<void (const Data&)>;
virtual void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const = 0;
virtual ~AdderBase() = default;
AdderBase() = default;
AdderBase(const AdderBase&) = delete;
};
struct Adder : AdderBase {
void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const override {
PYBIND11_OVERRIDE_PURE_NAME(void, AdderBase, "__call__", operator(), first, second, visitor);
}
};
static void test_gil() { static void test_gil() {
{ {
py::gil_scoped_acquire lock; py::gil_scoped_acquire lock;
@ -295,6 +314,27 @@ TEST_SUBMODULE(virtual_functions, m) {
m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); }); m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
// test_recursive_dispatch_issue
// #3357: Recursive dispatch fails to find python function override
pybind11::class_<AdderBase, Adder>(m, "Adder")
.def(pybind11::init<>())
.def("__call__", &AdderBase::operator());
pybind11::class_<AdderBase::Data>(m, "Data")
.def(pybind11::init<>());
m.def("add2", [](const AdderBase::Data& first, const AdderBase::Data& second,
const AdderBase& adder, const AdderBase::DataVisitor& visitor) {
adder(first, second, visitor);
});
m.def("add3", [](const AdderBase::Data& first, const AdderBase::Data& second, const AdderBase::Data& third,
const AdderBase& adder, const AdderBase::DataVisitor& visitor) {
adder(first, second, [&] (const AdderBase::Data& first_plus_second) {
adder(first_plus_second, third, visitor);
});
});
// test_override_ref // test_override_ref
// #392/397: overriding reference-returning functions // #392/397: overriding reference-returning functions
class OverrideTest { class OverrideTest {

View File

@ -257,6 +257,39 @@ def test_dispatch_issue(msg):
assert m.dispatch_issue_go(b) == "Yay.." assert m.dispatch_issue_go(b) == "Yay.."
def test_recursive_dispatch_issue(msg):
"""#3357: Recursive dispatch fails to find python function override"""
class Data(m.Data):
def __init__(self, value):
super(Data, self).__init__()
self.value = value
class Adder(m.Adder):
def __call__(self, first, second, visitor):
# lambda is a workaround, which adds extra frame to the
# current CPython thread. Removing lambda reveals the bug
# [https://github.com/pybind/pybind11/issues/3357]
(lambda: visitor(Data(first.value + second.value)))()
class StoreResultVisitor:
def __init__(self):
self.result = None
def __call__(self, data):
self.result = data.value
store = StoreResultVisitor()
m.add2(Data(1), Data(2), Adder(), store)
assert store.result == 3
# without lambda in Adder class, this function fails with
# RuntimeError: Tried to call pure virtual function "AdderBase::__call__"
m.add3(Data(1), Data(2), Data(3), Adder(), store)
assert store.result == 6
def test_override_ref(): def test_override_ref():
"""#392/397: overriding reference-returning functions""" """#392/397: overriding reference-returning functions"""
o = m.OverrideTest("asdf") o = m.OverrideTest("asdf")

View File

@ -20,6 +20,7 @@ Adds the following functions::
#]======================================================] #]======================================================]
# CMake 3.10 has an include_guard command, but we can't use that yet # CMake 3.10 has an include_guard command, but we can't use that yet
# include_guard(global) (pre-CMake 3.10)
if(TARGET pybind11::lto) if(TARGET pybind11::lto)
return() return()
endif() endif()

View File

@ -5,6 +5,12 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
if(CMAKE_VERSION VERSION_LESS 3.12)
message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12")
endif()
include_guard(GLOBAL)
get_property( get_property(
is_config is_config
TARGET pybind11::headers TARGET pybind11::headers
@ -16,10 +22,6 @@ else()
set(_pybind11_quiet "") set(_pybind11_quiet "")
endif() endif()
if(CMAKE_VERSION VERSION_LESS 3.12)
message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12")
endif()
if(NOT Python_FOUND if(NOT Python_FOUND
AND NOT Python3_FOUND AND NOT Python3_FOUND
AND NOT Python2_FOUND) AND NOT Python2_FOUND)

View File

@ -5,6 +5,11 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
# include_guard(global) (pre-CMake 3.10)
if(TARGET pybind11::python_headers)
return()
endif()
# Built-in in CMake 3.5+ # Built-in in CMake 3.5+
include(CMakeParseArguments) include(CMakeParseArguments)
@ -38,7 +43,7 @@ endif()
# A user can set versions manually too # A user can set versions manually too
set(Python_ADDITIONAL_VERSIONS set(Python_ADDITIONAL_VERSIONS
"3.10;3.9;3.8;3.7;3.6;3.5;3.4" "3.11;3.10;3.9;3.8;3.7;3.6;3.5;3.4"
CACHE INTERNAL "") CACHE INTERNAL "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")