mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-11 08:03:55 +00:00
Merge branch 'master' into stable
This commit is contained in:
commit
acae930123
83
.github/workflows/ci.yml
vendored
83
.github/workflows/ci.yml
vendored
@ -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: >-
|
||||
|
9
.github/workflows/pip.yml
vendored
9
.github/workflows/pip.yml
vendored
@ -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/
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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``:
|
||||
|
@ -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<int>();
|
||||
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.
|
||||
|
@ -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<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
|
||||
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
|
||||
``py::register_exception_translator(translator)`` and
|
||||
|
@ -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<T1, T2, T3...>``. The
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
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:
|
||||
|
@ -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 <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
|
||||
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 <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
|
||||
`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() <add_ostream_redirect>` function:
|
||||
manager, using the ``py::add_ostream_redirect() <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.
|
||||
|
||||
|
@ -6,6 +6,54 @@ Changelog
|
||||
Starting with version 1.8.0, pybind11 releases use a `semantic versioning
|
||||
<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)
|
||||
--------------------
|
||||
|
||||
@ -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 <https://github.com/pybind/pybind11/pull/2650>`_
|
||||
`#2650 <https://github.com/pybinyyd/pybind11/pull/2650>`_
|
||||
|
||||
* 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>`_
|
||||
|
||||
* ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new
|
||||
@ -47,6 +95,7 @@ New features:
|
||||
`#3293 <https://github.com/pybind/pybind11/pull/3293>`_
|
||||
|
||||
* 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).
|
||||
* Allow ``__contains__`` to take any object.
|
||||
@ -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 <https://github.com/pybind/pybind11/pull/2409>`_
|
||||
@ -1398,7 +1447,7 @@ v2.2.0 (August 31, 2017)
|
||||
* Intel C++ compiler compatibility fixes.
|
||||
`#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>`_.
|
||||
|
||||
* Added ``py::hash`` to fetch the hash value of Python objects, and
|
||||
|
@ -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 <https://github.com/pybind/pybind11/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
|
||||
|
||||
|
@ -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
|
||||
====
|
||||
|
@ -1036,16 +1036,6 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
|
||||
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
|
||||
/// Annotation for arguments
|
||||
struct arg {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<std::string>() +
|
||||
" " + handle(f_code->co_filename).cast<std::string>() +
|
||||
"(" + 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;
|
||||
Py_DECREF(f_code);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -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 <Eigen/Core>
|
||||
#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
|
||||
// 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<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
if (!values || !innerIndices || !outerIndices)
|
||||
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,
|
||||
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data());
|
||||
|
||||
|
@ -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 <klemens.morgenstern@ed-chemnitz.de> and
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1124,6 +1124,15 @@ inline dict globals() {
|
||||
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)
|
||||
/// 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 <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()))>
|
||||
// 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 <typename Iterator, typename SFINAE = decltype(*std::declval<Iterator &>())>
|
||||
struct iterator_access {
|
||||
using result_type = decltype((*std::declval<Iterator &>()));
|
||||
using result_type = decltype(*std::declval<Iterator &>());
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
result_type operator()(Iterator &it) const {
|
||||
return *it;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).first)) >
|
||||
struct iterator_key_access {
|
||||
using result_type = decltype(((*std::declval<Iterator &>()).first));
|
||||
template <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()).first) >
|
||||
class iterator_key_access {
|
||||
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 {
|
||||
return (*it).first;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).second))>
|
||||
struct iterator_value_access {
|
||||
using result_type = decltype(((*std::declval<Iterator &>()).second));
|
||||
template <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()).second)>
|
||||
class iterator_value_access {
|
||||
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 {
|
||||
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
|
||||
|
@ -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> { };
|
||||
|
||||
// This type caster is intended to be used for std::optional and std::experimental::optional
|
||||
template<typename T> struct optional_caster {
|
||||
using value_conv = make_caster<typename T::value_type>;
|
||||
template<typename Type, typename Value = typename Type::value_type> struct optional_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
template <typename T_>
|
||||
static handle cast(T_ &&src, return_value_policy policy, handle parent) {
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!src)
|
||||
return none().inc_ref();
|
||||
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) {
|
||||
@ -269,11 +269,11 @@ template<typename T> struct optional_caster {
|
||||
if (!inner_caster.load(src, convert))
|
||||
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;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]"));
|
||||
PYBIND11_TYPE_CASTER(Type, _("Optional[") + value_conv::name + _("]"));
|
||||
};
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
|
@ -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",
|
||||
|
@ -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("."))
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
import env
|
||||
from pybind11_tests import pickling as m
|
||||
|
||||
try:
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -38,6 +38,17 @@ bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentine
|
||||
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 {
|
||||
public:
|
||||
explicit NonCopyableInt(int value) : value_(value) {}
|
||||
@ -340,6 +351,20 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(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<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
|
||||
.def("simple_iterator", [](IntPairs& self) {
|
||||
return py::make_iterator(self);
|
||||
|
@ -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),
|
||||
|
@ -19,6 +19,18 @@
|
||||
#include <vector>
|
||||
#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
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
using std::variant;
|
||||
@ -59,7 +71,8 @@ namespace std {
|
||||
template <template <typename> class OptionalImpl, typename T>
|
||||
struct OptionalHolder
|
||||
{
|
||||
OptionalHolder() = default;
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): breaks GCC 4.8
|
||||
OptionalHolder() {};
|
||||
bool member_initialized() const {
|
||||
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_vector
|
||||
m.def("cast_vector", []() { return std::vector<int>{1}; });
|
||||
@ -145,6 +247,10 @@ TEST_SUBMODULE(stl, m) {
|
||||
return v;
|
||||
});
|
||||
|
||||
pybind11::enum_<EnumType>(m, "EnumType")
|
||||
.value("kSet", EnumType::kSet)
|
||||
.value("kUnset", EnumType::kUnset);
|
||||
|
||||
// test_move_out_container
|
||||
struct MoveOutContainer {
|
||||
struct Value { int value; };
|
||||
@ -213,6 +319,12 @@ TEST_SUBMODULE(stl, m) {
|
||||
.def(py::init<>())
|
||||
.def_readonly("member", &opt_holder::member)
|
||||
.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
|
||||
|
||||
#ifdef PYBIND11_HAS_EXP_OPTIONAL
|
||||
@ -239,8 +351,75 @@ TEST_SUBMODULE(stl, m) {
|
||||
.def(py::init<>())
|
||||
.def_readonly("member", &opt_exp_holder::member)
|
||||
.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
|
||||
|
||||
#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
|
||||
// test_fs_path
|
||||
m.attr("has_filesystem") = true;
|
||||
@ -280,8 +459,12 @@ TEST_SUBMODULE(stl, m) {
|
||||
m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
|
||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {});
|
||||
#endif
|
||||
#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
|
||||
|
||||
// test_vec_of_reference_wrapper
|
||||
|
@ -132,6 +132,10 @@ def test_optional():
|
||||
assert mvalue.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(
|
||||
not hasattr(m, "has_exp_optional"), reason="no <experimental/optional>"
|
||||
@ -160,6 +164,69 @@ def test_exp_optional():
|
||||
assert mvalue.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>")
|
||||
def test_fs_path():
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
import env
|
||||
from pybind11_tests import stl_binders as m
|
||||
|
||||
|
||||
|
@ -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() {
|
||||
{
|
||||
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(); });
|
||||
|
||||
// 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
|
||||
// #392/397: overriding reference-returning functions
|
||||
class OverrideTest {
|
||||
|
@ -257,6 +257,39 @@ def test_dispatch_issue(msg):
|
||||
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():
|
||||
"""#392/397: overriding reference-returning functions"""
|
||||
o = m.OverrideTest("asdf")
|
||||
|
@ -20,6 +20,7 @@ Adds the following functions::
|
||||
#]======================================================]
|
||||
|
||||
# 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)
|
||||
return()
|
||||
endif()
|
||||
|
@ -5,6 +5,12 @@
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# 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(
|
||||
is_config
|
||||
TARGET pybind11::headers
|
||||
@ -16,10 +22,6 @@ else()
|
||||
set(_pybind11_quiet "")
|
||||
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
|
||||
AND NOT Python3_FOUND
|
||||
AND NOT Python2_FOUND)
|
||||
|
@ -5,6 +5,11 @@
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# 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+
|
||||
include(CMakeParseArguments)
|
||||
|
||||
@ -38,7 +43,7 @@ endif()
|
||||
|
||||
# A user can set versions manually too
|
||||
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 "")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
Loading…
Reference in New Issue
Block a user