Squashed 'wrap/' changes from 2f136936d..bd57210d9
bd57210d9 Merge pull request #162 from borglab/matlab-updates 1467cf23a small improvements to Matlab wrapper ce4d872c3 support for Apple Silicon 246c723e2 Merge pull request #161 from borglab/fix-matlab-enum 4a4ddb912 allow optional std:: for pair 390cb8092 Merge pull request #160 from borglab/upgrade-pybind11 c48bfa418 upgrade pybind11 to v2.11.1 c941bdd48 update gitignore git-subtree-dir: wrap git-subtree-split: bd57210d9aa620babbffe7eeb43abd77fea641e3release/4.3a0
parent
30a39a0bdb
commit
c7abbad0bc
|
@ -5,6 +5,8 @@ __pycache__/
|
|||
*dist*
|
||||
*.egg-info
|
||||
|
||||
**/.DS_Store
|
||||
|
||||
# Files related to code coverage stats
|
||||
**/.coverage
|
||||
|
||||
|
|
|
@ -105,7 +105,12 @@ function(wrap_library_internal interfaceHeader moduleName linkLibraries extraInc
|
|||
set(mexModuleExt mexglx)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
set(mexModuleExt mexmaci64)
|
||||
check_cxx_compiler_flag("-arch arm64" arm64Supported)
|
||||
if (arm64Supported)
|
||||
set(mexModuleExt mexmaca64)
|
||||
else()
|
||||
set(mexModuleExt mexmaci64)
|
||||
endif()
|
||||
elseif(MSVC)
|
||||
if(CMAKE_CL_64)
|
||||
set(mexModuleExt mexw64)
|
||||
|
@ -299,7 +304,12 @@ function(wrap_library_internal interfaceHeader moduleName linkLibraries extraInc
|
|||
APPEND
|
||||
PROPERTY COMPILE_FLAGS "/bigobj")
|
||||
elseif(APPLE)
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/maci64")
|
||||
check_cxx_compiler_flag("-arch arm64" arm64Supported)
|
||||
if (arm64Supported)
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/maca64")
|
||||
else()
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/maci64")
|
||||
endif()
|
||||
target_link_libraries(
|
||||
${moduleName}_matlab_wrapper "${mxLibPath}/libmex.dylib"
|
||||
"${mxLibPath}/libmx.dylib" "${mxLibPath}/libmat.dylib")
|
||||
|
@ -367,7 +377,12 @@ function(check_conflicting_libraries_internal libraries)
|
|||
if(UNIX)
|
||||
# Set path for matlab's built-in libraries
|
||||
if(APPLE)
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/maci64")
|
||||
check_cxx_compiler_flag("-arch arm64" arm64Supported)
|
||||
if (arm64Supported)
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/maca64")
|
||||
else()
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/maci64")
|
||||
endif()
|
||||
else()
|
||||
if(CMAKE_CL_64)
|
||||
set(mxLibPath "${MATLAB_ROOT}/bin/glnxa64")
|
||||
|
|
|
@ -12,7 +12,8 @@ Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellae
|
|||
|
||||
from typing import Any, Iterable, List, Union
|
||||
|
||||
from pyparsing import Optional, ParseResults, delimitedList # type: ignore
|
||||
from pyparsing import (Literal, Optional, ParseResults, # type: ignore
|
||||
delimitedList)
|
||||
|
||||
from .template import Template
|
||||
from .tokens import (COMMA, DEFAULT_ARG, EQUAL, IDENT, LOPBRACK, LPAREN, PAIR,
|
||||
|
@ -105,8 +106,10 @@ class ReturnType:
|
|||
|
||||
The return type can either be a single type or a pair such as <type1, type2>.
|
||||
"""
|
||||
# rule to parse optional std:: in front of `pair`
|
||||
optional_std = Optional(Literal('std::')).suppress()
|
||||
_pair = (
|
||||
PAIR.suppress() #
|
||||
optional_std + PAIR.suppress() #
|
||||
+ LOPBRACK #
|
||||
+ Type.rule("type1") #
|
||||
+ COMMA #
|
||||
|
|
|
@ -1284,7 +1284,8 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
[instantiated_class.name])
|
||||
else:
|
||||
# Get the full namespace
|
||||
class_name = ".".join(instantiated_class.parent.full_namespaces()[1:])
|
||||
class_name = ".".join(
|
||||
instantiated_class.parent.full_namespaces()[1:])
|
||||
|
||||
if class_name != "":
|
||||
class_name += '.'
|
||||
|
@ -1860,6 +1861,7 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
"""
|
||||
for c in cc_content:
|
||||
if isinstance(c, list):
|
||||
# c is a namespace
|
||||
if len(c) == 0:
|
||||
continue
|
||||
|
||||
|
@ -1875,6 +1877,7 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
self.generate_content(sub_content[1], path_to_folder)
|
||||
|
||||
elif isinstance(c[1], list):
|
||||
# c is a wrapped function
|
||||
path_to_folder = osp.join(path, c[0])
|
||||
|
||||
if not osp.isdir(path_to_folder):
|
||||
|
@ -1882,11 +1885,13 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
os.makedirs(path_to_folder, exist_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
for sub_content in c[1]:
|
||||
path_to_file = osp.join(path_to_folder, sub_content[0])
|
||||
with open(path_to_file, 'w') as f:
|
||||
f.write(sub_content[1])
|
||||
else:
|
||||
# c is a wrapped class
|
||||
path_to_file = osp.join(path, c[0])
|
||||
|
||||
if not osp.isdir(path_to_file):
|
||||
|
@ -1921,6 +1926,8 @@ class MatlabWrapper(CheckMixin, FormatMixin):
|
|||
for module in modules.values():
|
||||
# Wrap the full namespace
|
||||
self.wrap_namespace(module)
|
||||
|
||||
# Generate the wrapping code (both C++ and .m files)
|
||||
self.generate_wrapper(module)
|
||||
|
||||
# Generate the corresponding .m and .cpp files
|
||||
|
|
|
@ -63,6 +63,8 @@ Checks: |
|
|||
-bugprone-unused-raii,
|
||||
|
||||
CheckOptions:
|
||||
- key: modernize-use-equals-default.IgnoreMacros
|
||||
value: false
|
||||
- key: performance-for-range-copy.WarnOnAllAutoCopies
|
||||
value: true
|
||||
- key: performance-inefficient-string-concatenation.StrictMode
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
|
||||
template <typename ThisT>
|
||||
auto &this_ = static_cast<ThisT &>(*this);
|
||||
if (load_impl<ThisT>(temp, false)) {
|
||||
ssize_t nd = 0;
|
||||
auto trivial = broadcast(buffers, nd, shape);
|
||||
auto ndim = (size_t) nd;
|
||||
int nd;
|
||||
ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
|
||||
struct IntStruct {
|
||||
explicit IntStruct(int v) : value(v){};
|
||||
~IntStruct() { value = -value; }
|
||||
IntStruct(const IntStruct &) = default;
|
||||
IntStruct &operator=(const IntStruct &) = default;
|
||||
py::class_<IntStruct>(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); }));
|
||||
py::implicitly_convertible<int, IntStruct>();
|
||||
m.def("test", [](int expected, const IntStruct &in) {
|
||||
[](int expected, const IntStruct &in) {
|
|
@ -235,8 +235,8 @@ directory inside your pybind11 git clone. Files will be modified in place,
|
|||
so you can use git to monitor the changes.
|
||||
|
||||
```bash
|
||||
docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:13
|
||||
apt-get update && apt-get install -y python3-dev python3-pytest
|
||||
docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:15-bullseye
|
||||
apt-get update && apt-get install -y git python3-dev python3-pytest
|
||||
cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17
|
||||
cmake --build build -j 2
|
||||
```
|
||||
|
|
|
@ -6,7 +6,8 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Maintainers will only make a best effort to triage PRs. Please do your best to make the issue as easy to act on as possible, and only open if clearly a problem with pybind11 (ask first if unsure).
|
||||
Please do your best to make the issue as easy to act on as possible, and only submit here if there is clearly a problem with pybind11 (ask first if unsure). **Note that a reproducer in a PR is much more likely to get immediate attention.**
|
||||
|
||||
- type: checkboxes
|
||||
id: steps
|
||||
attributes:
|
||||
|
@ -20,6 +21,13 @@ body:
|
|||
- label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new).
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: What version (or hash if on master) of pybind11 are you using?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
@ -40,6 +48,14 @@ body:
|
|||
The code should be minimal, have no external dependencies, isolate the
|
||||
function(s) that cause breakage. Submit matched and complete C++ and
|
||||
Python snippets that can be easily compiled and run to diagnose the
|
||||
issue. If possible, make a PR with a new, failing test to give us a
|
||||
starting point to work on!
|
||||
issue. — Note that a reproducer in a PR is much more likely to get
|
||||
immediate attention: failing tests in the pybind11 CI are the best
|
||||
starting point for working out fixes.
|
||||
render: text
|
||||
|
||||
- type: input
|
||||
id: regression
|
||||
attributes:
|
||||
label: Is this a regression? Put the last known working version here if it is.
|
||||
description: Put the last known working version here if this is a regression.
|
||||
value: Not a regression
|
||||
|
|
|
@ -9,14 +9,19 @@ on:
|
|||
- stable
|
||||
- v*
|
||||
|
||||
permissions: read-all
|
||||
|
||||
concurrency:
|
||||
group: test-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||
PIP_ONLY_BINARY: numpy
|
||||
FORCE_COLOR: 3
|
||||
PYTEST_TIMEOUT: 300
|
||||
# For cmake:
|
||||
VERBOSE: 1
|
||||
|
||||
jobs:
|
||||
# This is the "main" test suite, which tests a large number of different
|
||||
|
@ -25,15 +30,16 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, windows-2022, macos-latest]
|
||||
runs-on: [ubuntu-20.04, windows-2022, macos-latest]
|
||||
python:
|
||||
- '3.6'
|
||||
- '3.9'
|
||||
- '3.10'
|
||||
- '3.11-dev'
|
||||
- 'pypy-3.7'
|
||||
- '3.11'
|
||||
- '3.12'
|
||||
- 'pypy-3.8'
|
||||
- 'pypy-3.9'
|
||||
- 'pypy-3.10'
|
||||
|
||||
# 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,12 +48,12 @@ jobs:
|
|||
# We support an optional key: args, for cmake args
|
||||
include:
|
||||
# Just add a key
|
||||
- runs-on: ubuntu-latest
|
||||
- runs-on: ubuntu-20.04
|
||||
python: '3.6'
|
||||
args: >
|
||||
-DPYBIND11_FINDPYTHON=ON
|
||||
-DCMAKE_CXX_FLAGS="-D_=1"
|
||||
- runs-on: ubuntu-latest
|
||||
- runs-on: ubuntu-20.04
|
||||
python: 'pypy-3.8'
|
||||
args: >
|
||||
-DPYBIND11_FINDPYTHON=ON
|
||||
|
@ -69,6 +75,7 @@ jobs:
|
|||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
allow-prereleases: true
|
||||
|
||||
- name: Setup Boost (Linux)
|
||||
# Can't use boost + define _
|
||||
|
@ -80,7 +87,7 @@ jobs:
|
|||
run: brew install boost
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Cache wheels
|
||||
if: runner.os == 'macOS'
|
||||
|
@ -102,10 +109,12 @@ jobs:
|
|||
run: python -m pip install pytest-github-actions-annotate-failures
|
||||
|
||||
# First build - C++11 mode and inplace
|
||||
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here.
|
||||
- name: Configure C++11 ${{ matrix.args }}
|
||||
run: >
|
||||
cmake -S . -B .
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=11
|
||||
|
@ -119,7 +128,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' || matrix.python == '3.11-dev' || matrix.python == 'pypy-3.8'))"
|
||||
if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))"
|
||||
run: cmake --build . --target cpptest -j 2
|
||||
|
||||
- name: Interface test C++11
|
||||
|
@ -129,10 +138,12 @@ jobs:
|
|||
run: git clean -fdx
|
||||
|
||||
# Second build - C++17 mode and in a build directory
|
||||
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here.
|
||||
- name: Configure C++17
|
||||
run: >
|
||||
cmake -S . -B build2
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
|
@ -146,7 +157,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' || matrix.python == '3.11-dev' || matrix.python == 'pypy-3.8'))"
|
||||
if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))"
|
||||
run: cmake --build build2 --target cpptest
|
||||
|
||||
# Third build - C++17 mode with unstable ABI
|
||||
|
@ -158,7 +169,6 @@ jobs:
|
|||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
-DPYBIND11_INTERNALS_VERSION=10000000
|
||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||
${{ matrix.args }}
|
||||
|
||||
- name: Build (unstable ABI)
|
||||
|
@ -173,7 +183,9 @@ jobs:
|
|||
# This makes sure the setup_helpers module can build packages using
|
||||
# setuptools
|
||||
- name: Setuptools helpers test
|
||||
run: pytest tests/extra_setuptools
|
||||
run: |
|
||||
pip install setuptools
|
||||
pytest tests/extra_setuptools
|
||||
if: "!(matrix.runs-on == 'windows-2022')"
|
||||
|
||||
|
||||
|
@ -186,23 +198,23 @@ jobs:
|
|||
- python-version: "3.9"
|
||||
python-debug: true
|
||||
valgrind: true
|
||||
- python-version: "3.11-dev"
|
||||
- python-version: "3.11"
|
||||
python-debug: false
|
||||
|
||||
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
|
||||
uses: deadsnakes/action@v2.1.1
|
||||
uses: deadsnakes/action@v3.0.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
debug: ${{ matrix.python-debug }}
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Valgrind cache
|
||||
if: matrix.valgrind
|
||||
|
@ -235,8 +247,6 @@ jobs:
|
|||
python -m pip install -r tests/requirements.txt
|
||||
|
||||
- name: Configure
|
||||
env:
|
||||
SETUPTOOLS_USE_DISTUTILS: stdlib
|
||||
run: >
|
||||
cmake -S . -B build
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
|
@ -274,18 +284,27 @@ jobs:
|
|||
- dev
|
||||
std:
|
||||
- 11
|
||||
container_suffix:
|
||||
- ""
|
||||
include:
|
||||
- clang: 5
|
||||
std: 14
|
||||
- clang: 10
|
||||
std: 20
|
||||
- clang: 10
|
||||
std: 17
|
||||
- clang: 11
|
||||
std: 20
|
||||
- clang: 12
|
||||
std: 20
|
||||
- clang: 13
|
||||
std: 20
|
||||
- clang: 14
|
||||
std: 20
|
||||
- clang: 15
|
||||
std: 20
|
||||
container_suffix: "-bullseye"
|
||||
|
||||
name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64"
|
||||
container: "silkeh/clang:${{ matrix.clang }}"
|
||||
container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
@ -318,8 +337,8 @@ jobs:
|
|||
# Testing NVCC; forces sources to behave like .cu files
|
||||
cuda:
|
||||
runs-on: ubuntu-latest
|
||||
name: "🐍 3.8 • CUDA 11.2 • Ubuntu 20.04"
|
||||
container: nvidia/cuda:11.2.2-devel-ubuntu20.04
|
||||
name: "🐍 3.10 • CUDA 12.2 • Ubuntu 22.04"
|
||||
container: nvidia/cuda:12.2.0-devel-ubuntu22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
@ -384,8 +403,9 @@ jobs:
|
|||
|
||||
# Testing on CentOS 7 + PGI compilers, which seems to require more workarounds
|
||||
centos-nvhpc7:
|
||||
if: ${{ false }} # JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4690
|
||||
runs-on: ubuntu-latest
|
||||
name: "🐍 3 • CentOS7 / PGI 22.3 • x64"
|
||||
name: "🐍 3 • CentOS7 / PGI 22.9 • x64"
|
||||
container: centos:7
|
||||
|
||||
steps:
|
||||
|
@ -395,7 +415,7 @@ jobs:
|
|||
run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 yum-utils
|
||||
|
||||
- name: Install NVidia HPC SDK
|
||||
run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.3
|
||||
run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.9
|
||||
|
||||
# On CentOS 7, we have to filter a few tests (compiler internal error)
|
||||
# and allow deeper template recursion (not needed on CentOS 8 with a newer
|
||||
|
@ -405,12 +425,12 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
source /etc/profile.d/modules.sh
|
||||
module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.3
|
||||
module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.9
|
||||
cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \
|
||||
-DCMAKE_CXX_STANDARD=11 \
|
||||
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \
|
||||
-DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0" \
|
||||
-DPYBIND11_TEST_FILTER="test_smart_ptr.cpp;test_virtual_functions.cpp"
|
||||
-DPYBIND11_TEST_FILTER="test_smart_ptr.cpp"
|
||||
|
||||
# Building before installing Pip should produce a warning but not an error
|
||||
- name: Build
|
||||
|
@ -437,14 +457,14 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gcc:
|
||||
- 7
|
||||
- latest
|
||||
std:
|
||||
- 11
|
||||
include:
|
||||
- gcc: 10
|
||||
std: 20
|
||||
- { gcc: 7, std: 11 }
|
||||
- { gcc: 7, std: 17 }
|
||||
- { gcc: 8, std: 14 }
|
||||
- { gcc: 8, std: 17 }
|
||||
- { gcc: 10, std: 17 }
|
||||
- { gcc: 11, std: 20 }
|
||||
- { gcc: 12, std: 20 }
|
||||
|
||||
name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64"
|
||||
container: "gcc:${{ matrix.gcc }}"
|
||||
|
@ -459,7 +479,7 @@ jobs:
|
|||
run: python3 -m pip install --upgrade pip
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Configure
|
||||
shell: bash
|
||||
|
@ -482,6 +502,24 @@ jobs:
|
|||
- name: Interface test
|
||||
run: cmake --build build --target test_cmake_build
|
||||
|
||||
- name: Configure - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
if: matrix.gcc == '12'
|
||||
shell: bash
|
||||
run: >
|
||||
cmake -S . -B build_partial
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
|
||||
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
|
||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||
|
||||
- name: Build - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
if: matrix.gcc == '12'
|
||||
run: cmake --build build_partial -j 2
|
||||
|
||||
- name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
if: matrix.gcc == '12'
|
||||
run: cmake --build build_partial --target pytest
|
||||
|
||||
# Testing on ICC using the oneAPI apt repo
|
||||
icc:
|
||||
|
@ -731,6 +769,9 @@ jobs:
|
|||
args: -DCMAKE_CXX_STANDARD=20
|
||||
- python: 3.8
|
||||
args: -DCMAKE_CXX_STANDARD=17
|
||||
- python: 3.7
|
||||
args: -DCMAKE_CXX_STANDARD=14
|
||||
|
||||
|
||||
name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}"
|
||||
runs-on: windows-2019
|
||||
|
@ -745,10 +786,10 @@ jobs:
|
|||
architecture: x86
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Prepare MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
uses: ilammy/msvc-dev-cmd@v1.12.1
|
||||
with:
|
||||
arch: x86
|
||||
|
||||
|
@ -798,10 +839,10 @@ jobs:
|
|||
architecture: x86
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Prepare MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
uses: ilammy/msvc-dev-cmd@v1.12.1
|
||||
with:
|
||||
arch: x86
|
||||
|
||||
|
@ -849,7 +890,7 @@ jobs:
|
|||
python3 -m pip install -r tests/requirements.txt
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Configure C++20
|
||||
run: >
|
||||
|
@ -871,6 +912,21 @@ jobs:
|
|||
- name: Interface test C++20
|
||||
run: cmake --build build --target test_cmake_build
|
||||
|
||||
- name: Configure C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
run: >
|
||||
cmake -S . -B build_partial
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=20
|
||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||
|
||||
- name: Build C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
run: cmake --build build_partial -j 2
|
||||
|
||||
- name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
run: cmake --build build_partial --target pytest
|
||||
|
||||
mingw:
|
||||
name: "🐍 3 • windows-latest • ${{ matrix.sys }}"
|
||||
runs-on: windows-latest
|
||||
|
@ -905,7 +961,7 @@ jobs:
|
|||
- name: Configure C++11
|
||||
# LTO leads to many undefined reference like
|
||||
# `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&)
|
||||
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DDOWNLOAD_CATCH=ON -S . -B build
|
||||
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build
|
||||
|
||||
- name: Build C++11
|
||||
run: cmake --build build -j 2
|
||||
|
@ -923,7 +979,7 @@ jobs:
|
|||
run: git clean -fdx
|
||||
|
||||
- name: Configure C++14
|
||||
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DDOWNLOAD_CATCH=ON -S . -B build2
|
||||
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build2
|
||||
|
||||
- name: Build C++14
|
||||
run: cmake --build build2 -j 2
|
||||
|
@ -941,7 +997,7 @@ jobs:
|
|||
run: git clean -fdx
|
||||
|
||||
- name: Configure C++17
|
||||
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DDOWNLOAD_CATCH=ON -S . -B build3
|
||||
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build3
|
||||
|
||||
- name: Build C++17
|
||||
run: cmake --build build3 -j 2
|
||||
|
@ -954,3 +1010,156 @@ jobs:
|
|||
|
||||
- name: Interface test C++17
|
||||
run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build
|
||||
|
||||
windows_clang:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest]
|
||||
python: ['3.10']
|
||||
|
||||
runs-on: "${{ matrix.os }}"
|
||||
|
||||
name: "🐍 ${{ matrix.python }} • ${{ matrix.os }} • clang-latest"
|
||||
|
||||
steps:
|
||||
- name: Show env
|
||||
run: env
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Clang
|
||||
uses: egor-tensin/setup-clang@v1
|
||||
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Install ninja-build tool
|
||||
uses: seanmiddleditch/gha-setup-ninja@v3
|
||||
|
||||
- name: Run pip installs
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r tests/requirements.txt
|
||||
|
||||
- name: Show Clang++ version
|
||||
run: clang++ --version
|
||||
|
||||
- name: Show CMake version
|
||||
run: cmake --version
|
||||
|
||||
# TODO: WERROR=ON
|
||||
- name: Configure Clang
|
||||
run: >
|
||||
cmake -G Ninja -S . -B .
|
||||
-DPYBIND11_WERROR=OFF
|
||||
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_COMPILER=clang++
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
|
||||
- name: Build
|
||||
run: cmake --build . -j 2
|
||||
|
||||
- name: Python tests
|
||||
run: cmake --build . --target pytest -j 2
|
||||
|
||||
- name: C++ tests
|
||||
run: cmake --build . --target cpptest -j 2
|
||||
|
||||
- name: Interface test
|
||||
run: cmake --build . --target test_cmake_build -j 2
|
||||
|
||||
- name: Clean directory
|
||||
run: git clean -fdx
|
||||
|
||||
macos_brew_install_llvm:
|
||||
name: "macos-latest • brew install llvm"
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
# https://apple.stackexchange.com/questions/227026/how-to-install-recent-clang-with-homebrew
|
||||
LDFLAGS: '-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib'
|
||||
|
||||
steps:
|
||||
- name: Update PATH
|
||||
run: echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Show env
|
||||
run: env
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Show Clang++ version before brew install llvm
|
||||
run: clang++ --version
|
||||
|
||||
- name: brew install llvm
|
||||
run: brew install llvm
|
||||
|
||||
- name: Show Clang++ version after brew install llvm
|
||||
run: clang++ --version
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Run pip installs
|
||||
run: |
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install -r tests/requirements.txt
|
||||
python3 -m pip install numpy
|
||||
python3 -m pip install scipy
|
||||
|
||||
- name: Show CMake version
|
||||
run: cmake --version
|
||||
|
||||
- name: CMake Configure
|
||||
run: >
|
||||
cmake -S . -B .
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_COMPILER=clang++
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
|
||||
|
||||
- name: Build
|
||||
run: cmake --build . -j 2
|
||||
|
||||
- name: Python tests
|
||||
run: cmake --build . --target pytest -j 2
|
||||
|
||||
- name: C++ tests
|
||||
run: cmake --build . --target cpptest -j 2
|
||||
|
||||
- name: Interface test
|
||||
run: cmake --build . --target test_cmake_build -j 2
|
||||
|
||||
- name: CMake Configure - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
run: >
|
||||
cmake -S . -B build_partial
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_COMPILER=clang++
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
|
||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||
|
||||
- name: Build - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
run: cmake --build build_partial -j 2
|
||||
|
||||
- name: Python tests - Exercise cmake -DPYBIND11_TEST_OVERRIDE
|
||||
run: cmake --build build_partial --target pytest -j 2
|
||||
|
||||
- name: Clean directory
|
||||
run: git clean -fdx
|
||||
|
|
|
@ -9,6 +9,14 @@ on:
|
|||
- stable
|
||||
- v*
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||
# For cmake:
|
||||
VERBOSE: 1
|
||||
|
||||
jobs:
|
||||
# This tests various versions of CMake in various combinations, to make sure
|
||||
# the configure step passes.
|
||||
|
@ -16,22 +24,26 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
arch: [x64]
|
||||
cmake: ["3.23"]
|
||||
cmake: ["3.26"]
|
||||
|
||||
include:
|
||||
- runs-on: ubuntu-latest
|
||||
- runs-on: ubuntu-20.04
|
||||
arch: x64
|
||||
cmake: 3.4
|
||||
cmake: "3.5"
|
||||
|
||||
- runs-on: ubuntu-20.04
|
||||
arch: x64
|
||||
cmake: "3.27"
|
||||
|
||||
- runs-on: macos-latest
|
||||
arch: x64
|
||||
cmake: 3.7
|
||||
cmake: "3.7"
|
||||
|
||||
- runs-on: windows-2019
|
||||
arch: x64 # x86 compilers seem to be missing on 2019 image
|
||||
cmake: 3.18
|
||||
cmake: "3.18"
|
||||
|
||||
name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
@ -51,7 +63,7 @@ jobs:
|
|||
# An action for adding a specific version of CMake:
|
||||
# https://github.com/jwlawson/actions-setup-cmake
|
||||
- name: Setup CMake ${{ matrix.cmake }}
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
with:
|
||||
cmake-version: ${{ matrix.cmake }}
|
||||
|
||||
|
|
|
@ -12,8 +12,13 @@ on:
|
|||
- stable
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 3
|
||||
# For cmake:
|
||||
VERBOSE: 1
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
|
@ -36,12 +41,12 @@ jobs:
|
|||
# in .github/CONTRIBUTING.md and update as needed.
|
||||
name: Clang-Tidy
|
||||
runs-on: ubuntu-latest
|
||||
container: silkeh/clang:13
|
||||
container: silkeh/clang:15-bullseye
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install requirements
|
||||
run: apt-get update && apt-get install -y python3-dev python3-pytest
|
||||
run: apt-get update && apt-get install -y git python3-dev python3-pytest
|
||||
|
||||
- name: Configure
|
||||
run: >
|
||||
|
|
|
@ -3,14 +3,23 @@ on:
|
|||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
label:
|
||||
name: Labeler
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
|
||||
- uses: actions/labeler@main
|
||||
if: github.event.pull_request.merged == true
|
||||
if: >
|
||||
github.event.pull_request.merged == true &&
|
||||
!startsWith(github.event.pull_request.title, 'chore(deps):') &&
|
||||
!startsWith(github.event.pull_request.title, 'ci(fix):') &&
|
||||
!startsWith(github.event.pull_request.title, 'docs(changelog):')
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
configuration-path: .github/labeler_merged.yml
|
||||
|
|
|
@ -12,7 +12,11 @@ on:
|
|||
types:
|
||||
- published
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||
PIP_ONLY_BINARY: numpy
|
||||
|
||||
jobs:
|
||||
|
@ -98,13 +102,13 @@ jobs:
|
|||
- uses: actions/download-artifact@v3
|
||||
|
||||
- name: Publish standard package
|
||||
uses: pypa/gh-action-pypi-publish@v1.5.0
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.pypi_password }}
|
||||
packages_dir: standard/
|
||||
packages-dir: standard/
|
||||
|
||||
- name: Publish global package
|
||||
uses: pypa/gh-action-pypi-publish@v1.5.0
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.pypi_password_global }}
|
||||
packages_dir: global/
|
||||
packages-dir: global/
|
||||
|
|
|
@ -1,112 +1,116 @@
|
|||
|
||||
name: Upstream
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: upstream-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PIP_ONLY_BINARY: numpy
|
||||
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||
PIP_ONLY_BINARY: ":all:"
|
||||
# For cmake:
|
||||
VERBOSE: 1
|
||||
|
||||
jobs:
|
||||
standard:
|
||||
name: "🐍 3.11 latest internals • ubuntu-latest • x64"
|
||||
name: "🐍 3.12 latest • ubuntu-latest • x64"
|
||||
runs-on: ubuntu-latest
|
||||
# Only runs when the 'python dev' label is selected
|
||||
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python 3.11
|
||||
- name: Setup Python 3.12
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11-dev"
|
||||
python-version: "3.12-dev"
|
||||
|
||||
- name: Setup Boost (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
- name: Setup Boost
|
||||
run: sudo apt-get install libboost-dev
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
uses: jwlawson/actions-setup-cmake@v1.14
|
||||
|
||||
- name: Prepare env
|
||||
- name: Run pip installs
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r tests/requirements.txt
|
||||
|
||||
- name: Setup annotations on Linux
|
||||
if: runner.os == 'Linux'
|
||||
run: python -m pip install pytest-github-actions-annotate-failures
|
||||
- name: Show platform info
|
||||
run: |
|
||||
python -m platform
|
||||
cmake --version
|
||||
pip list
|
||||
|
||||
# First build - C++11 mode and inplace
|
||||
- name: Configure C++11
|
||||
run: >
|
||||
cmake -S . -B .
|
||||
cmake -S . -B build11
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=11
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
|
||||
- name: Build C++11
|
||||
run: cmake --build . -j 2
|
||||
run: cmake --build build11 -j 2
|
||||
|
||||
- name: Python tests C++11
|
||||
run: cmake --build . --target pytest -j 2
|
||||
run: cmake --build build11 --target pytest -j 2
|
||||
|
||||
- name: C++11 tests
|
||||
run: cmake --build . --target cpptest -j 2
|
||||
run: cmake --build build11 --target cpptest -j 2
|
||||
|
||||
- name: Interface test C++11
|
||||
run: cmake --build . --target test_cmake_build
|
||||
|
||||
- name: Clean directory
|
||||
run: git clean -fdx
|
||||
run: cmake --build build11 --target test_cmake_build
|
||||
|
||||
# Second build - C++17 mode and in a build directory
|
||||
- name: Configure C++17
|
||||
run: >
|
||||
cmake -S . -B build2
|
||||
cmake -S . -B build17
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
${{ matrix.args }}
|
||||
${{ matrix.args2 }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build2 -j 2
|
||||
- name: Build C++17
|
||||
run: cmake --build build17 -j 2
|
||||
|
||||
- name: Python tests
|
||||
run: cmake --build build2 --target pytest
|
||||
- name: Python tests C++17
|
||||
run: cmake --build build17 --target pytest
|
||||
|
||||
- name: C++ tests
|
||||
run: cmake --build build2 --target cpptest
|
||||
- name: C++17 tests
|
||||
run: cmake --build build17 --target cpptest
|
||||
|
||||
# Third build - C++17 mode with unstable ABI
|
||||
- name: Configure (unstable ABI)
|
||||
run: >
|
||||
cmake -S . -B build3
|
||||
cmake -S . -B build17max
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
-DPYBIND11_INTERNALS_VERSION=10000000
|
||||
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
|
||||
${{ matrix.args }}
|
||||
|
||||
- name: Build (unstable ABI)
|
||||
run: cmake --build build3 -j 2
|
||||
run: cmake --build build17max -j 2
|
||||
|
||||
- name: Python tests (unstable ABI)
|
||||
run: cmake --build build3 --target pytest
|
||||
run: cmake --build build17max --target pytest
|
||||
|
||||
- name: Interface test
|
||||
run: cmake --build build2 --target test_cmake_build
|
||||
- name: Interface test (unstable ABI)
|
||||
run: cmake --build build17max --target test_cmake_build
|
||||
|
||||
# This makes sure the setup_helpers module can build packages using
|
||||
# setuptools
|
||||
- name: Setuptools helpers test
|
||||
run: pytest tests/extra_setuptools
|
||||
run: |
|
||||
pip install setuptools
|
||||
pytest tests/extra_setuptools
|
||||
|
|
|
@ -43,3 +43,4 @@ pybind11Targets.cmake
|
|||
/pybind11/share/*
|
||||
/docs/_build/*
|
||||
.ipynb_checkpoints/
|
||||
tests/main.cpp
|
||||
|
|
|
@ -12,10 +12,62 @@
|
|||
#
|
||||
# See https://github.com/pre-commit/pre-commit
|
||||
|
||||
|
||||
ci:
|
||||
autoupdate_commit_msg: "chore(deps): update pre-commit hooks"
|
||||
autofix_commit_msg: "style: pre-commit fixes"
|
||||
autoupdate_schedule: monthly
|
||||
|
||||
# third-party content
|
||||
exclude: ^tools/JoinPaths.cmake$
|
||||
|
||||
repos:
|
||||
|
||||
# Clang format the codebase automatically
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: "v16.0.6"
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c++, c, cuda]
|
||||
|
||||
# Black, the code formatter, natively supports pre-commit
|
||||
- repo: https://github.com/psf/black
|
||||
rev: "23.3.0" # Keep in sync with blacken-docs
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
# Ruff, the Python auto-correcting linter written in Rust
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.0.276
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: ["--fix", "--show-fixes"]
|
||||
|
||||
# Check static types with mypy
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: "v1.4.1"
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: []
|
||||
exclude: ^(tests|docs)/
|
||||
additional_dependencies:
|
||||
- markdown-it-py<3 # Drop this together with dropping Python 3.7 support.
|
||||
- nox
|
||||
- rich
|
||||
- types-setuptools
|
||||
|
||||
# CMake formatting
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
rev: "v0.6.13"
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
additional_dependencies: [pyyaml]
|
||||
types: [file]
|
||||
files: (\.cmake|CMakeLists.txt)(.in)?$
|
||||
|
||||
# Standard hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: "v4.3.0"
|
||||
rev: "v4.4.0"
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
|
@ -30,109 +82,38 @@ repos:
|
|||
- id: requirements-txt-fixer
|
||||
- id: trailing-whitespace
|
||||
|
||||
# Upgrade old Python syntax
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: "v2.37.1"
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py36-plus]
|
||||
|
||||
# Nicely sort includes
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: "5.10.1"
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
# Black, the code formatter, natively supports pre-commit
|
||||
- repo: https://github.com/psf/black
|
||||
rev: "22.6.0" # Keep in sync with blacken-docs
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
# Also code format the docs
|
||||
- repo: https://github.com/asottile/blacken-docs
|
||||
rev: "v1.12.1"
|
||||
rev: "1.14.0"
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies:
|
||||
- black==22.6.0 # keep in sync with black hook
|
||||
- black==23.3.0 # keep in sync with black hook
|
||||
|
||||
# Changes tabs to spaces
|
||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||
rev: "v1.3.0"
|
||||
rev: "v1.5.1"
|
||||
hooks:
|
||||
- id: remove-tabs
|
||||
|
||||
# Avoid directional quotes
|
||||
- repo: https://github.com/sirosen/texthooks
|
||||
rev: "0.3.1"
|
||||
rev: "0.5.0"
|
||||
hooks:
|
||||
- id: fix-ligatures
|
||||
- id: fix-smartquotes
|
||||
|
||||
# Autoremoves unused imports
|
||||
- repo: https://github.com/hadialqattan/pycln
|
||||
rev: "v2.0.1"
|
||||
hooks:
|
||||
- id: pycln
|
||||
stages: [manual]
|
||||
|
||||
# Checking for common mistakes
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: "v1.9.0"
|
||||
rev: "v1.10.0"
|
||||
hooks:
|
||||
- id: python-check-blanket-noqa
|
||||
- id: python-check-blanket-type-ignore
|
||||
- id: python-no-log-warn
|
||||
- id: python-use-type-annotations
|
||||
- id: rst-backticks
|
||||
- id: rst-directive-colons
|
||||
- id: rst-inline-touching-normal
|
||||
|
||||
# Automatically remove noqa that are not used
|
||||
- repo: https://github.com/asottile/yesqa
|
||||
rev: "v1.3.0"
|
||||
hooks:
|
||||
- id: yesqa
|
||||
additional_dependencies: &flake8_dependencies
|
||||
- flake8-bugbear
|
||||
- pep8-naming
|
||||
|
||||
# Flake8 also supports pre-commit natively (same author)
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: "4.0.1"
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: ^(docs/.*|tools/.*)$
|
||||
additional_dependencies: *flake8_dependencies
|
||||
|
||||
# PyLint has native support - not always usable, but works for us
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: "v2.14.4"
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^pybind11
|
||||
|
||||
# CMake formatting
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
rev: "v0.6.13"
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
additional_dependencies: [pyyaml]
|
||||
types: [file]
|
||||
files: (\.cmake|CMakeLists.txt)(.in)?$
|
||||
|
||||
# Check static types with mypy
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: "v0.961"
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: []
|
||||
exclude: ^(tests|docs)/
|
||||
additional_dependencies: [nox, rich]
|
||||
|
||||
# Checks the manifest for missing files (native support)
|
||||
- repo: https://github.com/mgedmin/check-manifest
|
||||
rev: "0.48"
|
||||
rev: "0.49"
|
||||
hooks:
|
||||
- id: check-manifest
|
||||
# This is a slow hook, so only run this if --hook-stage manual is passed
|
||||
|
@ -140,16 +121,18 @@ repos:
|
|||
additional_dependencies: [cmake, ninja]
|
||||
|
||||
# Check for spelling
|
||||
# Use tools/codespell_ignore_lines_from_errors.py
|
||||
# to rebuild .codespell-ignore-lines
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: "v2.1.0"
|
||||
rev: "v2.2.5"
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude: ".supp$"
|
||||
args: ["-L", "nd,ot,thist"]
|
||||
args: ["-x.codespell-ignore-lines", "-Lccompiler"]
|
||||
|
||||
# Check for common shell mistakes
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: "v0.8.0.4"
|
||||
rev: "v0.9.0.5"
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
|
||||
|
@ -162,9 +145,9 @@ repos:
|
|||
entry: PyBind|Numpy|Cmake|CCache|PyTest
|
||||
exclude: ^\.pre-commit-config.yaml$
|
||||
|
||||
# Clang format the codebase automatically
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: "v14.0.6"
|
||||
# PyLint has native support - not always usable, but works for us
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: "v3.0.0a6"
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c++, c, cuda]
|
||||
- id: pylint
|
||||
files: ^pybind11
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
# All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.22)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.22)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.22)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
# Avoid infinite recursion if tests include this as a subdirectory
|
||||
|
@ -91,10 +91,16 @@ endif()
|
|||
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
|
||||
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
"Use simpler GIL management logic that does not support disassociation" OFF)
|
||||
set(PYBIND11_INTERNALS_VERSION
|
||||
""
|
||||
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
|
||||
|
||||
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
endif()
|
||||
|
||||
cmake_dependent_option(
|
||||
USE_PYTHON_INCLUDE_DIR
|
||||
"Install pybind11 headers in Python include directory instead of default installation prefix"
|
||||
|
@ -120,6 +126,9 @@ set(PYBIND11_HEADERS
|
|||
include/pybind11/complex.h
|
||||
include/pybind11/options.h
|
||||
include/pybind11/eigen.h
|
||||
include/pybind11/eigen/common.h
|
||||
include/pybind11/eigen/matrix.h
|
||||
include/pybind11/eigen/tensor.h
|
||||
include/pybind11/embed.h
|
||||
include/pybind11/eval.h
|
||||
include/pybind11/gil.h
|
||||
|
@ -131,7 +140,8 @@ set(PYBIND11_HEADERS
|
|||
include/pybind11/pytypes.h
|
||||
include/pybind11/stl.h
|
||||
include/pybind11/stl_bind.h
|
||||
include/pybind11/stl/filesystem.h)
|
||||
include/pybind11/stl/filesystem.h
|
||||
include/pybind11/type_caster_pyobject_ptr.h)
|
||||
|
||||
# Compare with grep and warn if mismatched
|
||||
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
||||
|
@ -198,6 +208,9 @@ else()
|
|||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake")
|
||||
# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files
|
||||
# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake")
|
||||
|
||||
# Relative directory setting
|
||||
if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS)
|
||||
|
@ -262,6 +275,16 @@ if(PYBIND11_INSTALL)
|
|||
NAMESPACE "pybind11::"
|
||||
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
|
||||
# pkg-config support
|
||||
if(NOT prefix_for_pc_file)
|
||||
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}")
|
||||
endif()
|
||||
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
|
||||
|
||||
# Uninstall target
|
||||
if(PYBIND11_MASTER_PROJECT)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
prune tests
|
||||
recursive-include pybind11/include/pybind11 *.h
|
||||
recursive-include pybind11 *.py
|
||||
recursive-include pybind11 py.typed
|
||||
include pybind11/share/cmake/pybind11/*.cmake
|
||||
include LICENSE README.rst pyproject.toml setup.py setup.cfg
|
||||
include LICENSE README.rst SECURITY.md pyproject.toml setup.py setup.cfg
|
||||
|
|
|
@ -135,7 +135,7 @@ This project was created by `Wenzel
|
|||
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
|
||||
improvements to the code were contributed by Jonas Adler, Lori A. Burns,
|
||||
Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel
|
||||
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko,
|
||||
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov, Johan Mabille, Tomasz Miąsko,
|
||||
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
|
||||
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are applied only to the latest release.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
||||
|
||||
Please disclose it at [security advisory](https://github.com/pybind/pybind11/security/advisories/new).
|
||||
|
||||
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
|
|
@ -38,7 +38,7 @@ type is explicitly allowed.
|
|||
|
||||
.. code-block:: cpp
|
||||
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
template <> struct type_caster<inty> {
|
||||
public:
|
||||
/**
|
||||
|
@ -78,7 +78,7 @@ type is explicitly allowed.
|
|||
return PyLong_FromLong(src.long_value);
|
||||
}
|
||||
};
|
||||
}} // namespace pybind11::detail
|
||||
}} // namespace PYBIND11_NAMESPACE::detail
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ types:
|
|||
.. code-block:: cpp
|
||||
|
||||
// `boost::optional` as an example -- can be any `std::optional`-like container
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
template <typename T>
|
||||
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
|
||||
}}
|
||||
|
@ -54,7 +54,7 @@ for custom variant types:
|
|||
.. code-block:: cpp
|
||||
|
||||
// `boost::variant` as an example -- can be any `std::variant`-like container
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
template <typename... Ts>
|
||||
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
|
||||
|
||||
|
@ -66,7 +66,7 @@ for custom variant types:
|
|||
return boost::apply_visitor(args...);
|
||||
}
|
||||
};
|
||||
}} // namespace pybind11::detail
|
||||
}} // namespace PYBIND11_NAMESPACE::detail
|
||||
|
||||
The ``visit_helper`` specialization is not required if your ``name::variant`` provides
|
||||
a ``name::visit()`` function. For any other function name, the specialization must be
|
||||
|
|
|
@ -101,8 +101,11 @@ conversion has the same overhead as implicit conversion.
|
|||
m.def("str_output",
|
||||
[]() {
|
||||
std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1
|
||||
py::str py_s = PyUnicode_DecodeLatin1(s.data(), s.length());
|
||||
return py_s;
|
||||
py::handle py_s = PyUnicode_DecodeLatin1(s.data(), s.length(), nullptr);
|
||||
if (!py_s) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
return py::reinterpret_steal<py::str>(py_s);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -113,7 +116,8 @@ conversion has the same overhead as implicit conversion.
|
|||
|
||||
The `Python C API
|
||||
<https://docs.python.org/3/c-api/unicode.html#built-in-codecs>`_ provides
|
||||
several built-in codecs.
|
||||
several built-in codecs. Note that these all return *new* references, so
|
||||
use :cpp:func:`reinterpret_steal` when converting them to a :cpp:class:`str`.
|
||||
|
||||
|
||||
One could also use a third party encoding library such as libiconv to transcode
|
||||
|
|
|
@ -1228,7 +1228,7 @@ whether a downcast is safe, you can proceed by specializing the
|
|||
std::string bark() const { return sound; }
|
||||
};
|
||||
|
||||
namespace pybind11 {
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
template<> struct polymorphic_type_hook<Pet> {
|
||||
static const void *get(const Pet *src, const std::type_info*& type) {
|
||||
// note that src may be nullptr
|
||||
|
@ -1239,7 +1239,7 @@ whether a downcast is safe, you can proceed by specializing the
|
|||
return src;
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
|
||||
When pybind11 wants to convert a C++ pointer of type ``Base*`` to a
|
||||
Python object, it calls ``polymorphic_type_hook<Base>::get()`` to
|
||||
|
|
|
@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
|
|||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5...3.26)
|
||||
project(example)
|
||||
|
||||
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
|
||||
|
|
|
@ -177,9 +177,12 @@ section.
|
|||
may be explicitly (re-)thrown to delegate it to the other,
|
||||
previously-declared existing exception translators.
|
||||
|
||||
Note that ``libc++`` and ``libstdc++`` `behave differently <https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
|
||||
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``.
|
||||
See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
|
||||
Note that ``libc++`` and ``libstdc++`` `behave differently under macOS
|
||||
<https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
|
||||
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI
|
||||
boundaries need to be explicitly exported, as exercised in
|
||||
``tests/test_exceptions.h``. See also:
|
||||
"Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
|
||||
|
||||
|
||||
Local vs Global Exception Translators
|
||||
|
|
|
@ -39,15 +39,42 @@ The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds.
|
|||
Global Interpreter Lock (GIL)
|
||||
=============================
|
||||
|
||||
When calling a C++ function from Python, the GIL is always held.
|
||||
The Python C API dictates that the Global Interpreter Lock (GIL) must always
|
||||
be held by the current thread to safely access Python objects. As a result,
|
||||
when Python calls into C++ via pybind11 the GIL must be held, and pybind11
|
||||
will never implicitly release the GIL.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void my_function() {
|
||||
/* GIL is held when this function is called from Python */
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(example, m) {
|
||||
m.def("my_function", &my_function);
|
||||
}
|
||||
|
||||
pybind11 will ensure that the GIL is held when it knows that it is calling
|
||||
Python code. For example, if a Python callback is passed to C++ code via
|
||||
``std::function``, when C++ code calls the function the built-in wrapper
|
||||
will acquire the GIL before calling the Python callback. Similarly, the
|
||||
``PYBIND11_OVERRIDE`` family of macros will acquire the GIL before calling
|
||||
back into Python.
|
||||
|
||||
When writing C++ code that is called from other C++ code, if that code accesses
|
||||
Python state, it must explicitly acquire and release the GIL.
|
||||
|
||||
The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be
|
||||
used to acquire and release the global interpreter lock in the body of a C++
|
||||
function call. In this way, long-running C++ code can be parallelized using
|
||||
multiple Python threads. Taking :ref:`overriding_virtuals` as an example, this
|
||||
multiple Python threads, **but great care must be taken** when any
|
||||
:class:`gil_scoped_release` appear: if there is any way that the C++ code
|
||||
can access Python objects, :class:`gil_scoped_acquire` should be used to
|
||||
reacquire the GIL. Taking :ref:`overriding_virtuals` as an example, this
|
||||
could be realized as follows (important changes highlighted):
|
||||
|
||||
.. code-block:: cpp
|
||||
:emphasize-lines: 8,9,31,32
|
||||
:emphasize-lines: 8,30,31
|
||||
|
||||
class PyAnimal : public Animal {
|
||||
public:
|
||||
|
@ -56,9 +83,7 @@ could be realized as follows (important changes highlighted):
|
|||
|
||||
/* Trampoline (need one for each virtual function) */
|
||||
std::string go(int n_times) {
|
||||
/* Acquire GIL before calling Python code */
|
||||
py::gil_scoped_acquire acquire;
|
||||
|
||||
/* PYBIND11_OVERRIDE_PURE will acquire the GIL before accessing Python state */
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
std::string, /* Return type */
|
||||
Animal, /* Parent class */
|
||||
|
@ -78,7 +103,8 @@ could be realized as follows (important changes highlighted):
|
|||
.def(py::init<>());
|
||||
|
||||
m.def("call_go", [](Animal *animal) -> std::string {
|
||||
/* Release GIL before calling into (potentially long-running) C++ code */
|
||||
// GIL is held when called from Python code. Release GIL before
|
||||
// calling into (potentially long-running) C++ code
|
||||
py::gil_scoped_release release;
|
||||
return call_go(animal);
|
||||
});
|
||||
|
@ -92,6 +118,34 @@ The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
|
|||
m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>());
|
||||
|
||||
|
||||
Common Sources Of Global Interpreter Lock Errors
|
||||
==================================================================
|
||||
|
||||
Failing to properly hold the Global Interpreter Lock (GIL) is one of the
|
||||
more common sources of bugs within code that uses pybind11. If you are
|
||||
running into GIL related errors, we highly recommend you consult the
|
||||
following checklist.
|
||||
|
||||
- Do you have any global variables that are pybind11 objects or invoke
|
||||
pybind11 functions in either their constructor or destructor? You are generally
|
||||
not allowed to invoke any Python function in a global static context. We recommend
|
||||
using lazy initialization and then intentionally leaking at the end of the program.
|
||||
|
||||
- Do you have any pybind11 objects that are members of other C++ structures? One
|
||||
commonly overlooked requirement is that pybind11 objects have to increase their reference count
|
||||
whenever their copy constructor is called. Thus, you need to be holding the GIL to invoke
|
||||
the copy constructor of any C++ class that has a pybind11 member. This can sometimes be very
|
||||
tricky to track for complicated programs Think carefully when you make a pybind11 object
|
||||
a member in another struct.
|
||||
|
||||
- C++ destructors that invoke Python functions can be particularly troublesome as
|
||||
destructors can sometimes get invoked in weird and unexpected circumstances as a result
|
||||
of exceptions.
|
||||
|
||||
- You should try running your code in a debug build. That will enable additional assertions
|
||||
within pybind11 that will throw exceptions on certain GIL handling errors
|
||||
(reference counting operations).
|
||||
|
||||
Binding sequence data types, iterators, the slicing protocol, etc.
|
||||
==================================================================
|
||||
|
||||
|
@ -298,6 +352,15 @@ The class ``options`` allows you to selectively suppress auto-generated signatur
|
|||
m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers");
|
||||
}
|
||||
|
||||
pybind11 also appends all members of an enum to the resulting enum docstring.
|
||||
This default behavior can be disabled by using the ``disable_enum_members_docstring()``
|
||||
function of the ``options`` class.
|
||||
|
||||
With ``disable_user_defined_docstrings()`` all user defined docstrings of
|
||||
``module_::def()``, ``class_::def()`` and ``enum_()`` are disabled, but the
|
||||
function signatures and enum members are included in the docstring, unless they
|
||||
are disabled separately.
|
||||
|
||||
Note that changes to the settings affect only function bindings created during the
|
||||
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
|
||||
the default settings are restored to prevent unwanted side effects.
|
||||
|
|
|
@ -433,7 +433,7 @@ following:
|
|||
{ 2, 4 }, // shape (rows, cols)
|
||||
{ sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
This approach is meant for providing a ``memoryview`` for a C/C++ buffer not
|
||||
managed by Python. The user is responsible for managing the lifetime of the
|
||||
|
@ -449,7 +449,7 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer:
|
|||
buffer, // buffer pointer
|
||||
sizeof(uint8_t) * 8 // buffer size
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
``memoryview::from_memory`` added.
|
||||
|
|
|
@ -157,7 +157,7 @@ specialized:
|
|||
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
|
||||
|
||||
// Only needed if the type's `.get()` goes by another name
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
template <typename T>
|
||||
struct holder_helper<SmartPtr<T>> { // <-- specialization
|
||||
static const T *get(const SmartPtr<T> &p) { return p.getPointer(); }
|
||||
|
|
|
@ -9,6 +9,364 @@ Starting with version 1.8.0, pybind11 releases use a `semantic versioning
|
|||
Changes will be added here periodically from the "Suggested changelog entry"
|
||||
block in pull request descriptions.
|
||||
|
||||
|
||||
Version 2.11.1 (July 17, 2023)
|
||||
-----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* ``PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF`` is now provided as an option
|
||||
for disabling the default-on ``PyGILState_Check()``'s in
|
||||
``pybind11::handle``'s ``inc_ref()`` & ``dec_ref()``.
|
||||
`#4753 <https://github.com/pybind/pybind11/pull/4753>`_
|
||||
|
||||
* ``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF`` was disabled for PyPy in general
|
||||
(not just PyPy Windows).
|
||||
`#4751 <https://github.com/pybind/pybind11/pull/4751>`_
|
||||
|
||||
|
||||
Version 2.11.0 (July 14, 2023)
|
||||
-----------------------------
|
||||
|
||||
New features:
|
||||
|
||||
* The newly added ``pybind11::detail::is_move_constructible`` trait can be
|
||||
specialized for cases in which ``std::is_move_constructible`` does not work
|
||||
as needed. This is very similar to the long-established
|
||||
``pybind11::detail::is_copy_constructible``.
|
||||
`#4631 <https://github.com/pybind/pybind11/pull/4631>`_
|
||||
|
||||
* Introduce ``recursive_container_traits``.
|
||||
`#4623 <https://github.com/pybind/pybind11/pull/4623>`_
|
||||
|
||||
* ``pybind11/type_caster_pyobject_ptr.h`` was added to support automatic
|
||||
wrapping of APIs that make use of ``PyObject *``. This header needs to
|
||||
included explicitly (i.e. it is not included implicitly
|
||||
with ``pybind/pybind11.h``).
|
||||
`#4601 <https://github.com/pybind/pybind11/pull/4601>`_
|
||||
|
||||
* ``format_descriptor<>`` & ``npy_format_descriptor<>`` ``PyObject *``
|
||||
specializations were added. The latter enables ``py::array_t<PyObject *>``
|
||||
to/from-python conversions.
|
||||
`#4674 <https://github.com/pybind/pybind11/pull/4674>`_
|
||||
|
||||
* ``buffer_info`` gained an ``item_type_is_equivalent_to<T>()`` member
|
||||
function.
|
||||
`#4674 <https://github.com/pybind/pybind11/pull/4674>`_
|
||||
|
||||
* The ``capsule`` API gained a user-friendly constructor
|
||||
(``py::capsule(ptr, "name", dtor)``).
|
||||
`#4720 <https://github.com/pybind/pybind11/pull/4720>`_
|
||||
|
||||
Changes:
|
||||
|
||||
* ``PyGILState_Check()``'s in ``pybind11::handle``'s ``inc_ref()`` &
|
||||
``dec_ref()`` are now enabled by default again.
|
||||
`#4246 <https://github.com/pybind/pybind11/pull/4246>`_
|
||||
|
||||
* ``py::initialize_interpreter()`` using ``PyConfig_InitPythonConfig()``
|
||||
instead of ``PyConfig_InitIsolatedConfig()``, to obtain complete
|
||||
``sys.path``.
|
||||
`#4473 <https://github.com/pybind/pybind11/pull/4473>`_
|
||||
|
||||
* Cast errors now always include Python type information, even if
|
||||
``PYBIND11_DETAILED_ERROR_MESSAGES`` is not defined. This increases binary
|
||||
sizes slightly (~1.5%) but the error messages are much more informative.
|
||||
`#4463 <https://github.com/pybind/pybind11/pull/4463>`_
|
||||
|
||||
* The docstring generation for the ``std::array``-list caster was fixed.
|
||||
Previously, signatures included the size of the list in a non-standard,
|
||||
non-spec compliant way. The new format conforms to PEP 593.
|
||||
**Tooling for processing the docstrings may need to be updated accordingly.**
|
||||
`#4679 <https://github.com/pybind/pybind11/pull/4679>`_
|
||||
|
||||
* Setter return values (which are inaccessible for all practical purposes) are
|
||||
no longer converted to Python (only to be discarded).
|
||||
`#4621 <https://github.com/pybind/pybind11/pull/4621>`_
|
||||
|
||||
* Allow lambda specified to function definition to be ``noexcept(true)``
|
||||
in C++17.
|
||||
`#4593 <https://github.com/pybind/pybind11/pull/4593>`_
|
||||
|
||||
* Get rid of recursive template instantiations for concatenating type
|
||||
signatures on C++17 and higher.
|
||||
`#4587 <https://github.com/pybind/pybind11/pull/4587>`_
|
||||
|
||||
* Compatibility with Python 3.12 (beta). Note that the minimum pybind11
|
||||
ABI version for Python 3.12 is version 5. (The default ABI version
|
||||
for Python versions up to and including 3.11 is still version 4.).
|
||||
`#4570 <https://github.com/pybind/pybind11/pull/4570>`_
|
||||
|
||||
* With ``PYBIND11_INTERNALS_VERSION 5`` (default for Python 3.12+), MSVC builds
|
||||
use ``std::hash<std::type_index>`` and ``std::equal_to<std::type_index>``
|
||||
instead of string-based type comparisons. This resolves issues when binding
|
||||
types defined in the unnamed namespace.
|
||||
`#4319 <https://github.com/pybind/pybind11/pull/4319>`_
|
||||
|
||||
* Python exception ``__notes__`` (introduced with Python 3.11) are now added to
|
||||
the ``error_already_set::what()`` output.
|
||||
`#4678 <https://github.com/pybind/pybind11/pull/4678>`_
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* CMake 3.27 support was added, CMake 3.4 support was dropped.
|
||||
FindPython will be used if ``FindPythonInterp`` is not present.
|
||||
`#4719 <https://github.com/pybind/pybind11/pull/4719>`_
|
||||
|
||||
* Update clang-tidy to 15 in CI.
|
||||
`#4387 <https://github.com/pybind/pybind11/pull/4387>`_
|
||||
|
||||
* Moved the linting framework over to Ruff.
|
||||
`#4483 <https://github.com/pybind/pybind11/pull/4483>`_
|
||||
|
||||
* Skip ``lto`` checks and target generation when
|
||||
``CMAKE_INTERPROCEDURAL_OPTIMIZATION`` is defined.
|
||||
`#4643 <https://github.com/pybind/pybind11/pull/4643>`_
|
||||
|
||||
* No longer inject ``-stdlib=libc++``, not needed for modern Pythons
|
||||
(macOS 10.9+).
|
||||
`#4639 <https://github.com/pybind/pybind11/pull/4639>`_
|
||||
|
||||
* PyPy 3.10 support was added, PyPy 3.7 support was dropped.
|
||||
`#4728 <https://github.com/pybind/pybind11/pull/4728>`_
|
||||
|
||||
* Testing with Python 3.12 beta releases was added.
|
||||
`#4713 <https://github.com/pybind/pybind11/pull/4713>`_
|
||||
|
||||
|
||||
Version 2.10.4 (Mar 16, 2023)
|
||||
-----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* ``python3 -m pybind11`` gained a ``--version`` option (prints the version and
|
||||
exits).
|
||||
`#4526 <https://github.com/pybind/pybind11/pull/4526>`_
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
* Fix a warning when pydebug is enabled on Python 3.11.
|
||||
`#4461 <https://github.com/pybind/pybind11/pull/4461>`_
|
||||
|
||||
* Ensure ``gil_scoped_release`` RAII is non-copyable.
|
||||
`#4490 <https://github.com/pybind/pybind11/pull/4490>`_
|
||||
|
||||
* Ensure the tests dir does not show up with new versions of setuptools.
|
||||
`#4510 <https://github.com/pybind/pybind11/pull/4510>`_
|
||||
|
||||
* Better stacklevel for a warning in setuptools helpers.
|
||||
`#4516 <https://github.com/pybind/pybind11/pull/4516>`_
|
||||
|
||||
Version 2.10.3 (Jan 3, 2023)
|
||||
----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* Temporarily made our GIL status assertions (added in 2.10.2) disabled by
|
||||
default (re-enable manually by defining
|
||||
``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, will be enabled in 2.11).
|
||||
`#4432 <https://github.com/pybind/pybind11/pull/4432>`_
|
||||
|
||||
* Improved error messages when ``inc_ref``/``dec_ref`` are called with an
|
||||
invalid GIL state.
|
||||
`#4427 <https://github.com/pybind/pybind11/pull/4427>`_
|
||||
`#4436 <https://github.com/pybind/pybind11/pull/4436>`_
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
* Some minor touchups found by static analyzers.
|
||||
`#4440 <https://github.com/pybind/pybind11/pull/4440>`_
|
||||
|
||||
|
||||
Version 2.10.2 (Dec 20, 2022)
|
||||
-----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* ``scoped_interpreter`` constructor taking ``PyConfig``.
|
||||
`#4330 <https://github.com/pybind/pybind11/pull/4330>`_
|
||||
|
||||
* ``pybind11/eigen/tensor.h`` adds converters to and from ``Eigen::Tensor`` and
|
||||
``Eigen::TensorMap``.
|
||||
`#4201 <https://github.com/pybind/pybind11/pull/4201>`_
|
||||
|
||||
* ``PyGILState_Check()``'s were integrated to ``pybind11::handle``
|
||||
``inc_ref()`` & ``dec_ref()``. The added GIL checks are guarded by
|
||||
``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, which is the default only if
|
||||
``NDEBUG`` is not defined. (Made non-default in 2.10.3, will be active in 2.11)
|
||||
`#4246 <https://github.com/pybind/pybind11/pull/4246>`_
|
||||
|
||||
* Add option for enable/disable enum members in docstring.
|
||||
`#2768 <https://github.com/pybind/pybind11/pull/2768>`_
|
||||
|
||||
* Fixed typing of ``KeysView``, ``ValuesView`` and ``ItemsView`` in ``bind_map``.
|
||||
`#4353 <https://github.com/pybind/pybind11/pull/4353>`_
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Bug fix affecting only Python 3.6 under very specific, uncommon conditions:
|
||||
move ``PyEval_InitThreads()`` call to the correct location.
|
||||
`#4350 <https://github.com/pybind/pybind11/pull/4350>`_
|
||||
|
||||
* Fix segfault bug when passing foreign native functions to functional.h.
|
||||
`#4254 <https://github.com/pybind/pybind11/pull/4254>`_
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* Support setting PYTHON_LIBRARIES manually for Windows ARM cross-compilation
|
||||
(classic mode).
|
||||
`#4406 <https://github.com/pybind/pybind11/pull/4406>`_
|
||||
|
||||
* Extend IPO/LTO detection for ICX (a.k.a IntelLLVM) compiler.
|
||||
`#4402 <https://github.com/pybind/pybind11/pull/4402>`_
|
||||
|
||||
* Allow calling ``find_package(pybind11 CONFIG)`` multiple times from separate
|
||||
directories in the same CMake project and properly link Python (new mode).
|
||||
`#4401 <https://github.com/pybind/pybind11/pull/4401>`_
|
||||
|
||||
* ``multiprocessing_set_spawn`` in pytest fixture for added safety.
|
||||
`#4377 <https://github.com/pybind/pybind11/pull/4377>`_
|
||||
|
||||
* Fixed a bug in two pybind11/tools cmake scripts causing "Unknown arguments specified" errors.
|
||||
`#4327 <https://github.com/pybind/pybind11/pull/4327>`_
|
||||
|
||||
|
||||
|
||||
Version 2.10.1 (Oct 31, 2022)
|
||||
-----------------------------
|
||||
|
||||
This is the first version to fully support embedding the newly released Python 3.11.
|
||||
|
||||
Changes:
|
||||
|
||||
* Allow ``pybind11::capsule`` constructor to take null destructor pointers.
|
||||
`#4221 <https://github.com/pybind/pybind11/pull/4221>`_
|
||||
|
||||
* ``embed.h`` was changed so that ``PYTHONPATH`` is used also with Python 3.11
|
||||
(established behavior).
|
||||
`#4119 <https://github.com/pybind/pybind11/pull/4119>`_
|
||||
|
||||
* A ``PYBIND11_SIMPLE_GIL_MANAGEMENT`` option was added (cmake, C++ define),
|
||||
along with many additional tests in ``test_gil_scoped.py``. The option may be
|
||||
useful to try when debugging GIL-related issues, to determine if the more
|
||||
complex default implementation is or is not to blame. See #4216 for
|
||||
background. WARNING: Please be careful to not create ODR violations when
|
||||
using the option: everything that is linked together with mutual symbol
|
||||
visibility needs to be rebuilt.
|
||||
`#4216 <https://github.com/pybind/pybind11/pull/4216>`_
|
||||
|
||||
* ``PYBIND11_EXPORT_EXCEPTION`` was made non-empty only under macOS. This makes
|
||||
Linux builds safer, and enables the removal of warning suppression pragmas for
|
||||
Windows.
|
||||
`#4298 <https://github.com/pybind/pybind11/pull/4298>`_
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fixed a bug where ``UnicodeDecodeError`` was not propagated from various
|
||||
``py::str`` ctors when decoding surrogate utf characters.
|
||||
`#4294 <https://github.com/pybind/pybind11/pull/4294>`_
|
||||
|
||||
* Revert perfect forwarding for ``make_iterator``. This broke at least one
|
||||
valid use case. May revisit later.
|
||||
`#4234 <https://github.com/pybind/pybind11/pull/4234>`_
|
||||
|
||||
* Fix support for safe casts to ``void*`` (regression in 2.10.0).
|
||||
`#4275 <https://github.com/pybind/pybind11/pull/4275>`_
|
||||
|
||||
* Fix ``char8_t`` support (regression in 2.9).
|
||||
`#4278 <https://github.com/pybind/pybind11/pull/4278>`_
|
||||
|
||||
* Unicode surrogate character in Python exception message leads to process
|
||||
termination in ``error_already_set::what()``.
|
||||
`#4297 <https://github.com/pybind/pybind11/pull/4297>`_
|
||||
|
||||
* Fix MSVC 2019 v.1924 & C++14 mode error for ``overload_cast``.
|
||||
`#4188 <https://github.com/pybind/pybind11/pull/4188>`_
|
||||
|
||||
* Make augmented assignment operators non-const for the object-api. Behavior
|
||||
was previously broken for augmented assignment operators.
|
||||
`#4065 <https://github.com/pybind/pybind11/pull/4065>`_
|
||||
|
||||
* Add proper error checking to C++ bindings for Python list append and insert.
|
||||
`#4208 <https://github.com/pybind/pybind11/pull/4208>`_
|
||||
|
||||
* Work-around for Nvidia's CUDA nvcc compiler in versions 11.4.0 - 11.8.0.
|
||||
`#4220 <https://github.com/pybind/pybind11/pull/4220>`_
|
||||
|
||||
* A workaround for PyPy was added in the ``py::error_already_set``
|
||||
implementation, related to PR `#1895 <https://github.com/pybind/pybind11/pull/1895>`_
|
||||
released with v2.10.0.
|
||||
`#4079 <https://github.com/pybind/pybind11/pull/4079>`_
|
||||
|
||||
* Fixed compiler errors when C++23 ``std::forward_like`` is available.
|
||||
`#4136 <https://github.com/pybind/pybind11/pull/4136>`_
|
||||
|
||||
* Properly raise exceptions in contains methods (like when an object in unhashable).
|
||||
`#4209 <https://github.com/pybind/pybind11/pull/4209>`_
|
||||
|
||||
* Further improve another error in exception handling.
|
||||
`#4232 <https://github.com/pybind/pybind11/pull/4232>`_
|
||||
|
||||
* ``get_local_internals()`` was made compatible with
|
||||
``finalize_interpreter()``, fixing potential freezes during interpreter
|
||||
finalization.
|
||||
`#4192 <https://github.com/pybind/pybind11/pull/4192>`_
|
||||
|
||||
Performance and style:
|
||||
|
||||
* Reserve space in set and STL map casters if possible. This will prevent
|
||||
unnecessary rehashing / resizing by knowing the number of keys ahead of time
|
||||
for Python to C++ casting. This improvement will greatly speed up the casting
|
||||
of large unordered maps and sets.
|
||||
`#4194 <https://github.com/pybind/pybind11/pull/4194>`_
|
||||
|
||||
* GIL RAII scopes are non-copyable to avoid potential bugs.
|
||||
`#4183 <https://github.com/pybind/pybind11/pull/4183>`_
|
||||
|
||||
* Explicitly default all relevant ctors for pytypes in the ``PYBIND11_OBJECT``
|
||||
macros and enforce the clang-tidy checks ``modernize-use-equals-default`` in
|
||||
macros as well.
|
||||
`#4017 <https://github.com/pybind/pybind11/pull/4017>`_
|
||||
|
||||
* Optimize iterator advancement in C++ bindings.
|
||||
`#4237 <https://github.com/pybind/pybind11/pull/4237>`_
|
||||
|
||||
* Use the modern ``PyObject_GenericGetDict`` and ``PyObject_GenericSetDict``
|
||||
for handling dynamic attribute dictionaries.
|
||||
`#4106 <https://github.com/pybind/pybind11/pull/4106>`_
|
||||
|
||||
* Document that users should use ``PYBIND11_NAMESPACE`` instead of using ``pybind11`` when
|
||||
opening namespaces. Using namespace declarations and namespace qualification
|
||||
remain the same as ``pybind11``. This is done to ensure consistent symbol
|
||||
visibility.
|
||||
`#4098 <https://github.com/pybind/pybind11/pull/4098>`_
|
||||
|
||||
* Mark ``detail::forward_like`` as constexpr.
|
||||
`#4147 <https://github.com/pybind/pybind11/pull/4147>`_
|
||||
|
||||
* Optimize unpacking_collector when processing ``arg_v`` arguments.
|
||||
`#4219 <https://github.com/pybind/pybind11/pull/4219>`_
|
||||
|
||||
* Optimize casting C++ object to ``None``.
|
||||
`#4269 <https://github.com/pybind/pybind11/pull/4269>`_
|
||||
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* CMake: revert overwrite behavior, now opt-in with ``PYBIND11_PYTHONLIBS_OVERRWRITE OFF``.
|
||||
`#4195 <https://github.com/pybind/pybind11/pull/4195>`_
|
||||
|
||||
* Include a pkg-config file when installing pybind11, such as in the Python
|
||||
package.
|
||||
`#4077 <https://github.com/pybind/pybind11/pull/4077>`_
|
||||
|
||||
* Avoid stripping debug symbols when ``CMAKE_BUILD_TYPE`` is set to ``DEBUG``
|
||||
instead of ``Debug``.
|
||||
`#4078 <https://github.com/pybind/pybind11/pull/4078>`_
|
||||
|
||||
* Followup to `#3948 <https://github.com/pybind/pybind11/pull/3948>`_, fixing vcpkg again.
|
||||
`#4123 <https://github.com/pybind/pybind11/pull/4123>`_
|
||||
|
||||
Version 2.10.0 (Jul 15, 2022)
|
||||
-----------------------------
|
||||
|
||||
|
|
|
@ -58,6 +58,16 @@ interactive Python session demonstrating this example is shown below:
|
|||
Static member functions can be bound in the same way using
|
||||
:func:`class_::def_static`.
|
||||
|
||||
.. note::
|
||||
|
||||
Binding C++ types in unnamed namespaces (also known as anonymous namespaces)
|
||||
works reliably on many platforms, but not all. The `XFAIL_CONDITION` in
|
||||
tests/test_unnamed_namespace_a.py encodes the currently known conditions.
|
||||
For background see `#4319 <https://github.com/pybind/pybind11/pull/4319>`_.
|
||||
If portability is a concern, it is therefore not recommended to bind C++
|
||||
types in unnamed namespaces. It will be safest to manually pick unique
|
||||
namespace names.
|
||||
|
||||
Keyword and default arguments
|
||||
=============================
|
||||
It is possible to specify keyword and default arguments using the syntax
|
||||
|
@ -539,3 +549,7 @@ The ``name`` property returns the name of the enum value as a unicode string.
|
|||
...
|
||||
|
||||
By default, these are omitted to conserve space.
|
||||
|
||||
.. warning::
|
||||
|
||||
Contrary to Python customs, enum values from the wrappers should not be compared using ``is``, but with ``==`` (see `#1177 <https://github.com/pybind/pybind11/issues/1177>`_ for background).
|
||||
|
|
|
@ -241,7 +241,7 @@ extension module can be created with just a few lines of code:
|
|||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.4...3.18)
|
||||
cmake_minimum_required(VERSION 3.5...3.26)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
add_subdirectory(pybind11)
|
||||
|
@ -261,6 +261,9 @@ PyPI integration, can be found in the [cmake_example]_ repository.
|
|||
.. versionchanged:: 2.6
|
||||
CMake 3.4+ is required.
|
||||
|
||||
.. versionchanged:: 2.11
|
||||
CMake 3.5+ is required.
|
||||
|
||||
Further information can be found at :doc:`cmake/index`.
|
||||
|
||||
pybind11_add_module
|
||||
|
@ -495,7 +498,7 @@ You can use these targets to build complex applications. For example, the
|
|||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5...3.26)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||
|
@ -553,7 +556,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
|
|||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.4...3.18)
|
||||
cmake_minimum_required(VERSION 3.5...3.26)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||
|
|
|
@ -353,12 +353,11 @@ def prepare(app):
|
|||
f.write(contents)
|
||||
|
||||
|
||||
def clean_up(app, exception):
|
||||
def clean_up(app, exception): # noqa: ARG001
|
||||
(DIR / "readme.rst").unlink()
|
||||
|
||||
|
||||
def setup(app):
|
||||
|
||||
# Add hook for building doxygen xml when needed
|
||||
app.connect("builder-inited", generate_doxygen_xml)
|
||||
|
||||
|
|
|
@ -284,7 +284,8 @@ There are three possible solutions:
|
|||
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better,
|
||||
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead
|
||||
of the old, deprecated search tools, and these modules are much better at
|
||||
finding the correct Python.
|
||||
finding the correct Python. If FindPythonLibs/Interp are not available
|
||||
(CMake 3.27+), then this will be ignored and FindPython will be used.
|
||||
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
|
||||
However, you will have to use the target-based system, and do more setup
|
||||
yourself, because it does not know about or include things that depend on
|
||||
|
|
|
@ -33,10 +33,12 @@ If you don't have nox, you should either use ``pipx run nox`` instead, or use
|
|||
- 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``.
|
||||
- Check to make sure
|
||||
`needs-changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_
|
||||
issues are entered in the changelog (clear the label when done).
|
||||
- Add release date in ``docs/changelog.rst`` and integrate the output of
|
||||
``nox -s make_changelog``.
|
||||
- Note that the ``make_changelog`` command inspects
|
||||
`needs changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_.
|
||||
- Manually clear the ``needs changelog`` labels using the GitHub web
|
||||
interface (very easy: start by clicking the link above).
|
||||
- ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
|
||||
fails due to a known flake issue, either ignore or restart CI.)
|
||||
- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version
|
||||
|
|
|
@ -8,6 +8,20 @@ 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.11:
|
||||
|
||||
v2.11
|
||||
=====
|
||||
|
||||
* The minimum version of CMake is now 3.5. A future version will likely move to
|
||||
requiring something like CMake 3.15. Note that CMake 3.27 is removing the
|
||||
long-deprecated support for ``FindPythonInterp`` if you set 3.27 as the
|
||||
minimum or maximum supported version. To prepare for that future, CMake 3.15+
|
||||
using ``FindPython`` or setting ``PYBIND11_FINDPYTHON`` is highly recommended,
|
||||
otherwise pybind11 will automatically switch to using ``FindPython`` if
|
||||
``FindPythonInterp`` is not available.
|
||||
|
||||
|
||||
.. _upgrade-guide-2.9:
|
||||
|
||||
v2.9
|
||||
|
|
|
@ -26,6 +26,9 @@ struct is_method {
|
|||
explicit is_method(const handle &c) : class_(c) {}
|
||||
};
|
||||
|
||||
/// Annotation for setters
|
||||
struct is_setter {};
|
||||
|
||||
/// Annotation for operators
|
||||
struct is_operator {};
|
||||
|
||||
|
@ -188,8 +191,8 @@ struct argument_record {
|
|||
struct function_record {
|
||||
function_record()
|
||||
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
||||
is_operator(false), is_method(false), has_args(false), has_kwargs(false),
|
||||
prepend(false) {}
|
||||
is_operator(false), is_method(false), is_setter(false), has_args(false),
|
||||
has_kwargs(false), prepend(false) {}
|
||||
|
||||
/// Function name
|
||||
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
||||
|
@ -230,6 +233,9 @@ struct function_record {
|
|||
/// True if this is a method
|
||||
bool is_method : 1;
|
||||
|
||||
/// True if this is a setter
|
||||
bool is_setter : 1;
|
||||
|
||||
/// True if the function has a '*args' argument
|
||||
bool has_args : 1;
|
||||
|
||||
|
@ -399,7 +405,7 @@ struct process_attribute<doc> : process_attribute_default<doc> {
|
|||
template <>
|
||||
struct process_attribute<const char *> : process_attribute_default<const char *> {
|
||||
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
|
||||
static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
|
||||
static void init(const char *d, type_record *r) { r->doc = d; }
|
||||
};
|
||||
template <>
|
||||
struct process_attribute<char *> : process_attribute<const char *> {};
|
||||
|
@ -426,6 +432,12 @@ struct process_attribute<is_method> : process_attribute_default<is_method> {
|
|||
}
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this function is a setter
|
||||
template <>
|
||||
struct process_attribute<is_setter> : process_attribute_default<is_setter> {
|
||||
static void init(const is_setter &, function_record *r) { r->is_setter = true; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates the parent scope of a method
|
||||
template <>
|
||||
struct process_attribute<scope> : process_attribute_default<scope> {
|
||||
|
|
|
@ -37,6 +37,9 @@ inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t
|
|||
return strides;
|
||||
}
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct compare_buffer_info;
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Information record describing a Python buffer object
|
||||
|
@ -150,6 +153,17 @@ struct buffer_info {
|
|||
Py_buffer *view() const { return m_view; }
|
||||
Py_buffer *&view() { return m_view; }
|
||||
|
||||
/* True if the buffer item type is equivalent to `T`. */
|
||||
// To define "equivalent" by example:
|
||||
// `buffer_info::item_type_is_equivalent_to<int>(b)` and
|
||||
// `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
|
||||
// on some platforms, but `int` and `unsigned` will never be equivalent.
|
||||
// For the ground truth, please inspect `detail::compare_buffer_info<>`.
|
||||
template <typename T>
|
||||
bool item_type_is_equivalent_to() const {
|
||||
return detail::compare_buffer_info<T>::compare(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
struct private_ctr_tag {};
|
||||
|
||||
|
@ -170,9 +184,10 @@ private:
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
template <typename T, typename SFINAE>
|
||||
struct compare_buffer_info {
|
||||
static bool compare(const buffer_info &b) {
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
|
||||
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include <vector>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename type, typename SFINAE = void>
|
||||
|
@ -88,7 +91,8 @@ public:
|
|||
template <typename T_, \
|
||||
::pybind11::detail::enable_if_t< \
|
||||
std::is_same<type, ::pybind11::detail::remove_cv_t<T_>>::value, \
|
||||
int> = 0> \
|
||||
int> \
|
||||
= 0> \
|
||||
static ::pybind11::handle cast( \
|
||||
T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \
|
||||
if (!src) \
|
||||
|
@ -248,7 +252,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
static handle cast(T, return_value_policy /* policy */, handle /* parent */) {
|
||||
return none().inc_ref();
|
||||
return none().release();
|
||||
}
|
||||
PYBIND11_TYPE_CASTER(T, const_name("None"));
|
||||
};
|
||||
|
@ -291,7 +295,7 @@ public:
|
|||
if (ptr) {
|
||||
return capsule(ptr).release();
|
||||
}
|
||||
return none().inc_ref();
|
||||
return none().release();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -389,7 +393,7 @@ struct string_caster {
|
|||
|
||||
// For UTF-8 we avoid the need for a temporary `bytes` object by using
|
||||
// `PyUnicode_AsUTF8AndSize`.
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) {
|
||||
if (UTF_N == 8) {
|
||||
Py_ssize_t size = -1;
|
||||
const auto *buffer
|
||||
= reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size));
|
||||
|
@ -416,7 +420,7 @@ struct string_caster {
|
|||
= reinterpret_cast<const CharT *>(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr()));
|
||||
size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);
|
||||
// Skip BOM for UTF-16/32
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) {
|
||||
if (UTF_N > 8) {
|
||||
buffer++;
|
||||
length--;
|
||||
}
|
||||
|
@ -537,7 +541,7 @@ public:
|
|||
|
||||
static handle cast(const CharT *src, return_value_policy policy, handle parent) {
|
||||
if (src == nullptr) {
|
||||
return pybind11::none().inc_ref();
|
||||
return pybind11::none().release();
|
||||
}
|
||||
return StringCaster::cast(StringType(src), policy, parent);
|
||||
}
|
||||
|
@ -572,7 +576,7 @@ public:
|
|||
// figure out how long the first encoded character is in bytes to distinguish between these
|
||||
// two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as
|
||||
// those can fit into a single char value.
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 8) && str_len > 1 && str_len <= 4) {
|
||||
if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) {
|
||||
auto v0 = static_cast<unsigned char>(value[0]);
|
||||
// low bits only: 0-127
|
||||
// 0b110xxxxx - start of 2-byte sequence
|
||||
|
@ -598,7 +602,7 @@ public:
|
|||
// UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a
|
||||
// surrogate pair with total length 2 instantly indicates a range error (but not a "your
|
||||
// string was too long" error).
|
||||
else if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 16) && str_len == 2) {
|
||||
else if (StringCaster::UTF_N == 16 && str_len == 2) {
|
||||
one_char = static_cast<CharT>(value[0]);
|
||||
if (one_char >= 0xD800 && one_char < 0xE000) {
|
||||
throw value_error("Character code point not in range(0x10000)");
|
||||
|
@ -960,7 +964,7 @@ struct move_always<
|
|||
enable_if_t<
|
||||
all_of<move_is_plain_type<T>,
|
||||
negation<is_copy_constructible<T>>,
|
||||
std::is_move_constructible<T>,
|
||||
is_move_constructible<T>,
|
||||
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
|
||||
: std::true_type {};
|
||||
template <typename T, typename SFINAE = void>
|
||||
|
@ -971,7 +975,7 @@ struct move_if_unreferenced<
|
|||
enable_if_t<
|
||||
all_of<move_is_plain_type<T>,
|
||||
negation<move_always<T>>,
|
||||
std::is_move_constructible<T>,
|
||||
is_move_constructible<T>,
|
||||
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
|
||||
: std::true_type {};
|
||||
template <typename T>
|
||||
|
@ -1013,11 +1017,14 @@ type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &ha
|
|||
"Internal error: type_caster should only be used for C++ types");
|
||||
if (!conv.load(handle, true)) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error("Unable to cast Python instance to C++ type (#define "
|
||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
throw cast_error(
|
||||
"Unable to cast Python instance of type "
|
||||
+ str(type::handle_of(handle)).cast<std::string>()
|
||||
+ " to C++ type '?' (#define "
|
||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to cast Python instance of type "
|
||||
+ (std::string) str(type::handle_of(handle)) + " to C++ type '"
|
||||
+ str(type::handle_of(handle)).cast<std::string>() + " to C++ type '"
|
||||
+ type_id<T>() + "'");
|
||||
#endif
|
||||
}
|
||||
|
@ -1034,7 +1041,11 @@ make_caster<T> load_type(const handle &handle) {
|
|||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
// pytype -> C++ type
|
||||
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
||||
template <typename T,
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value
|
||||
&& !detail::is_same_ignoring_cvref<T, PyObject *>::value,
|
||||
int>
|
||||
= 0>
|
||||
T cast(const handle &handle) {
|
||||
using namespace detail;
|
||||
static_assert(!cast_is_temporary_value_reference<T>::value,
|
||||
|
@ -1048,6 +1059,34 @@ T cast(const handle &handle) {
|
|||
return T(reinterpret_borrow<object>(handle));
|
||||
}
|
||||
|
||||
// Note that `cast<PyObject *>(obj)` increments the reference count of `obj`.
|
||||
// This is necessary for the case that `obj` is a temporary, and could
|
||||
// not possibly be different, given
|
||||
// 1. the established convention that the passed `handle` is borrowed, and
|
||||
// 2. we don't want to force all generic code using `cast<T>()` to special-case
|
||||
// handling of `T` = `PyObject *` (to increment the reference count there).
|
||||
// It is the responsibility of the caller to ensure that the reference count
|
||||
// is decremented.
|
||||
template <typename T,
|
||||
typename Handle,
|
||||
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
|
||||
&& detail::is_same_ignoring_cvref<Handle, handle>::value,
|
||||
int>
|
||||
= 0>
|
||||
T cast(Handle &&handle) {
|
||||
return handle.inc_ref().ptr();
|
||||
}
|
||||
// To optimize way an inc_ref/dec_ref cycle:
|
||||
template <typename T,
|
||||
typename Object,
|
||||
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
|
||||
&& detail::is_same_ignoring_cvref<Object, object>::value,
|
||||
int>
|
||||
= 0>
|
||||
T cast(Object &&obj) {
|
||||
return obj.release().ptr();
|
||||
}
|
||||
|
||||
// C++ type -> py::object
|
||||
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
|
||||
object cast(T &&value,
|
||||
|
@ -1081,12 +1120,13 @@ detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
|
|||
if (obj.ref_count() > 1) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error(
|
||||
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
|
||||
" (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
"Unable to cast Python " + str(type::handle_of(obj)).cast<std::string>()
|
||||
+ " instance to C++ rvalue: instance has multiple references"
|
||||
" (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to move from Python " + (std::string) str(type::handle_of(obj))
|
||||
+ " instance to C++ " + type_id<T>()
|
||||
+ " instance: instance has multiple references");
|
||||
throw cast_error("Unable to move from Python "
|
||||
+ str(type::handle_of(obj)).cast<std::string>() + " instance to C++ "
|
||||
+ type_id<T>() + " instance: instance has multiple references");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1179,11 +1219,9 @@ enable_if_t<cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&)
|
|||
pybind11_fail("Internal error: cast_safe fallback invoked");
|
||||
}
|
||||
template <typename T>
|
||||
enable_if_t<std::is_same<void, intrinsic_t<T>>::value, void> cast_safe(object &&) {}
|
||||
enable_if_t<std::is_void<T>::value, void> cast_safe(object &&) {}
|
||||
template <typename T>
|
||||
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>,
|
||||
std::is_same<void, intrinsic_t<T>>>::value,
|
||||
T>
|
||||
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>, std::is_void<T>>::value, T>
|
||||
cast_safe(object &&o) {
|
||||
return pybind11::cast<T>(std::move(o));
|
||||
}
|
||||
|
@ -1193,9 +1231,10 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
// The overloads could coexist, i.e. the #if is not strictly speaking needed,
|
||||
// but it is an easy minor optimization.
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
inline cast_error cast_error_unable_to_convert_call_arg() {
|
||||
return cast_error("Unable to convert call argument to Python object (#define "
|
||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name) {
|
||||
return cast_error("Unable to convert call argument '" + name
|
||||
+ "' to Python object (#define "
|
||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
}
|
||||
#else
|
||||
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
|
||||
|
@ -1218,7 +1257,7 @@ tuple make_tuple(Args &&...args_) {
|
|||
for (size_t i = 0; i < args.size(); i++) {
|
||||
if (!args[i]) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
throw cast_error_unable_to_convert_call_arg(std::to_string(i));
|
||||
#else
|
||||
std::array<std::string, size> argtypes{{type_id<Args>()...}};
|
||||
throw cast_error_unable_to_convert_call_arg(std::to_string(i), argtypes[i]);
|
||||
|
@ -1508,7 +1547,7 @@ private:
|
|||
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
|
||||
if (!o) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()));
|
||||
#else
|
||||
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
|
||||
type_id<T>());
|
||||
|
@ -1540,12 +1579,12 @@ private:
|
|||
}
|
||||
if (!a.value) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
throw cast_error_unable_to_convert_call_arg(a.name);
|
||||
#else
|
||||
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
|
||||
#endif
|
||||
}
|
||||
m_kwargs[a.name] = a.value;
|
||||
m_kwargs[a.name] = std::move(a.value);
|
||||
}
|
||||
|
||||
void process(list & /*args_list*/, detail::kwargs_proxy kp) {
|
||||
|
|
|
@ -55,6 +55,9 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec
|
|||
return PyProperty_Type.tp_descr_set(self, cls, value);
|
||||
}
|
||||
|
||||
// Forward declaration to use in `make_static_property_type()`
|
||||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type);
|
||||
|
||||
/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()`
|
||||
methods are modified to always use the object type instead of a concrete instance.
|
||||
Return value: New reference. */
|
||||
|
@ -87,6 +90,13 @@ inline PyTypeObject *make_static_property_type() {
|
|||
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
|
||||
}
|
||||
|
||||
# if PY_VERSION_HEX >= 0x030C0000
|
||||
// PRE 3.12 FEATURE FREEZE. PLEASE REVIEW AFTER FREEZE.
|
||||
// Since Python-3.12 property-derived types are required to
|
||||
// have dynamic attributes (to set `__doc__`)
|
||||
enable_dynamic_attributes(heap_type);
|
||||
# endif
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||
|
||||
|
@ -435,9 +445,17 @@ inline void clear_instance(PyObject *self) {
|
|||
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
|
||||
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
|
||||
extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
||||
auto *type = Py_TYPE(self);
|
||||
|
||||
// If this is a GC tracked object, untrack it first
|
||||
// Note that the track call is implicitly done by the
|
||||
// default tp_alloc, which we never override.
|
||||
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) {
|
||||
PyObject_GC_UnTrack(self);
|
||||
}
|
||||
|
||||
clear_instance(self);
|
||||
|
||||
auto *type = Py_TYPE(self);
|
||||
type->tp_free(self);
|
||||
|
||||
#if PY_VERSION_HEX < 0x03080000
|
||||
|
@ -502,31 +520,6 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
|||
return (PyObject *) heap_type;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Support for `d = instance.__dict__`.
|
||||
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
if (!dict) {
|
||||
dict = PyDict_New();
|
||||
}
|
||||
Py_XINCREF(dict);
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
|
||||
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
|
||||
if (!PyDict_Check(new_dict)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"__dict__ must be set to a dictionary, not a '%.200s'",
|
||||
get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
|
||||
return -1;
|
||||
}
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_INCREF(new_dict);
|
||||
Py_CLEAR(dict);
|
||||
dict = new_dict;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
|
||||
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
|
@ -558,9 +551,17 @@ inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
|||
type->tp_traverse = pybind11_traverse;
|
||||
type->tp_clear = pybind11_clear;
|
||||
|
||||
static PyGetSetDef getset[] = {
|
||||
{const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}};
|
||||
static PyGetSetDef getset[] = {{
|
||||
#if PY_VERSION_HEX < 0x03070000
|
||||
const_cast<char *>("__dict__"),
|
||||
#else
|
||||
"__dict__",
|
||||
#endif
|
||||
PyObject_GenericGetDict,
|
||||
PyObject_GenericSetDict,
|
||||
nullptr,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}};
|
||||
type->tp_getset = getset;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,15 +10,76 @@
|
|||
#pragma once
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 10
|
||||
#define PYBIND11_VERSION_PATCH 0
|
||||
#define PYBIND11_VERSION_MINOR 11
|
||||
#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 0x020A0000
|
||||
#define PYBIND11_VERSION_HEX 0x020B0100
|
||||
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
|
||||
#define PYBIND11_NAMESPACE_END(name) }
|
||||
// Define some generic pybind11 helper macros for warning management.
|
||||
//
|
||||
// Note that compiler-specific push/pop pairs are baked into the
|
||||
// PYBIND11_NAMESPACE_BEGIN/PYBIND11_NAMESPACE_END pair of macros. Therefore manual
|
||||
// PYBIND11_WARNING_PUSH/PYBIND11_WARNING_POP are usually only needed in `#include` sections.
|
||||
//
|
||||
// If you find you need to suppress a warning, please try to make the suppression as local as
|
||||
// possible using these macros. Please also be sure to push/pop with the pybind11 macros. Please
|
||||
// only use compiler specifics if you need to check specific versions, e.g. Apple Clang vs. vanilla
|
||||
// Clang.
|
||||
#if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_MSVC
|
||||
# define PYBIND11_PRAGMA(...) __pragma(__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(warning(push))
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(warning(pop))
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_INTEL
|
||||
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(warning push)
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(warning pop)
|
||||
#elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_CLANG
|
||||
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(clang diagnostic push)
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(clang diagnostic push)
|
||||
#elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_GCC
|
||||
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(GCC diagnostic push)
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(GCC diagnostic pop)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_MSVC
|
||||
# define PYBIND11_WARNING_DISABLE_MSVC(name) PYBIND11_PRAGMA(warning(disable : name))
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_MSVC(name)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_CLANG
|
||||
# define PYBIND11_WARNING_DISABLE_CLANG(name) PYBIND11_PRAGMA(clang diagnostic ignored name)
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_CLANG(name)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_GCC
|
||||
# define PYBIND11_WARNING_DISABLE_GCC(name) PYBIND11_PRAGMA(GCC diagnostic ignored name)
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_GCC(name)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_INTEL
|
||||
# define PYBIND11_WARNING_DISABLE_INTEL(name) PYBIND11_PRAGMA(warning disable name)
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_INTEL(name)
|
||||
#endif
|
||||
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) \
|
||||
namespace name { \
|
||||
PYBIND11_WARNING_PUSH
|
||||
|
||||
#define PYBIND11_NAMESPACE_END(name) \
|
||||
PYBIND11_WARNING_POP \
|
||||
}
|
||||
|
||||
// Robust support for some features and loading modules compiled against different pybind versions
|
||||
// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute
|
||||
|
@ -96,13 +157,10 @@
|
|||
#endif
|
||||
|
||||
#if !defined(PYBIND11_EXPORT_EXCEPTION)
|
||||
# ifdef __MINGW32__
|
||||
// workaround for:
|
||||
// error: 'dllexport' implies default visibility, but xxx has already been declared with a
|
||||
// different visibility
|
||||
# define PYBIND11_EXPORT_EXCEPTION
|
||||
# else
|
||||
# if defined(__apple_build_version__)
|
||||
# define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT
|
||||
# else
|
||||
# define PYBIND11_EXPORT_EXCEPTION
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
@ -154,9 +212,9 @@
|
|||
|
||||
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4505)
|
||||
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
|
||||
# pragma warning(disable : 4505)
|
||||
# if defined(_DEBUG) && !defined(Py_DEBUG)
|
||||
// Workaround for a VS 2022 issue.
|
||||
// NOTE: This workaround knowingly violates the Python.h include order requirement:
|
||||
|
@ -205,11 +263,8 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
|
||||
# define PYBIND11_HAS_U8STRING
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
// Reminder: WITH_THREAD is always defined if PY_VERSION_HEX >= 0x03070000
|
||||
#if PY_VERSION_HEX < 0x03060000
|
||||
# error "PYTHON < 3.6 IS UNSUPPORTED. pybind11 v2.9 was the last to support Python 2 and 3.5."
|
||||
#endif
|
||||
|
@ -233,12 +288,16 @@
|
|||
# undef copysign
|
||||
#endif
|
||||
|
||||
#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# define PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# if defined(PYBIND11_DEBUG_MARKER)
|
||||
# define _DEBUG
|
||||
# undef PYBIND11_DEBUG_MARKER
|
||||
# endif
|
||||
# pragma warning(pop)
|
||||
PYBIND11_WARNING_POP
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
@ -259,6 +318,17 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
// Must be after including <version> or one of the other headers specified by the standard
|
||||
#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
|
||||
# define PYBIND11_HAS_U8STRING
|
||||
#endif
|
||||
|
||||
// See description of PR #4246:
|
||||
#if !defined(PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF) && !defined(NDEBUG) \
|
||||
&& !defined(PYPY_VERSION) && !defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
|
||||
# define PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||
#endif
|
||||
|
||||
// #define PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
|
||||
// (probably surprising and never documented, but this was the
|
||||
|
@ -363,7 +433,7 @@
|
|||
|
||||
/** \rst
|
||||
This macro creates the entry point that will be invoked when the Python interpreter
|
||||
imports an extension module. The module name is given as the fist argument and it
|
||||
imports an extension module. The module name is given as the first argument and it
|
||||
should not be in quotes. The second macro argument defines a variable of type
|
||||
`py::module_` which can be used to initialize the module.
|
||||
|
||||
|
@ -588,6 +658,10 @@ template <class T>
|
|||
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
#endif
|
||||
|
||||
/// Example usage: is_same_ignoring_cvref<T, PyObject *>::value
|
||||
template <typename T, typename U>
|
||||
using is_same_ignoring_cvref = std::is_same<detail::remove_cvref_t<T>, U>;
|
||||
|
||||
/// Index sequences
|
||||
#if defined(PYBIND11_CPP14)
|
||||
using std::index_sequence;
|
||||
|
@ -681,7 +755,16 @@ template <typename C, typename R, typename... A>
|
|||
struct remove_class<R (C::*)(A...) const> {
|
||||
using type = R(A...);
|
||||
};
|
||||
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
template <typename C, typename R, typename... A>
|
||||
struct remove_class<R (C::*)(A...) noexcept> {
|
||||
using type = R(A...);
|
||||
};
|
||||
template <typename C, typename R, typename... A>
|
||||
struct remove_class<R (C::*)(A...) const noexcept> {
|
||||
using type = R(A...);
|
||||
};
|
||||
#endif
|
||||
/// Helper template to strip away type modifiers
|
||||
template <typename T>
|
||||
struct intrinsic_type {
|
||||
|
@ -898,12 +981,6 @@ using expand_side_effects = bool[];
|
|||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4275)
|
||||
// warning C4275: An exported class was derived from a class that wasn't exported.
|
||||
// Can be ignored when derived from a STL class.
|
||||
#endif
|
||||
/// C++ bindings of builtin Python exceptions
|
||||
class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error {
|
||||
public:
|
||||
|
@ -911,9 +988,6 @@ public:
|
|||
/// Set the error using the Python C API
|
||||
virtual void set_error() const = 0;
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \
|
||||
class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { \
|
||||
|
@ -948,6 +1022,15 @@ PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used in
|
|||
template <typename T, typename SFINAE = void>
|
||||
struct format_descriptor {};
|
||||
|
||||
template <typename T>
|
||||
struct format_descriptor<
|
||||
T,
|
||||
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value>> {
|
||||
static constexpr const char c = 'O';
|
||||
static constexpr const char value[2] = {c, '\0'};
|
||||
static std::string format() { return std::string(1, c); }
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
// Returns the index of the given type in the type char array below, and in the list in numpy.h
|
||||
// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double;
|
||||
|
@ -1033,12 +1116,7 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
|
||||
template <typename... Args>
|
||||
# if (defined(_MSC_VER) && _MSC_VER < 1920) /* MSVC 2017 */ \
|
||||
|| (defined(__clang__) && __clang_major__ == 5)
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
|
||||
# else
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast;
|
||||
# endif
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast{};
|
||||
#endif
|
||||
|
||||
/// Const member function selector for overload_cast
|
||||
|
@ -1147,20 +1225,28 @@ constexpr
|
|||
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) // All versions (as of July 2021).
|
||||
|
||||
// warning C4127: Conditional expression is constant
|
||||
constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
|
||||
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) ::pybind11::detail::silence_msvc_c4127(__VA_ARGS__)
|
||||
|
||||
#else
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__
|
||||
#if defined(__clang__) \
|
||||
&& (defined(__apple_build_version__) /* AppleClang 13.0.0.13000029 was the only data point \
|
||||
available. */ \
|
||||
|| (__clang_major__ >= 7 \
|
||||
&& __clang_major__ <= 12) /* Clang 3, 5, 13, 14, 15 do not generate the warning. */ \
|
||||
)
|
||||
# define PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
|
||||
// Example:
|
||||
// tests/test_kwargs_and_defaults.cpp:46:68: error: local variable 'args' will be copied despite
|
||||
// being returned by name [-Werror,-Wreturn-std-move]
|
||||
// m.def("args_function", [](py::args args) -> py::tuple { return args; });
|
||||
// ^~~~
|
||||
// test_kwargs_and_defaults.cpp:46:68: note: call 'std::move' explicitly to avoid copying
|
||||
// m.def("args_function", [](py::args args) -> py::tuple { return args; });
|
||||
// ^~~~
|
||||
// std::move(args)
|
||||
#endif
|
||||
|
||||
// Pybind offers detailed error messages by default for all builts that are debug (through the
|
||||
// negation of ndebug). This can also be manually enabled by users, for any builds, through
|
||||
// defining PYBIND11_DETAILED_ERROR_MESSAGES.
|
||||
// negation of NDEBUG). This can also be manually enabled by users, for any builds, through
|
||||
// defining PYBIND11_DETAILED_ERROR_MESSAGES. This information is primarily useful for those
|
||||
// who are writing (as opposed to merely using) libraries that use pybind11.
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG)
|
||||
# define PYBIND11_DETAILED_ERROR_MESSAGES
|
||||
#endif
|
||||
|
|
|
@ -143,11 +143,24 @@ constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) {
|
|||
return descr;
|
||||
}
|
||||
|
||||
#ifdef __cpp_fold_expressions
|
||||
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
|
||||
constexpr descr<N1 + N2 + 2, Ts1..., Ts2...> operator,(const descr<N1, Ts1...> &a,
|
||||
const descr<N2, Ts2...> &b) {
|
||||
return a + const_name(", ") + b;
|
||||
}
|
||||
|
||||
template <size_t N, typename... Ts, typename... Args>
|
||||
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) {
|
||||
return (d, ..., args);
|
||||
}
|
||||
#else
|
||||
template <size_t N, typename... Ts, typename... Args>
|
||||
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
|
||||
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
|
||||
return d + const_name(", ") + concat(args...);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <size_t N, typename... Ts>
|
||||
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include "class.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <>
|
||||
|
@ -115,7 +118,7 @@ template <typename Class>
|
|||
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
no_nullptr(ptr);
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
|
||||
// We're going to try to construct an alias by moving the cpp type. Whether or not
|
||||
// that succeeds, we still need to destroy the original cpp pointer (either the
|
||||
// moved away leftover, if the alias construction works, or the value itself if we
|
||||
|
@ -156,7 +159,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
|||
auto *ptr = holder_helper<Holder<Class>>::get(holder);
|
||||
no_nullptr(ptr);
|
||||
// If we need an alias, check that the held pointer is actually an alias instance
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
|
||||
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
|
||||
"is not an alias instance");
|
||||
}
|
||||
|
@ -172,9 +175,9 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
|||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
static_assert(std::is_move_constructible<Cpp<Class>>::value,
|
||||
static_assert(is_move_constructible<Cpp<Class>>::value,
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) {
|
||||
if (Class::has_alias && need_alias) {
|
||||
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
||||
} else {
|
||||
v_h.value_ptr() = new Cpp<Class>(std::move(result));
|
||||
|
@ -187,7 +190,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
|||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
||||
static_assert(
|
||||
std::is_move_constructible<Alias<Class>>::value,
|
||||
is_move_constructible<Alias<Class>>::value,
|
||||
"pybind11::init() return-by-alias-value factory function requires a movable alias class");
|
||||
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
||||
}
|
||||
|
@ -206,10 +209,11 @@ struct constructor {
|
|||
extra...);
|
||||
}
|
||||
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
template <
|
||||
typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, int>
|
||||
= 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
|
@ -226,10 +230,11 @@ struct constructor {
|
|||
extra...);
|
||||
}
|
||||
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
template <
|
||||
typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, int>
|
||||
= 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
|
@ -245,10 +250,11 @@ struct constructor {
|
|||
// Implementing class for py::init_alias<...>()
|
||||
template <typename... Args>
|
||||
struct alias_constructor {
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
template <
|
||||
typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int>
|
||||
= 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# include "../gil.h"
|
||||
#endif
|
||||
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include <exception>
|
||||
|
@ -28,15 +34,26 @@
|
|||
/// further ABI-incompatible changes may be made before the ABI is officially
|
||||
/// changed to the new version.
|
||||
#ifndef PYBIND11_INTERNALS_VERSION
|
||||
# define PYBIND11_INTERNALS_VERSION 4
|
||||
# if PY_VERSION_HEX >= 0x030C0000
|
||||
// Version bump for Python 3.12+, before first 3.12 beta release.
|
||||
# define PYBIND11_INTERNALS_VERSION 5
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_VERSION 4
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// This requirement is mainly to reduce the support burden (see PR #4570).
|
||||
static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5,
|
||||
"pybind11 ABI version 5 is the minimum for Python 3.12+");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
using ExceptionTranslator = void (*)(std::exception_ptr);
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
constexpr const char *internals_function_record_capsule_name = "pybind11_function_record_capsule";
|
||||
|
||||
// Forward declarations
|
||||
inline PyTypeObject *make_static_property_type();
|
||||
inline PyTypeObject *make_default_metaclass();
|
||||
|
@ -49,7 +66,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
|||
// `Py_LIMITED_API` anyway.
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t &
|
||||
# ifdef __GNUC__
|
||||
# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
|
||||
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
|
||||
// for every field.
|
||||
# define PYBIND11_TLS_KEY_INIT(var) \
|
||||
|
@ -106,7 +123,8 @@ inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
|
|||
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
|
||||
// which works. If not under a known-good stl, provide our own name-based hash and equality
|
||||
// functions that use the type name.
|
||||
#if defined(__GLIBCXX__)
|
||||
#if (PYBIND11_INTERNALS_VERSION <= 4 && defined(__GLIBCXX__)) \
|
||||
|| (PYBIND11_INTERNALS_VERSION >= 5 && !defined(_LIBCPP_VERSION))
|
||||
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
|
||||
using type_hash = std::hash<std::type_index>;
|
||||
using type_equal_to = std::equal_to<std::type_index>;
|
||||
|
@ -169,11 +187,23 @@ struct internals {
|
|||
PyTypeObject *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#if defined(WITH_THREAD)
|
||||
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
|
||||
PYBIND11_TLS_KEY_INIT(tstate)
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
|
||||
PyInterpreterState *istate = nullptr;
|
||||
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
// Note that we have to use a std::string to allocate memory to ensure a unique address
|
||||
// We want unique addresses since we use pointer equality to compare function records
|
||||
std::string function_record_capsule_name = internals_function_record_capsule_name;
|
||||
# endif
|
||||
|
||||
internals() = default;
|
||||
internals(const internals &other) = delete;
|
||||
internals &operator=(const internals &other) = delete;
|
||||
~internals() {
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_FREE(loader_life_support_tls_key);
|
||||
|
@ -401,6 +431,38 @@ inline void translate_local_exception(std::exception_ptr p) {
|
|||
}
|
||||
#endif
|
||||
|
||||
inline object get_python_state_dict() {
|
||||
object state_dict;
|
||||
#if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
|
||||
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
|
||||
#else
|
||||
# if PY_VERSION_HEX < 0x03090000
|
||||
PyInterpreterState *istate = _PyInterpreterState_Get();
|
||||
# else
|
||||
PyInterpreterState *istate = PyInterpreterState_Get();
|
||||
# endif
|
||||
if (istate) {
|
||||
state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
|
||||
}
|
||||
#endif
|
||||
if (!state_dict) {
|
||||
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
|
||||
}
|
||||
return state_dict;
|
||||
}
|
||||
|
||||
inline object get_internals_obj_from_state_dict(handle state_dict) {
|
||||
return reinterpret_borrow<object>(dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID));
|
||||
}
|
||||
|
||||
inline internals **get_internals_pp_from_capsule(handle obj) {
|
||||
void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr);
|
||||
if (raw_ptr == nullptr) {
|
||||
raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED");
|
||||
}
|
||||
return static_cast<internals **>(raw_ptr);
|
||||
}
|
||||
|
||||
/// Return a reference to the current `internals` data
|
||||
PYBIND11_NOINLINE internals &get_internals() {
|
||||
auto **&internals_pp = get_internals_pp();
|
||||
|
@ -408,21 +470,29 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||
return **internals_pp;
|
||||
}
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
# if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
gil_scoped_acquire gil;
|
||||
# else
|
||||
// Ensure that the GIL is held since we will need to make Python calls.
|
||||
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
|
||||
struct gil_scoped_acquire_local {
|
||||
gil_scoped_acquire_local() : state(PyGILState_Ensure()) {}
|
||||
gil_scoped_acquire_local(const gil_scoped_acquire_local &) = delete;
|
||||
gil_scoped_acquire_local &operator=(const gil_scoped_acquire_local &) = delete;
|
||||
~gil_scoped_acquire_local() { PyGILState_Release(state); }
|
||||
const PyGILState_STATE state;
|
||||
} gil;
|
||||
# endif
|
||||
#endif
|
||||
error_scope err_scope;
|
||||
|
||||
PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID);
|
||||
auto builtins = handle(PyEval_GetBuiltins());
|
||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
|
||||
internals_pp = static_cast<internals **>(capsule(builtins[id]));
|
||||
|
||||
// We loaded builtins through python's builtins, which means that our `error_already_set`
|
||||
dict state_dict = get_python_state_dict();
|
||||
if (object internals_obj = get_internals_obj_from_state_dict(state_dict)) {
|
||||
internals_pp = get_internals_pp_from_capsule(internals_obj);
|
||||
}
|
||||
if (internals_pp && *internals_pp) {
|
||||
// We loaded the internals through `state_dict`, which means that our `error_already_set`
|
||||
// and `builtin_exception` may be different local classes than the ones set up in the
|
||||
// initial exception translator, below, so add another for our local exception classes.
|
||||
//
|
||||
|
@ -440,16 +510,15 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||
internals_ptr = new internals();
|
||||
#if defined(WITH_THREAD)
|
||||
|
||||
# if PY_VERSION_HEX < 0x03090000
|
||||
PyEval_InitThreads();
|
||||
# endif
|
||||
PyThreadState *tstate = PyThreadState_Get();
|
||||
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||
}
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
||||
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the "
|
||||
"loader_life_support TSS key!");
|
||||
|
@ -457,7 +526,7 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||
# endif
|
||||
internals_ptr->istate = tstate->interp;
|
||||
#endif
|
||||
builtins[id] = capsule(internals_pp);
|
||||
state_dict[PYBIND11_INTERNALS_ID] = capsule(internals_pp);
|
||||
internals_ptr->registered_exception_translators.push_front(&translate_exception);
|
||||
internals_ptr->static_property_type = make_static_property_type();
|
||||
internals_ptr->default_metaclass = make_default_metaclass();
|
||||
|
@ -489,6 +558,7 @@ struct local_internals {
|
|||
struct shared_loader_life_support_data {
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
shared_loader_life_support_data() {
|
||||
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
|
||||
pybind11_fail("local_internals: could not successfully initialize the "
|
||||
"loader_life_support TLS key!");
|
||||
|
@ -512,8 +582,13 @@ struct local_internals {
|
|||
|
||||
/// Works like `get_internals`, but for things which are locally registered.
|
||||
inline local_internals &get_local_internals() {
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
// Current static can be created in the interpreter finalization routine. If the later will be
|
||||
// destroyed in another static variable destructor, creation of this static there will cause
|
||||
// static deinitialization fiasco. In order to avoid it we avoid destruction of the
|
||||
// local_internals static. One can read more about the problem and current solution here:
|
||||
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
|
||||
static auto *locals = new local_internals();
|
||||
return *locals;
|
||||
}
|
||||
|
||||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
||||
|
@ -527,6 +602,25 @@ const char *c_str(Args &&...args) {
|
|||
return strings.front().c_str();
|
||||
}
|
||||
|
||||
inline const char *get_function_record_capsule_name() {
|
||||
#if PYBIND11_INTERNALS_VERSION > 4
|
||||
return get_internals().function_record_capsule_name.c_str();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine whether or not the following capsule contains a pybind11 function record.
|
||||
// Note that we use `internals` to make sure that only ABI compatible records are touched.
|
||||
//
|
||||
// This check is currently used in two places:
|
||||
// - An important optimization in functional.h to avoid overhead in C++ -> Python -> C++
|
||||
// - The sibling feature of cpp_function to allow overloads
|
||||
inline bool is_function_record_capsule(const capsule &cap) {
|
||||
// Pointer equality as we rely on internals() to ensure unique pointers
|
||||
return cap.name() == get_function_record_capsule_name();
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Returns a named pointer that is shared among all extension modules (using the same
|
||||
|
|
|
@ -258,9 +258,9 @@ struct value_and_holder {
|
|||
|
||||
// Main constructor for a found value/holder:
|
||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
|
||||
: inst{i}, index{index}, type{type}, vh{inst->simple_layout
|
||||
? inst->simple_value_holder
|
||||
: &inst->nonsimple.values_and_holders[vpos]} {}
|
||||
: inst{i}, index{index}, type{type},
|
||||
vh{inst->simple_layout ? inst->simple_value_holder
|
||||
: &inst->nonsimple.values_and_holders[vpos]} {}
|
||||
|
||||
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
||||
value_and_holder() = default;
|
||||
|
@ -822,23 +822,179 @@ using movable_cast_op_type
|
|||
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
||||
|
||||
// Does the container have a mapped type and is it recursive?
|
||||
// Implemented by specializations below.
|
||||
template <typename Container, typename SFINAE = void>
|
||||
struct container_mapped_type_traits {
|
||||
static constexpr bool has_mapped_type = false;
|
||||
static constexpr bool has_recursive_mapped_type = false;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
struct container_mapped_type_traits<
|
||||
Container,
|
||||
typename std::enable_if<
|
||||
std::is_same<typename Container::mapped_type, Container>::value>::type> {
|
||||
static constexpr bool has_mapped_type = true;
|
||||
static constexpr bool has_recursive_mapped_type = true;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
struct container_mapped_type_traits<
|
||||
Container,
|
||||
typename std::enable_if<
|
||||
negation<std::is_same<typename Container::mapped_type, Container>>::value>::type> {
|
||||
static constexpr bool has_mapped_type = true;
|
||||
static constexpr bool has_recursive_mapped_type = false;
|
||||
};
|
||||
|
||||
// Does the container have a value type and is it recursive?
|
||||
// Implemented by specializations below.
|
||||
template <typename Container, typename SFINAE = void>
|
||||
struct container_value_type_traits : std::false_type {
|
||||
static constexpr bool has_value_type = false;
|
||||
static constexpr bool has_recursive_value_type = false;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
struct container_value_type_traits<
|
||||
Container,
|
||||
typename std::enable_if<
|
||||
std::is_same<typename Container::value_type, Container>::value>::type> {
|
||||
static constexpr bool has_value_type = true;
|
||||
static constexpr bool has_recursive_value_type = true;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
struct container_value_type_traits<
|
||||
Container,
|
||||
typename std::enable_if<
|
||||
negation<std::is_same<typename Container::value_type, Container>>::value>::type> {
|
||||
static constexpr bool has_value_type = true;
|
||||
static constexpr bool has_recursive_value_type = false;
|
||||
};
|
||||
|
||||
/*
|
||||
* Tag to be used for representing the bottom of recursively defined types.
|
||||
* Define this tag so we don't have to use void.
|
||||
*/
|
||||
struct recursive_bottom {};
|
||||
|
||||
/*
|
||||
* Implementation detail of `recursive_container_traits` below.
|
||||
* `T` is the `value_type` of the container, which might need to be modified to
|
||||
* avoid recursive types and const types.
|
||||
*/
|
||||
template <typename T, bool is_this_a_map>
|
||||
struct impl_type_to_check_recursively {
|
||||
/*
|
||||
* If the container is recursive, then no further recursion should be done.
|
||||
*/
|
||||
using if_recursive = recursive_bottom;
|
||||
/*
|
||||
* Otherwise yield `T` unchanged.
|
||||
*/
|
||||
using if_not_recursive = T;
|
||||
};
|
||||
|
||||
/*
|
||||
* For pairs - only as value type of a map -, the first type should remove the `const`.
|
||||
* Also, if the map is recursive, then the recursive checking should consider
|
||||
* the first type only.
|
||||
*/
|
||||
template <typename A, typename B>
|
||||
struct impl_type_to_check_recursively<std::pair<A, B>, /* is_this_a_map = */ true> {
|
||||
using if_recursive = typename std::remove_const<A>::type;
|
||||
using if_not_recursive = std::pair<typename std::remove_const<A>::type, B>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of `recursive_container_traits` below.
|
||||
*/
|
||||
template <typename Container, typename SFINAE = void>
|
||||
struct impl_recursive_container_traits {
|
||||
using type_to_check_recursively = recursive_bottom;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
struct impl_recursive_container_traits<
|
||||
Container,
|
||||
typename std::enable_if<container_value_type_traits<Container>::has_value_type>::type> {
|
||||
static constexpr bool is_recursive
|
||||
= container_mapped_type_traits<Container>::has_recursive_mapped_type
|
||||
|| container_value_type_traits<Container>::has_recursive_value_type;
|
||||
/*
|
||||
* This member dictates which type Pybind11 should check recursively in traits
|
||||
* such as `is_move_constructible`, `is_copy_constructible`, `is_move_assignable`, ...
|
||||
* Direct access to `value_type` should be avoided:
|
||||
* 1. `value_type` might recursively contain the type again
|
||||
* 2. `value_type` of STL map types is `std::pair<A const, B>`, the `const`
|
||||
* should be removed.
|
||||
*
|
||||
*/
|
||||
using type_to_check_recursively = typename std::conditional<
|
||||
is_recursive,
|
||||
typename impl_type_to_check_recursively<
|
||||
typename Container::value_type,
|
||||
container_mapped_type_traits<Container>::has_mapped_type>::if_recursive,
|
||||
typename impl_type_to_check_recursively<
|
||||
typename Container::value_type,
|
||||
container_mapped_type_traits<Container>::has_mapped_type>::if_not_recursive>::type;
|
||||
};
|
||||
|
||||
/*
|
||||
* This trait defines the `type_to_check_recursively` which is needed to properly
|
||||
* handle recursively defined traits such as `is_move_constructible` without going
|
||||
* into an infinite recursion.
|
||||
* Should be used instead of directly accessing the `value_type`.
|
||||
* It cancels the recursion by returning the `recursive_bottom` tag.
|
||||
*
|
||||
* The default definition of `type_to_check_recursively` is as follows:
|
||||
*
|
||||
* 1. By default, it is `recursive_bottom`, so that the recursion is canceled.
|
||||
* 2. If the type is non-recursive and defines a `value_type`, then the `value_type` is used.
|
||||
* If the `value_type` is a pair and a `mapped_type` is defined,
|
||||
* then the `const` is removed from the first type.
|
||||
* 3. If the type is recursive and `value_type` is not a pair, then `recursive_bottom` is returned.
|
||||
* 4. If the type is recursive and `value_type` is a pair and a `mapped_type` is defined,
|
||||
* then `const` is removed from the first type and the first type is returned.
|
||||
*
|
||||
* This behavior can be extended by the user as seen in test_stl_binders.cpp.
|
||||
*
|
||||
* This struct is exactly the same as impl_recursive_container_traits.
|
||||
* The duplication achieves that user-defined specializations don't compete
|
||||
* with internal specializations, but take precedence.
|
||||
*/
|
||||
template <typename Container, typename SFINAE = void>
|
||||
struct recursive_container_traits : impl_recursive_container_traits<Container> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_move_constructible
|
||||
: all_of<std::is_move_constructible<T>,
|
||||
is_move_constructible<
|
||||
typename recursive_container_traits<T>::type_to_check_recursively>> {};
|
||||
|
||||
template <>
|
||||
struct is_move_constructible<recursive_bottom> : std::true_type {};
|
||||
|
||||
// Likewise for std::pair
|
||||
// (after C++17 it is mandatory that the move constructor not exist when the two types aren't
|
||||
// themselves move constructible, but this can not be relied upon when T1 or T2 are themselves
|
||||
// containers).
|
||||
template <typename T1, typename T2>
|
||||
struct is_move_constructible<std::pair<T1, T2>>
|
||||
: all_of<is_move_constructible<T1>, is_move_constructible<T2>> {};
|
||||
|
||||
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
|
||||
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_copy_constructible : std::is_copy_constructible<T> {};
|
||||
template <typename T>
|
||||
struct is_copy_constructible
|
||||
: all_of<std::is_copy_constructible<T>,
|
||||
is_copy_constructible<
|
||||
typename recursive_container_traits<T>::type_to_check_recursively>> {};
|
||||
|
||||
// Specialization for types that appear to be copy constructible but also look like stl containers
|
||||
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
|
||||
// so, copy constructability depends on whether the value_type is copy constructible.
|
||||
template <typename Container>
|
||||
struct is_copy_constructible<
|
||||
Container,
|
||||
enable_if_t<
|
||||
all_of<std::is_copy_constructible<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
||||
// Avoid infinite recursion
|
||||
negation<std::is_same<Container, typename Container::value_type>>>::value>>
|
||||
: is_copy_constructible<typename Container::value_type> {};
|
||||
template <>
|
||||
struct is_copy_constructible<recursive_bottom> : std::true_type {};
|
||||
|
||||
// Likewise for std::pair
|
||||
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
|
||||
|
@ -849,14 +1005,16 @@ struct is_copy_constructible<std::pair<T1, T2>>
|
|||
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
||||
|
||||
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_copy_assignable : std::is_copy_assignable<T> {};
|
||||
template <typename Container>
|
||||
struct is_copy_assignable<Container,
|
||||
enable_if_t<all_of<std::is_copy_assignable<Container>,
|
||||
std::is_same<typename Container::value_type &,
|
||||
typename Container::reference>>::value>>
|
||||
: is_copy_assignable<typename Container::value_type> {};
|
||||
template <typename T>
|
||||
struct is_copy_assignable
|
||||
: all_of<
|
||||
std::is_copy_assignable<T>,
|
||||
is_copy_assignable<typename recursive_container_traits<T>::type_to_check_recursively>> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_copy_assignable<recursive_bottom> : std::true_type {};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct is_copy_assignable<std::pair<T1, T2>>
|
||||
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
|
||||
|
@ -994,7 +1152,7 @@ protected:
|
|||
return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); };
|
||||
}
|
||||
|
||||
template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>>
|
||||
template <typename T, typename = enable_if_t<is_move_constructible<T>::value>>
|
||||
static auto make_move_constructor(const T *)
|
||||
-> decltype(new T(std::declval<T &&>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
|
@ -1006,5 +1164,14 @@ protected:
|
|||
static Constructor make_move_constructor(...) { return nullptr; }
|
||||
};
|
||||
|
||||
PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) {
|
||||
if (auto *type_data = get_type_info(ti)) {
|
||||
handle th((PyObject *) type_data->type);
|
||||
return th.attr("__module__").cast<std::string>() + '.'
|
||||
+ th.attr("__qualname__").cast<std::string>();
|
||||
}
|
||||
return clean_type_id(ti.name());
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -9,700 +9,4 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||
See also:
|
||||
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
|
||||
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
|
||||
*/
|
||||
|
||||
#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
|
||||
# pragma warning(disable : 5054) // https://github.com/pybind/pybind11/pull/3741
|
||||
// C5054: operator '&': deprecated between enumerations of different types
|
||||
#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.
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
|
||||
"Eigen support in pybind11 requires Eigen >= 3.2.7");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
|
||||
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
|
||||
template <typename MatrixType>
|
||||
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#endif
|
||||
|
||||
// Matches Eigen::Map, Eigen::Ref, blocks, etc:
|
||||
template <typename T>
|
||||
using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
|
||||
std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
|
||||
template <typename T>
|
||||
using is_eigen_dense_plain
|
||||
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
|
||||
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
|
||||
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
|
||||
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
|
||||
// SelfAdjointView fall into this category.
|
||||
template <typename T>
|
||||
using is_eigen_other
|
||||
= all_of<is_template_base_of<Eigen::EigenBase, T>,
|
||||
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
|
||||
|
||||
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
|
||||
template <bool EigenRowMajor>
|
||||
struct EigenConformable {
|
||||
bool conformable = false;
|
||||
EigenIndex rows = 0, cols = 0;
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
EigenConformable(bool fits = false) : conformable{fits} {}
|
||||
// Matrix type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
|
||||
: conformable{true}, rows{r}, cols{c},
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
|
||||
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
|
||||
: (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0)
|
||||
: (rstride > 0 ? rstride : 0) /* inner stride */},
|
||||
negativestrides{rstride < 0 || cstride < 0} {}
|
||||
// Vector type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
|
||||
: EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
|
||||
|
||||
template <typename props>
|
||||
bool stride_compatible() const {
|
||||
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant
|
||||
// (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly).
|
||||
if (negativestrides) {
|
||||
return false;
|
||||
}
|
||||
if (rows == 0 || cols == 0) {
|
||||
return true;
|
||||
}
|
||||
return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct eigen_extract_stride {
|
||||
using type = Type;
|
||||
};
|
||||
template <typename PlainObjectType, int MapOptions, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
template <typename PlainObjectType, int Options, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
|
||||
// Helper struct for extracting information from an Eigen type
|
||||
template <typename Type_>
|
||||
struct EigenProps {
|
||||
using Type = Type_;
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StrideType = typename eigen_extract_stride<Type>::type;
|
||||
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool row_major = Type::IsRowMajor,
|
||||
vector
|
||||
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
|
||||
template <EigenIndex i, EigenIndex ifzero>
|
||||
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride
|
||||
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size
|
||||
: row_major ? cols
|
||||
: rows > ::value;
|
||||
static constexpr bool dynamic_stride
|
||||
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
|
||||
static constexpr bool requires_row_major
|
||||
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
|
||||
static constexpr bool requires_col_major
|
||||
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
|
||||
|
||||
// Takes an input array and determines whether we can make it fit into the Eigen type. If
|
||||
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
|
||||
// (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
|
||||
static EigenConformable<row_major> conformable(const array &a) {
|
||||
const auto dims = a.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dims == 2) { // Matrix type: require exact match (or dynamic)
|
||||
|
||||
EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
|
||||
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
|
||||
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows)
|
||||
|| (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {np_rows, np_cols, np_rstride, np_cstride};
|
||||
}
|
||||
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
|
||||
// whichever is used, we want the (single) numpy stride value.
|
||||
const EigenIndex n = a.shape(0),
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) {
|
||||
return false; // Vector size mismatch
|
||||
}
|
||||
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
|
||||
}
|
||||
if (fixed) {
|
||||
// The type has a fixed size, but is not a vector: abort
|
||||
return false;
|
||||
}
|
||||
if (fixed_cols) {
|
||||
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly
|
||||
// equals the number of elements (rows is Dynamic, and so 1 row is allowed).
|
||||
if (cols != n) {
|
||||
return false;
|
||||
}
|
||||
return {1, n, stride};
|
||||
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) {
|
||||
return false;
|
||||
}
|
||||
return {n, 1, stride};
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable
|
||||
= is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
static constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
static constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
static constexpr bool show_f_contiguous
|
||||
= !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
static constexpr auto descriptor
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
|
||||
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
|
||||
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
|
||||
+
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
|
||||
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's
|
||||
// stride options, possibly f_contiguous or c_contiguous. We include them in the
|
||||
// descriptor output to provide some hint as to why a TypeError is occurring (otherwise
|
||||
// it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
|
||||
// an error message that you *gave* a numpy.ndarray of the right type and dimensions.
|
||||
const_name<show_writeable>(", flags.writeable", "")
|
||||
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
|
||||
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
|
||||
};
|
||||
|
||||
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
|
||||
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
|
||||
template <typename props>
|
||||
handle
|
||||
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
|
||||
constexpr ssize_t elem_size = sizeof(typename props::Scalar);
|
||||
array a;
|
||||
if (props::vector) {
|
||||
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
|
||||
} else {
|
||||
a = array({src.rows(), src.cols()},
|
||||
{elem_size * src.rowStride(), elem_size * src.colStride()},
|
||||
src.data(),
|
||||
base);
|
||||
}
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return a.release();
|
||||
}
|
||||
|
||||
// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that
|
||||
// reference the Eigen object's data with `base` as the python-registered base class (if omitted,
|
||||
// the base will be set to None, and lifetime management is up to the caller). The numpy array is
|
||||
// non-writeable if the given type is const.
|
||||
template <typename props, typename Type>
|
||||
handle eigen_ref_array(Type &src, handle parent = none()) {
|
||||
// none here is to get past array's should-we-copy detection, which currently always
|
||||
// copies when there is no base. Setting the base to None should be harmless.
|
||||
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
|
||||
}
|
||||
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
|
||||
// numpy array that references the encapsulated data with a python-side reference to the capsule to
|
||||
// tie its destruction to that of any dependent python objects. Const-ness is determined by
|
||||
// whether or not the Type of the pointer given is const.
|
||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||
handle eigen_encapsulate(Type *src) {
|
||||
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||
return eigen_ref_array<props>(*src, base);
|
||||
}
|
||||
|
||||
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
|
||||
// types.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using props = EigenProps<Type>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
// If we're in no-convert mode, only load if given an array of the correct type
|
||||
if (!convert && !isinstance<array_t<Scalar>>(src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Coerce into an array, but don't do type conversion yet; the copy below handles it.
|
||||
auto buf = array::ensure(src);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dims = buf.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fits = props::conformable(buf);
|
||||
if (!fits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the new type, then build a numpy reference into it
|
||||
value = Type(fits.rows, fits.cols);
|
||||
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
|
||||
if (dims == 1) {
|
||||
ref = ref.squeeze();
|
||||
} else if (ref.ndim() == 1) {
|
||||
buf = buf.squeeze();
|
||||
}
|
||||
|
||||
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
|
||||
|
||||
if (result < 0) { // Copy failed!
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Cast implementation
|
||||
template <typename CType>
|
||||
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::take_ownership:
|
||||
case return_value_policy::automatic:
|
||||
return eigen_encapsulate<props>(src);
|
||||
case return_value_policy::move:
|
||||
return eigen_encapsulate<props>(new CType(std::move(*src)));
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(*src);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_ref_array<props>(*src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_ref_array<props>(*src, parent);
|
||||
default:
|
||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||
};
|
||||
}
|
||||
|
||||
public:
|
||||
// Normal returned non-reference, non-const value:
|
||||
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// If you return a non-reference const, we mark the numpy array readonly:
|
||||
static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
// const lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
// non-const pointer return
|
||||
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
// const pointer return
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type *() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &() { return value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &&() && { return std::move(value); }
|
||||
template <typename T>
|
||||
using cast_op_type = movable_cast_op_type<T>;
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// Base class for casting reference/map/block/etc. objects back to python.
|
||||
template <typename MapType>
|
||||
struct eigen_map_caster {
|
||||
private:
|
||||
using props = EigenProps<MapType>;
|
||||
|
||||
public:
|
||||
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing
|
||||
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
|
||||
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
|
||||
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
|
||||
// with an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_array_cast<props>(src, none(), is_eigen_mutable_map<MapType>::value);
|
||||
default:
|
||||
// move, take_ownership don't make any sense for a ref/map:
|
||||
pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator MapType() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = MapType;
|
||||
};
|
||||
|
||||
// We can return any map-like object (but can only load Refs, specialized next):
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
|
||||
|
||||
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without
|
||||
// copying (it requires some extra effort in many cases).
|
||||
template <typename PlainObjectType, typename StrideType>
|
||||
struct type_caster<
|
||||
Eigen::Ref<PlainObjectType, 0, StrideType>,
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
|
||||
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
private:
|
||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||
using props = EigenProps<Type>;
|
||||
using Scalar = typename props::Scalar;
|
||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||
using Array
|
||||
= array_t<Scalar,
|
||||
array::forcecast
|
||||
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
|
||||
? array::c_style
|
||||
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
|
||||
? array::f_style
|
||||
: 0)>;
|
||||
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
|
||||
// Delay construction (these have no default constructor)
|
||||
std::unique_ptr<MapType> map;
|
||||
std::unique_ptr<Type> ref;
|
||||
// Our array. When possible, this is just a numpy array pointing to the source data, but
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
|
||||
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
|
||||
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
|
||||
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
|
||||
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
Array copy_or_ref;
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
// First check whether what we have is already an array of the right type. If not, we
|
||||
// can't avoid a copy (because the copy is also going to do type conversion).
|
||||
bool need_copy = !isinstance<Array>(src);
|
||||
|
||||
EigenConformable<props::row_major> fits;
|
||||
if (!need_copy) {
|
||||
// We don't need a converting copy, but we also need to check whether the strides are
|
||||
// compatible with the Ref's stride requirements
|
||||
auto aref = reinterpret_borrow<Array>(src);
|
||||
|
||||
if (aref && (!need_writeable || aref.writeable())) {
|
||||
fits = props::conformable(aref);
|
||||
if (!fits) {
|
||||
return false; // Incompatible dimensions
|
||||
}
|
||||
if (!fits.template stride_compatible<props>()) {
|
||||
need_copy = true;
|
||||
} else {
|
||||
copy_or_ref = std::move(aref);
|
||||
}
|
||||
} else {
|
||||
need_copy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_copy) {
|
||||
// We need to copy: If we need a mutable reference, or we're not supposed to convert
|
||||
// (either because we're in the no-convert overload pass, or because we're explicitly
|
||||
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
|
||||
if (!convert || need_writeable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array copy = Array::ensure(src);
|
||||
if (!copy) {
|
||||
return false;
|
||||
}
|
||||
fits = props::conformable(copy);
|
||||
if (!fits || !fits.template stride_compatible<props>()) {
|
||||
return false;
|
||||
}
|
||||
copy_or_ref = std::move(copy);
|
||||
loader_life_support::add_patient(copy_or_ref);
|
||||
}
|
||||
|
||||
ref.reset();
|
||||
map.reset(new MapType(data(copy_or_ref),
|
||||
fits.rows,
|
||||
fits.cols,
|
||||
make_stride(fits.stride.outer(), fits.stride.inner())));
|
||||
ref.reset(new Type(*map));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type *() { return ref.get(); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &() { return *ref; }
|
||||
template <typename _T>
|
||||
using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
|
||||
Scalar *data(Array &a) {
|
||||
return a.mutable_data();
|
||||
}
|
||||
|
||||
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
|
||||
const Scalar *data(Array &a) {
|
||||
return a.data();
|
||||
}
|
||||
|
||||
// Attempt to figure out a constructor of `Stride` that will work.
|
||||
// If both strides are fixed, use a default constructor:
|
||||
template <typename S>
|
||||
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_default_constructible<S>::value>;
|
||||
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
|
||||
// Eigen::Stride, and use it:
|
||||
template <typename S>
|
||||
using stride_ctor_dual
|
||||
= bool_constant<!stride_ctor_default<S>::value
|
||||
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
|
||||
// it (passing whichever stride is dynamic).
|
||||
template <typename S>
|
||||
using stride_ctor_outer
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_inner
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex) {
|
||||
return S();
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex inner) {
|
||||
return S(outer, inner);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex) {
|
||||
return S(outer);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex inner) {
|
||||
return S(inner);
|
||||
}
|
||||
};
|
||||
|
||||
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
|
||||
// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout).
|
||||
// load() is not supported, but we can cast them into the python domain by first copying to a
|
||||
// regular Eigen::Matrix, then casting that.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||
protected:
|
||||
using Matrix
|
||||
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||
using props = EigenProps<Matrix>;
|
||||
|
||||
public:
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
handle h = eigen_encapsulate<props>(new Matrix(src));
|
||||
return h;
|
||||
}
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator Type() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = Type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||
using Index = typename Type::Index;
|
||||
static constexpr bool rowMajor = Type::IsRowMajor;
|
||||
|
||||
bool load(handle src, bool) {
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obj = reinterpret_borrow<object>(src);
|
||||
object sparse_module = module_::import("scipy.sparse");
|
||||
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
if (!type::handle_of(obj).is(matrix_type)) {
|
||||
try {
|
||||
obj = matrix_type(obj);
|
||||
} catch (const error_already_set &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto values = array_t<Scalar>((object) obj.attr("data"));
|
||||
auto innerIndices = array_t<StorageIndex>((object) obj.attr("indices"));
|
||||
auto outerIndices = array_t<StorageIndex>((object) obj.attr("indptr"));
|
||||
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
|
||||
auto nnz = obj.attr("nnz").cast<Index>();
|
||||
|
||||
if (!values || !innerIndices || !outerIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = EigenMapSparseMatrix<Scalar,
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
std::move(nnz),
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type &>(src).makeCompressed();
|
||||
|
||||
object matrix_type
|
||||
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
array data(src.nonZeros(), src.valuePtr());
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(pybind11::make_tuple(
|
||||
std::move(data), std::move(innerIndices), std::move(outerIndices)),
|
||||
pybind11::make_tuple(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
|
||||
"scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
#include "eigen/matrix.h"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2023 The pybind Community.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Common message for `static_assert()`s, which are useful to easily
|
||||
// preempt much less obvious errors.
|
||||
#define PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED \
|
||||
"Pointer types (in particular `PyObject *`) are not supported as scalar types for Eigen " \
|
||||
"types."
|
|
@ -0,0 +1,714 @@
|
|||
/*
|
||||
pybind11/eigen/matrix.h: Transparent conversion for dense and sparse Eigen matrices
|
||||
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../numpy.h"
|
||||
#include "common.h"
|
||||
|
||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||
See also:
|
||||
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
|
||||
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
|
||||
*/
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(5054) // https://github.com/pybind/pybind11/pull/3741
|
||||
// C5054: operator '&': deprecated between enumerations of different types
|
||||
#if defined(__MINGW32__)
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
// 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.
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
|
||||
"Eigen matrix support in pybind11 requires Eigen >= 3.2.7");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
|
||||
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
|
||||
template <typename MatrixType>
|
||||
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#endif
|
||||
|
||||
// Matches Eigen::Map, Eigen::Ref, blocks, etc:
|
||||
template <typename T>
|
||||
using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
|
||||
std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
|
||||
template <typename T>
|
||||
using is_eigen_dense_plain
|
||||
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
|
||||
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
|
||||
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
|
||||
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
|
||||
// SelfAdjointView fall into this category.
|
||||
template <typename T>
|
||||
using is_eigen_other
|
||||
= all_of<is_template_base_of<Eigen::EigenBase, T>,
|
||||
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
|
||||
|
||||
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
|
||||
template <bool EigenRowMajor>
|
||||
struct EigenConformable {
|
||||
bool conformable = false;
|
||||
EigenIndex rows = 0, cols = 0;
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
EigenConformable(bool fits = false) : conformable{fits} {}
|
||||
// Matrix type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
|
||||
: conformable{true}, rows{r}, cols{c},
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
|
||||
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
|
||||
: (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0)
|
||||
: (rstride > 0 ? rstride : 0) /* inner stride */},
|
||||
negativestrides{rstride < 0 || cstride < 0} {}
|
||||
// Vector type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
|
||||
: EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
|
||||
|
||||
template <typename props>
|
||||
bool stride_compatible() const {
|
||||
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant
|
||||
// (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly).
|
||||
if (negativestrides) {
|
||||
return false;
|
||||
}
|
||||
if (rows == 0 || cols == 0) {
|
||||
return true;
|
||||
}
|
||||
return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct eigen_extract_stride {
|
||||
using type = Type;
|
||||
};
|
||||
template <typename PlainObjectType, int MapOptions, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
template <typename PlainObjectType, int Options, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
|
||||
// Helper struct for extracting information from an Eigen type
|
||||
template <typename Type_>
|
||||
struct EigenProps {
|
||||
using Type = Type_;
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StrideType = typename eigen_extract_stride<Type>::type;
|
||||
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool row_major = Type::IsRowMajor,
|
||||
vector
|
||||
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
|
||||
template <EigenIndex i, EigenIndex ifzero>
|
||||
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride
|
||||
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size
|
||||
: row_major ? cols
|
||||
: rows > ::value;
|
||||
static constexpr bool dynamic_stride
|
||||
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
|
||||
static constexpr bool requires_row_major
|
||||
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
|
||||
static constexpr bool requires_col_major
|
||||
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
|
||||
|
||||
// Takes an input array and determines whether we can make it fit into the Eigen type. If
|
||||
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
|
||||
// (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
|
||||
static EigenConformable<row_major> conformable(const array &a) {
|
||||
const auto dims = a.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dims == 2) { // Matrix type: require exact match (or dynamic)
|
||||
|
||||
EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
|
||||
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
|
||||
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {np_rows, np_cols, np_rstride, np_cstride};
|
||||
}
|
||||
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
|
||||
// whichever is used, we want the (single) numpy stride value.
|
||||
const EigenIndex n = a.shape(0),
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (fixed && size != n) {
|
||||
return false; // Vector size mismatch
|
||||
}
|
||||
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
|
||||
}
|
||||
if (fixed) {
|
||||
// The type has a fixed size, but is not a vector: abort
|
||||
return false;
|
||||
}
|
||||
if (fixed_cols) {
|
||||
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly
|
||||
// equals the number of elements (rows is Dynamic, and so 1 row is allowed).
|
||||
if (cols != n) {
|
||||
return false;
|
||||
}
|
||||
return {1, n, stride};
|
||||
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
|
||||
if (fixed_rows && rows != n) {
|
||||
return false;
|
||||
}
|
||||
return {n, 1, stride};
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable
|
||||
= is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
static constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
static constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
static constexpr bool show_f_contiguous
|
||||
= !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
static constexpr auto descriptor
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
|
||||
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
|
||||
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
|
||||
+
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
|
||||
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's
|
||||
// stride options, possibly f_contiguous or c_contiguous. We include them in the
|
||||
// descriptor output to provide some hint as to why a TypeError is occurring (otherwise
|
||||
// it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
|
||||
// an error message that you *gave* a numpy.ndarray of the right type and dimensions.
|
||||
const_name<show_writeable>(", flags.writeable", "")
|
||||
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
|
||||
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
|
||||
};
|
||||
|
||||
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
|
||||
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
|
||||
template <typename props>
|
||||
handle
|
||||
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
|
||||
constexpr ssize_t elem_size = sizeof(typename props::Scalar);
|
||||
array a;
|
||||
if (props::vector) {
|
||||
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
|
||||
} else {
|
||||
a = array({src.rows(), src.cols()},
|
||||
{elem_size * src.rowStride(), elem_size * src.colStride()},
|
||||
src.data(),
|
||||
base);
|
||||
}
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return a.release();
|
||||
}
|
||||
|
||||
// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that
|
||||
// reference the Eigen object's data with `base` as the python-registered base class (if omitted,
|
||||
// the base will be set to None, and lifetime management is up to the caller). The numpy array is
|
||||
// non-writeable if the given type is const.
|
||||
template <typename props, typename Type>
|
||||
handle eigen_ref_array(Type &src, handle parent = none()) {
|
||||
// none here is to get past array's should-we-copy detection, which currently always
|
||||
// copies when there is no base. Setting the base to None should be harmless.
|
||||
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
|
||||
}
|
||||
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
|
||||
// numpy array that references the encapsulated data with a python-side reference to the capsule to
|
||||
// tie its destruction to that of any dependent python objects. Const-ness is determined by
|
||||
// whether or not the Type of the pointer given is const.
|
||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||
handle eigen_encapsulate(Type *src) {
|
||||
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||
return eigen_ref_array<props>(*src, base);
|
||||
}
|
||||
|
||||
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
|
||||
// types.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
static_assert(!std::is_pointer<Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
using props = EigenProps<Type>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
// If we're in no-convert mode, only load if given an array of the correct type
|
||||
if (!convert && !isinstance<array_t<Scalar>>(src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Coerce into an array, but don't do type conversion yet; the copy below handles it.
|
||||
auto buf = array::ensure(src);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dims = buf.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fits = props::conformable(buf);
|
||||
if (!fits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the new type, then build a numpy reference into it
|
||||
value = Type(fits.rows, fits.cols);
|
||||
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
|
||||
if (dims == 1) {
|
||||
ref = ref.squeeze();
|
||||
} else if (ref.ndim() == 1) {
|
||||
buf = buf.squeeze();
|
||||
}
|
||||
|
||||
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
|
||||
|
||||
if (result < 0) { // Copy failed!
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Cast implementation
|
||||
template <typename CType>
|
||||
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::take_ownership:
|
||||
case return_value_policy::automatic:
|
||||
return eigen_encapsulate<props>(src);
|
||||
case return_value_policy::move:
|
||||
return eigen_encapsulate<props>(new CType(std::move(*src)));
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(*src);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_ref_array<props>(*src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_ref_array<props>(*src, parent);
|
||||
default:
|
||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||
};
|
||||
}
|
||||
|
||||
public:
|
||||
// Normal returned non-reference, non-const value:
|
||||
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// If you return a non-reference const, we mark the numpy array readonly:
|
||||
static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
// const lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
// non-const pointer return
|
||||
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
// const pointer return
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type *() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &() { return value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &&() && { return std::move(value); }
|
||||
template <typename T>
|
||||
using cast_op_type = movable_cast_op_type<T>;
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// Base class for casting reference/map/block/etc. objects back to python.
|
||||
template <typename MapType>
|
||||
struct eigen_map_caster {
|
||||
static_assert(!std::is_pointer<typename MapType::Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
|
||||
private:
|
||||
using props = EigenProps<MapType>;
|
||||
|
||||
public:
|
||||
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing
|
||||
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
|
||||
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
|
||||
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
|
||||
// with an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_array_cast<props>(src, none(), is_eigen_mutable_map<MapType>::value);
|
||||
default:
|
||||
// move, take_ownership don't make any sense for a ref/map:
|
||||
pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator MapType() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = MapType;
|
||||
};
|
||||
|
||||
// We can return any map-like object (but can only load Refs, specialized next):
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
|
||||
|
||||
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without
|
||||
// copying (it requires some extra effort in many cases).
|
||||
template <typename PlainObjectType, typename StrideType>
|
||||
struct type_caster<
|
||||
Eigen::Ref<PlainObjectType, 0, StrideType>,
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
|
||||
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
private:
|
||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||
using props = EigenProps<Type>;
|
||||
using Scalar = typename props::Scalar;
|
||||
static_assert(!std::is_pointer<Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||
using Array
|
||||
= array_t<Scalar,
|
||||
array::forcecast
|
||||
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
|
||||
? array::c_style
|
||||
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
|
||||
? array::f_style
|
||||
: 0)>;
|
||||
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
|
||||
// Delay construction (these have no default constructor)
|
||||
std::unique_ptr<MapType> map;
|
||||
std::unique_ptr<Type> ref;
|
||||
// Our array. When possible, this is just a numpy array pointing to the source data, but
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
|
||||
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
|
||||
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
|
||||
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
|
||||
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
Array copy_or_ref;
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
// First check whether what we have is already an array of the right type. If not, we
|
||||
// can't avoid a copy (because the copy is also going to do type conversion).
|
||||
bool need_copy = !isinstance<Array>(src);
|
||||
|
||||
EigenConformable<props::row_major> fits;
|
||||
if (!need_copy) {
|
||||
// We don't need a converting copy, but we also need to check whether the strides are
|
||||
// compatible with the Ref's stride requirements
|
||||
auto aref = reinterpret_borrow<Array>(src);
|
||||
|
||||
if (aref && (!need_writeable || aref.writeable())) {
|
||||
fits = props::conformable(aref);
|
||||
if (!fits) {
|
||||
return false; // Incompatible dimensions
|
||||
}
|
||||
if (!fits.template stride_compatible<props>()) {
|
||||
need_copy = true;
|
||||
} else {
|
||||
copy_or_ref = std::move(aref);
|
||||
}
|
||||
} else {
|
||||
need_copy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_copy) {
|
||||
// We need to copy: If we need a mutable reference, or we're not supposed to convert
|
||||
// (either because we're in the no-convert overload pass, or because we're explicitly
|
||||
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
|
||||
if (!convert || need_writeable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array copy = Array::ensure(src);
|
||||
if (!copy) {
|
||||
return false;
|
||||
}
|
||||
fits = props::conformable(copy);
|
||||
if (!fits || !fits.template stride_compatible<props>()) {
|
||||
return false;
|
||||
}
|
||||
copy_or_ref = std::move(copy);
|
||||
loader_life_support::add_patient(copy_or_ref);
|
||||
}
|
||||
|
||||
ref.reset();
|
||||
map.reset(new MapType(data(copy_or_ref),
|
||||
fits.rows,
|
||||
fits.cols,
|
||||
make_stride(fits.stride.outer(), fits.stride.inner())));
|
||||
ref.reset(new Type(*map));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type *() { return ref.get(); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &() { return *ref; }
|
||||
template <typename _T>
|
||||
using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
|
||||
Scalar *data(Array &a) {
|
||||
return a.mutable_data();
|
||||
}
|
||||
|
||||
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
|
||||
const Scalar *data(Array &a) {
|
||||
return a.data();
|
||||
}
|
||||
|
||||
// Attempt to figure out a constructor of `Stride` that will work.
|
||||
// If both strides are fixed, use a default constructor:
|
||||
template <typename S>
|
||||
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_default_constructible<S>::value>;
|
||||
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
|
||||
// Eigen::Stride, and use it:
|
||||
template <typename S>
|
||||
using stride_ctor_dual
|
||||
= bool_constant<!stride_ctor_default<S>::value
|
||||
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
|
||||
// it (passing whichever stride is dynamic).
|
||||
template <typename S>
|
||||
using stride_ctor_outer
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_inner
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex) {
|
||||
return S();
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex inner) {
|
||||
return S(outer, inner);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex) {
|
||||
return S(outer);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex inner) {
|
||||
return S(inner);
|
||||
}
|
||||
};
|
||||
|
||||
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
|
||||
// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout).
|
||||
// load() is not supported, but we can cast them into the python domain by first copying to a
|
||||
// regular Eigen::Matrix, then casting that.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
|
||||
protected:
|
||||
using Matrix
|
||||
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||
using props = EigenProps<Matrix>;
|
||||
|
||||
public:
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
handle h = eigen_encapsulate<props>(new Matrix(src));
|
||||
return h;
|
||||
}
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator Type() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = Type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
static_assert(!std::is_pointer<Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||
using Index = typename Type::Index;
|
||||
static constexpr bool rowMajor = Type::IsRowMajor;
|
||||
|
||||
bool load(handle src, bool) {
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obj = reinterpret_borrow<object>(src);
|
||||
object sparse_module = module_::import("scipy.sparse");
|
||||
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
if (!type::handle_of(obj).is(matrix_type)) {
|
||||
try {
|
||||
obj = matrix_type(obj);
|
||||
} catch (const error_already_set &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto values = array_t<Scalar>((object) obj.attr("data"));
|
||||
auto innerIndices = array_t<StorageIndex>((object) obj.attr("indices"));
|
||||
auto outerIndices = array_t<StorageIndex>((object) obj.attr("indptr"));
|
||||
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
|
||||
auto nnz = obj.attr("nnz").cast<Index>();
|
||||
|
||||
if (!values || !innerIndices || !outerIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = EigenMapSparseMatrix<Scalar,
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
std::move(nnz),
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type &>(src).makeCompressed();
|
||||
|
||||
object matrix_type
|
||||
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
array data(src.nonZeros(), src.valuePtr());
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(pybind11::make_tuple(
|
||||
std::move(data), std::move(innerIndices), std::move(outerIndices)),
|
||||
pybind11::make_tuple(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
|
||||
"scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
pybind11/eigen/tensor.h: Transparent conversion for Eigen tensors
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../numpy.h"
|
||||
#include "common.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
|
||||
#endif
|
||||
|
||||
// Disable warnings for Eigen
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4554)
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
#if defined(__MINGW32__)
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
|
||||
#endif
|
||||
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 0),
|
||||
"Eigen Tensor support in pybind11 requires Eigen >= 3.3.0");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline bool is_tensor_aligned(const void *data) {
|
||||
return (reinterpret_cast<std::size_t>(data) % EIGEN_DEFAULT_ALIGN_BYTES) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int compute_array_flag_from_tensor() {
|
||||
static_assert((static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor))
|
||||
|| (static_cast<int>(T::Layout) == static_cast<int>(Eigen::ColMajor)),
|
||||
"Layout must be row or column major");
|
||||
return (static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor)) ? array::c_style
|
||||
: array::f_style;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct eigen_tensor_helper {};
|
||||
|
||||
template <typename Scalar_, int NumIndices_, int Options_, typename IndexType>
|
||||
struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>> {
|
||||
using Type = Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>;
|
||||
using ValidType = void;
|
||||
|
||||
static Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape(const Type &f) {
|
||||
return f.dimensions();
|
||||
}
|
||||
|
||||
static constexpr bool
|
||||
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> & /*shape*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct helper {};
|
||||
|
||||
template <size_t... Is>
|
||||
struct helper<index_sequence<Is...>> {
|
||||
static constexpr auto value = concat(const_name(((void) Is, "?"))...);
|
||||
};
|
||||
|
||||
static constexpr auto dimensions_descriptor
|
||||
= helper<decltype(make_index_sequence<Type::NumIndices>())>::value;
|
||||
|
||||
template <typename... Args>
|
||||
static Type *alloc(Args &&...args) {
|
||||
return new Type(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static void free(Type *tensor) { delete tensor; }
|
||||
};
|
||||
|
||||
template <typename Scalar_, typename std::ptrdiff_t... Indices, int Options_, typename IndexType>
|
||||
struct eigen_tensor_helper<
|
||||
Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>> {
|
||||
using Type = Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>;
|
||||
using ValidType = void;
|
||||
|
||||
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices>
|
||||
get_shape(const Type & /*f*/) {
|
||||
return get_shape();
|
||||
}
|
||||
|
||||
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape() {
|
||||
return Eigen::DSizes<typename Type::Index, Type::NumIndices>(Indices...);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> &shape) {
|
||||
return get_shape() == shape;
|
||||
}
|
||||
|
||||
static constexpr auto dimensions_descriptor = concat(const_name<Indices>()...);
|
||||
|
||||
template <typename... Args>
|
||||
static Type *alloc(Args &&...args) {
|
||||
Eigen::aligned_allocator<Type> allocator;
|
||||
return ::new (allocator.allocate(1)) Type(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static void free(Type *tensor) {
|
||||
Eigen::aligned_allocator<Type> allocator;
|
||||
tensor->~Type();
|
||||
allocator.deallocate(tensor, 1);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
|
||||
struct get_tensor_descriptor {
|
||||
static constexpr auto details
|
||||
= const_name<NeedsWriteable>(", flags.writeable", "")
|
||||
+ const_name<static_cast<int>(Type::Layout) == static_cast<int>(Eigen::RowMajor)>(
|
||||
", flags.c_contiguous", ", flags.f_contiguous");
|
||||
static constexpr auto value
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<typename Type::Scalar>::name
|
||||
+ const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
|
||||
+ const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
|
||||
};
|
||||
|
||||
// When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
|
||||
// function. Falling back to a simple loop works around this issue.
|
||||
//
|
||||
// We need to disable the type-limits warning for the inner loop when size = 0.
|
||||
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wtype-limits")
|
||||
|
||||
template <typename T, int size>
|
||||
std::vector<T> convert_dsizes_to_vector(const Eigen::DSizes<T, size> &arr) {
|
||||
std::vector<T> result(size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
result[i] = arr[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, int size>
|
||||
Eigen::DSizes<T, size> get_shape_for_array(const array &arr) {
|
||||
Eigen::DSizes<T, size> result;
|
||||
const T *shape = arr.shape();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
result[i] = shape[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
template <typename Type>
|
||||
struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
|
||||
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
using Helper = eigen_tensor_helper<Type>;
|
||||
static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
|
||||
PYBIND11_TYPE_CASTER(Type, temp_name);
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!convert) {
|
||||
if (!isinstance<array>(src)) {
|
||||
return false;
|
||||
}
|
||||
array temp = array::ensure(src);
|
||||
if (!temp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!temp.dtype().is(dtype::of<typename Type::Scalar>())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()> arr(
|
||||
reinterpret_borrow<object>(src));
|
||||
|
||||
if (arr.ndim() != Type::NumIndices) {
|
||||
return false;
|
||||
}
|
||||
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
|
||||
|
||||
if (!Helper::is_correct_shape(shape)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
||||
auto data_pointer = arr.data();
|
||||
#else
|
||||
// Handle Eigen bug
|
||||
auto data_pointer = const_cast<typename Type::Scalar *>(arr.data());
|
||||
#endif
|
||||
|
||||
if (is_tensor_aligned(arr.data())) {
|
||||
value = Eigen::TensorMap<const Type, Eigen::Aligned>(data_pointer, shape);
|
||||
} else {
|
||||
value = Eigen::TensorMap<const Type>(data_pointer, shape);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(Type &&src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::reference
|
||||
|| policy == return_value_policy::reference_internal) {
|
||||
pybind11_fail("Cannot use a reference return value policy for an rvalue");
|
||||
}
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
static handle cast(const Type &&src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::reference
|
||||
|| policy == return_value_policy::reference_internal) {
|
||||
pybind11_fail("Cannot use a reference return value policy for an rvalue");
|
||||
}
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
|
||||
object parent_object;
|
||||
bool writeable = false;
|
||||
switch (policy) {
|
||||
case return_value_policy::move:
|
||||
if (std::is_const<C>::value) {
|
||||
pybind11_fail("Cannot move from a constant reference");
|
||||
}
|
||||
|
||||
src = Helper::alloc(std::move(*src));
|
||||
|
||||
parent_object
|
||||
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
|
||||
writeable = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::take_ownership:
|
||||
if (std::is_const<C>::value) {
|
||||
// This cast is ugly, and might be UB in some cases, but we don't have an
|
||||
// alternative here as we must free that memory
|
||||
Helper::free(const_cast<Type *>(src));
|
||||
pybind11_fail("Cannot take ownership of a const reference");
|
||||
}
|
||||
|
||||
parent_object
|
||||
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
|
||||
writeable = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::copy:
|
||||
writeable = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::reference:
|
||||
parent_object = none();
|
||||
writeable = !std::is_const<C>::value;
|
||||
break;
|
||||
|
||||
case return_value_policy::reference_internal:
|
||||
// Default should do the right thing
|
||||
if (!parent) {
|
||||
pybind11_fail("Cannot use reference internal when there is no parent");
|
||||
}
|
||||
parent_object = reinterpret_borrow<object>(parent);
|
||||
writeable = !std::is_const<C>::value;
|
||||
break;
|
||||
|
||||
default:
|
||||
pybind11_fail("pybind11 bug in eigen.h, please file a bug report");
|
||||
}
|
||||
|
||||
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
|
||||
convert_dsizes_to_vector(Helper::get_shape(*src)), src->data(), parent_object);
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return result.release();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename StoragePointerType,
|
||||
bool needs_writeable,
|
||||
enable_if_t<!needs_writeable, bool> = true>
|
||||
StoragePointerType get_array_data_for_type(array &arr) {
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
||||
return reinterpret_cast<StoragePointerType>(arr.data());
|
||||
#else
|
||||
// Handle Eigen bug
|
||||
return reinterpret_cast<StoragePointerType>(const_cast<void *>(arr.data()));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename StoragePointerType,
|
||||
bool needs_writeable,
|
||||
enable_if_t<needs_writeable, bool> = true>
|
||||
StoragePointerType get_array_data_for_type(array &arr) {
|
||||
return reinterpret_cast<StoragePointerType>(arr.mutable_data());
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct get_storage_pointer_type;
|
||||
|
||||
template <typename MapType>
|
||||
struct get_storage_pointer_type<MapType, void_t<typename MapType::StoragePointerType>> {
|
||||
using SPT = typename MapType::StoragePointerType;
|
||||
};
|
||||
|
||||
template <typename MapType>
|
||||
struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType>> {
|
||||
using SPT = typename MapType::PointerArgType;
|
||||
};
|
||||
|
||||
template <typename Type, int Options>
|
||||
struct type_caster<Eigen::TensorMap<Type, Options>,
|
||||
typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
|
||||
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||
using MapType = Eigen::TensorMap<Type, Options>;
|
||||
using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
|
||||
|
||||
bool load(handle src, bool /*convert*/) {
|
||||
// Note that we have a lot more checks here as we want to make sure to avoid copies
|
||||
if (!isinstance<array>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto arr = reinterpret_borrow<array>(src);
|
||||
if ((arr.flags() & compute_array_flag_from_tensor<Type>()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!arr.dtype().is(dtype::of<typename Type::Scalar>())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arr.ndim() != Type::NumIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_aligned = (Options & Eigen::Aligned) != 0;
|
||||
|
||||
if (is_aligned && !is_tensor_aligned(arr.data())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
|
||||
|
||||
if (!Helper::is_correct_shape(shape)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (needs_writeable && !arr.writeable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto result = get_array_data_for_type<typename get_storage_pointer_type<MapType>::SPT,
|
||||
needs_writeable>(arr);
|
||||
|
||||
value.reset(new MapType(std::move(result), std::move(shape)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(MapType &&src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const MapType &&src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(MapType &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(MapType *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const MapType *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
|
||||
object parent_object;
|
||||
constexpr bool writeable = !std::is_const<C>::value;
|
||||
switch (policy) {
|
||||
case return_value_policy::reference:
|
||||
parent_object = none();
|
||||
break;
|
||||
|
||||
case return_value_policy::reference_internal:
|
||||
// Default should do the right thing
|
||||
if (!parent) {
|
||||
pybind11_fail("Cannot use reference internal when there is no parent");
|
||||
}
|
||||
parent_object = reinterpret_borrow<object>(parent);
|
||||
break;
|
||||
|
||||
case return_value_policy::take_ownership:
|
||||
delete src;
|
||||
// fallthrough
|
||||
default:
|
||||
// move, take_ownership don't make any sense for a ref/map:
|
||||
pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "
|
||||
"reference or reference_internal");
|
||||
}
|
||||
|
||||
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
|
||||
convert_dsizes_to_vector(Helper::get_shape(*src)),
|
||||
src->data(),
|
||||
std::move(parent_object));
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return result.release();
|
||||
}
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
||||
|
||||
static constexpr bool needs_writeable = !std::is_const<typename std::remove_pointer<
|
||||
typename get_storage_pointer_type<MapType>::SPT>::type>::value;
|
||||
#else
|
||||
// Handle Eigen bug
|
||||
static constexpr bool needs_writeable = !std::is_const<Type>::value;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// TODO: Move to std::optional once std::optional has more support
|
||||
std::unique_ptr<MapType> value;
|
||||
|
||||
public:
|
||||
static constexpr auto name = get_tensor_descriptor<Type, true, needs_writeable>::value;
|
||||
explicit operator MapType *() { return value.get(); }
|
||||
explicit operator MapType &() { return *value; }
|
||||
explicit operator MapType &&() && { return std::move(*value); }
|
||||
|
||||
template <typename T_>
|
||||
using cast_op_type = ::pybind11::detail::movable_cast_op_type<T_>;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
@ -86,38 +86,26 @@ inline wchar_t *widen_chars(const char *safe_arg) {
|
|||
return widened_arg;
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/** \rst
|
||||
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
|
||||
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
|
||||
optional `init_signal_handlers` parameter can be used to skip the registration of
|
||||
signal handlers (see the `Python documentation`_ for details). Calling this function
|
||||
again after the interpreter has already been initialized is a fatal error.
|
||||
|
||||
If initializing the Python interpreter fails, then the program is terminated. (This
|
||||
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
|
||||
of throwing exceptions on errors.)
|
||||
|
||||
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
|
||||
used to populate ``sys.argv`` and ``sys.path``.
|
||||
See the |PySys_SetArgvEx documentation|_ for details.
|
||||
|
||||
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
|
||||
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
|
||||
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
inline void precheck_interpreter() {
|
||||
if (Py_IsInitialized() != 0) {
|
||||
pybind11_fail("The interpreter is already running");
|
||||
}
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX < 0x030B0000
|
||||
#if !defined(PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX)
|
||||
# define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000)
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
|
||||
int argc,
|
||||
const char *const *argv,
|
||||
bool add_program_dir_to_path) {
|
||||
detail::precheck_interpreter();
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
|
||||
PyEval_InitThreads();
|
||||
# endif
|
||||
|
||||
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||
|
@ -147,24 +135,30 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||
auto *pysys_argv = widened_argv.get();
|
||||
|
||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
||||
#else
|
||||
PyConfig config;
|
||||
PyConfig_InitIsolatedConfig(&config);
|
||||
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast<char *const *>(argv));
|
||||
if (PyStatus_Exception(status)) {
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
inline void initialize_interpreter(PyConfig *config,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
detail::precheck_interpreter();
|
||||
PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast<char *const *>(argv));
|
||||
if (PyStatus_Exception(status) != 0) {
|
||||
// A failure here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
PyConfig_Clear(&config);
|
||||
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg
|
||||
: "Failed to prepare CPython");
|
||||
PyConfig_Clear(config);
|
||||
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
|
||||
: "Failed to prepare CPython");
|
||||
}
|
||||
status = Py_InitializeFromConfig(&config);
|
||||
PyConfig_Clear(&config);
|
||||
if (PyStatus_Exception(status)) {
|
||||
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg
|
||||
: "Failed to init CPython");
|
||||
status = Py_InitializeFromConfig(config);
|
||||
if (PyStatus_Exception(status) != 0) {
|
||||
PyConfig_Clear(config);
|
||||
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
|
||||
: "Failed to init CPython");
|
||||
}
|
||||
if (add_program_dir_to_path) {
|
||||
PyRun_SimpleString("import sys, os.path; "
|
||||
|
@ -172,6 +166,44 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||
"os.path.abspath(os.path.dirname(sys.argv[0])) "
|
||||
"if sys.argv and os.path.exists(sys.argv[0]) else '')");
|
||||
}
|
||||
PyConfig_Clear(config);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
|
||||
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
|
||||
optional `init_signal_handlers` parameter can be used to skip the registration of
|
||||
signal handlers (see the `Python documentation`_ for details). Calling this function
|
||||
again after the interpreter has already been initialized is a fatal error.
|
||||
|
||||
If initializing the Python interpreter fails, then the program is terminated. (This
|
||||
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
|
||||
of throwing exceptions on errors.)
|
||||
|
||||
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
|
||||
used to populate ``sys.argv`` and ``sys.path``.
|
||||
See the |PySys_SetArgvEx documentation|_ for details.
|
||||
|
||||
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
|
||||
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
|
||||
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
detail::initialize_interpreter_pre_pyconfig(
|
||||
init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||
#else
|
||||
PyConfig config;
|
||||
PyConfig_InitPythonConfig(&config);
|
||||
// See PR #4473 for background
|
||||
config.parse_argv = 0;
|
||||
|
||||
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -211,16 +243,14 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||
|
||||
\endrst */
|
||||
inline void finalize_interpreter() {
|
||||
handle builtins(PyEval_GetBuiltins());
|
||||
const char *id = PYBIND11_INTERNALS_ID;
|
||||
|
||||
// Get the internals pointer (without creating it if it doesn't exist). It's possible for the
|
||||
// internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()`
|
||||
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
|
||||
detail::internals **internals_ptr_ptr = detail::get_internals_pp();
|
||||
// It could also be stashed in builtins, so look there too:
|
||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
|
||||
internals_ptr_ptr = capsule(builtins[id]);
|
||||
// It could also be stashed in state_dict, so look there too:
|
||||
if (object internals_obj
|
||||
= get_internals_obj_from_state_dict(detail::get_python_state_dict())) {
|
||||
internals_ptr_ptr = detail::get_internals_pp_from_capsule(internals_obj);
|
||||
}
|
||||
// Local internals contains data managed by the current interpreter, so we must clear them to
|
||||
// avoid undefined behaviors when initializing another interpreter
|
||||
|
@ -259,6 +289,15 @@ public:
|
|||
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
explicit scoped_interpreter(PyConfig *config,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
initialize_interpreter(config, argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
scoped_interpreter(const scoped_interpreter &) = delete;
|
||||
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
|
||||
scoped_interpreter &operator=(const scoped_interpreter &) = delete;
|
||||
|
|
|
@ -48,9 +48,16 @@ public:
|
|||
*/
|
||||
if (auto cfunc = func.cpp_function()) {
|
||||
auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr());
|
||||
if (isinstance<capsule>(cfunc_self)) {
|
||||
if (cfunc_self == nullptr) {
|
||||
PyErr_Clear();
|
||||
} else if (isinstance<capsule>(cfunc_self)) {
|
||||
auto c = reinterpret_borrow<capsule>(cfunc_self);
|
||||
auto *rec = (function_record *) c;
|
||||
|
||||
function_record *rec = nullptr;
|
||||
// Check that we can safely reinterpret the capsule into a function_record
|
||||
if (detail::is_function_record_capsule(c)) {
|
||||
rec = c.get_pointer<function_record>();
|
||||
}
|
||||
|
||||
while (rec != nullptr) {
|
||||
if (rec->is_stateless
|
||||
|
@ -110,7 +117,7 @@ public:
|
|||
template <typename Func>
|
||||
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
|
||||
if (!f_) {
|
||||
return none().inc_ref();
|
||||
return none().release();
|
||||
}
|
||||
|
||||
auto result = f_.template target<function_type>();
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "detail/internals.h"
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# include "detail/internals.h"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
|
@ -21,7 +24,9 @@ PyThreadState *get_thread_state_unchecked();
|
|||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
#if defined(WITH_THREAD)
|
||||
|
||||
# if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
|
||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||
* pattern, but there are a few important differences:
|
||||
|
@ -62,11 +67,11 @@ public:
|
|||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (!tstate) {
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
tstate->gilstate_counter = 0;
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||
} else {
|
||||
|
@ -80,24 +85,27 @@ public:
|
|||
inc_ref();
|
||||
}
|
||||
|
||||
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
|
||||
|
||||
void inc_ref() { ++tstate->gilstate_counter; }
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (detail::get_thread_state_unchecked() != tstate) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
}
|
||||
if (tstate->gilstate_counter < 0) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (!release) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
PyThreadState_Clear(tstate);
|
||||
if (active) {
|
||||
PyThreadState_DeleteCurrent();
|
||||
|
@ -144,6 +152,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
gil_scoped_release(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
|
||||
|
||||
/// This method will disable the PyThreadState_DeleteCurrent call and the
|
||||
/// GIL won't be acquired. This method should be used if the interpreter
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
|
@ -172,12 +183,16 @@ private:
|
|||
bool disassoc;
|
||||
bool active = true;
|
||||
};
|
||||
#elif defined(PYPY_VERSION)
|
||||
|
||||
# else // PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
|
||||
class gil_scoped_acquire {
|
||||
PyGILState_STATE state;
|
||||
|
||||
public:
|
||||
gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
||||
gil_scoped_acquire() : state{PyGILState_Ensure()} {}
|
||||
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
|
||||
~gil_scoped_acquire() { PyGILState_Release(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
|
@ -186,17 +201,39 @@ class gil_scoped_release {
|
|||
PyThreadState *state;
|
||||
|
||||
public:
|
||||
gil_scoped_release() { state = PyEval_SaveThread(); }
|
||||
gil_scoped_release() : state{PyEval_SaveThread()} {}
|
||||
gil_scoped_release(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
|
||||
~gil_scoped_release() { PyEval_RestoreThread(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
#else
|
||||
|
||||
# endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
|
||||
#else // WITH_THREAD
|
||||
|
||||
class gil_scoped_acquire {
|
||||
public:
|
||||
gil_scoped_acquire() {
|
||||
// Trick to suppress `unused variable` error messages (at call sites).
|
||||
(void) (this != (this + 1));
|
||||
}
|
||||
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
|
||||
void disarm() {}
|
||||
};
|
||||
|
||||
class gil_scoped_release {
|
||||
public:
|
||||
gil_scoped_release() {
|
||||
// Trick to suppress `unused variable` error messages (at call sites).
|
||||
(void) (this != (this + 1));
|
||||
}
|
||||
gil_scoped_release(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
|
||||
void disarm() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // WITH_THREAD
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -36,6 +36,8 @@ static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
class array; // Forward declaration
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
@ -537,7 +539,7 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
|
||||
class dtype : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_);
|
||||
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_)
|
||||
|
||||
explicit dtype(const buffer_info &info) {
|
||||
dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
|
||||
|
@ -562,6 +564,8 @@ public:
|
|||
m_ptr = from_args(args).release().ptr();
|
||||
}
|
||||
|
||||
/// Return dtype for the given typenum (one of the NPY_TYPES).
|
||||
/// https://numpy.org/devdocs/reference/c-api/array.html#c.PyArray_DescrFromType
|
||||
explicit dtype(int typenum)
|
||||
: object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
|
||||
if (m_ptr == nullptr) {
|
||||
|
@ -875,7 +879,7 @@ public:
|
|||
*/
|
||||
template <typename T, ssize_t Dims = -1>
|
||||
detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims) {
|
||||
if (Dims >= 0 && ndim() != Dims) {
|
||||
throw std::domain_error("array has incorrect number of dimensions: "
|
||||
+ std::to_string(ndim()) + "; expected "
|
||||
+ std::to_string(Dims));
|
||||
|
@ -893,7 +897,7 @@ public:
|
|||
*/
|
||||
template <typename T, ssize_t Dims = -1>
|
||||
detail::unchecked_reference<T, Dims> unchecked() const & {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims) {
|
||||
if (Dims >= 0 && ndim() != Dims) {
|
||||
throw std::domain_error("array has incorrect number of dimensions: "
|
||||
+ std::to_string(ndim()) + "; expected "
|
||||
+ std::to_string(Dims));
|
||||
|
@ -1119,10 +1123,10 @@ public:
|
|||
|
||||
/**
|
||||
* Returns a proxy object that provides const access to the array's data without bounds or
|
||||
* dimensionality checking. Unlike `unchecked()`, this does not require that the underlying
|
||||
* array have the `writable` flag. Use with care: the array must not be destroyed or reshaped
|
||||
* for the duration of the returned object, and the caller must take care not to access invalid
|
||||
* dimensions or dimension indices.
|
||||
* dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the
|
||||
* underlying array have the `writable` flag. Use with care: the array must not be destroyed
|
||||
* or reshaped for the duration of the returned object, and the caller must take care not to
|
||||
* access invalid dimensions or dimension indices.
|
||||
*/
|
||||
template <ssize_t Dims = -1>
|
||||
detail::unchecked_reference<T, Dims> unchecked() const & {
|
||||
|
@ -1281,12 +1285,16 @@ private:
|
|||
public:
|
||||
static constexpr int value = values[detail::is_fmt_numeric<T>::index];
|
||||
|
||||
static pybind11::dtype dtype() {
|
||||
if (auto *ptr = npy_api::get().PyArray_DescrFromType_(value)) {
|
||||
return reinterpret_steal<pybind11::dtype>(ptr);
|
||||
}
|
||||
pybind11_fail("Unsupported buffer format!");
|
||||
}
|
||||
static pybind11::dtype dtype() { return pybind11::dtype(/*typenum*/ value); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor<T, enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value>> {
|
||||
static constexpr auto name = const_name("object");
|
||||
|
||||
static constexpr int value = npy_api::NPY_OBJECT_;
|
||||
|
||||
static pybind11::dtype dtype() { return pybind11::dtype(/*typenum*/ value); }
|
||||
};
|
||||
|
||||
#define PYBIND11_DECL_CHAR_FMT \
|
||||
|
@ -1401,7 +1409,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
|||
oss << '}';
|
||||
auto format_str = oss.str();
|
||||
|
||||
// Sanity check: verify that NumPy properly parses our buffer format string
|
||||
// Smoke test: verify that NumPy properly parses our buffer format string
|
||||
auto &api = npy_api::get();
|
||||
auto arr = array(buffer_info(nullptr, itemsize, format_str, 1));
|
||||
if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) {
|
||||
|
@ -1469,7 +1477,7 @@ private:
|
|||
}
|
||||
|
||||
// Extract name, offset and format descriptor for a struct field
|
||||
# define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, # Field)
|
||||
# define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field)
|
||||
|
||||
// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro
|
||||
// (C) William Swanson, Paul Fultz
|
||||
|
@ -1866,8 +1874,13 @@ private:
|
|||
|
||||
auto result = returned_array::create(trivial, shape);
|
||||
|
||||
PYBIND11_WARNING_PUSH
|
||||
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
|
||||
#endif
|
||||
|
||||
if (size == 0) {
|
||||
return std::move(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Call the function */
|
||||
|
@ -1878,7 +1891,8 @@ private:
|
|||
apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq);
|
||||
}
|
||||
|
||||
return std::move(result);
|
||||
return result;
|
||||
PYBIND11_WARNING_POP
|
||||
}
|
||||
|
||||
template <size_t... Index, size_t... VIndex, size_t... BIndex>
|
||||
|
|
|
@ -84,6 +84,7 @@ struct op_impl {};
|
|||
/// Operator implementation generator
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
struct op_ {
|
||||
static constexpr bool op_enable_if_hook = true;
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) const {
|
||||
using Base = typename Class::type;
|
||||
|
|
|
@ -47,6 +47,16 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
options &disable_enum_members_docstring() & {
|
||||
global_state().show_enum_members_docstring = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
options &enable_enum_members_docstring() & {
|
||||
global_state().show_enum_members_docstring = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Getter methods (return the global state):
|
||||
|
||||
static bool show_user_defined_docstrings() {
|
||||
|
@ -55,6 +65,10 @@ public:
|
|||
|
||||
static bool show_function_signatures() { return global_state().show_function_signatures; }
|
||||
|
||||
static bool show_enum_members_docstring() {
|
||||
return global_state().show_enum_members_docstring;
|
||||
}
|
||||
|
||||
// This type is not meant to be allocated on the heap.
|
||||
void *operator new(size_t) = delete;
|
||||
|
||||
|
@ -63,6 +77,8 @@ private:
|
|||
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
|
||||
bool show_function_signatures = true; //< Include auto-generated function signatures
|
||||
// in docstrings.
|
||||
bool show_enum_members_docstring = true; //< Include auto-generated member list in enum
|
||||
// docstrings.
|
||||
};
|
||||
|
||||
static state &global_state() {
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
# include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/* https://stackoverflow.com/questions/46798456/handling-gccs-noexcept-type-warning
|
||||
This warning is about ABI compatibility, not code health.
|
||||
It is only actually needed in a couple places, but apparently GCC 7 "generates this warning if
|
||||
|
@ -43,11 +45,10 @@
|
|||
No other GCC version generates this warning.
|
||||
*/
|
||||
#if defined(__GNUC__) && __GNUC__ == 7
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnoexcept-type"
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wnoexcept-type")
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
|
@ -83,6 +84,7 @@ public:
|
|||
cpp_function() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
cpp_function(std::nullptr_t) {}
|
||||
cpp_function(std::nullptr_t, const is_setter &) {}
|
||||
|
||||
/// Construct a cpp_function from a vanilla function pointer
|
||||
template <typename Return, typename... Args, typename... Extra>
|
||||
|
@ -177,22 +179,22 @@ protected:
|
|||
auto *rec = unique_rec.get();
|
||||
|
||||
/* Store the capture object directly in the function record if there is enough space */
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(capture) <= sizeof(rec->data))) {
|
||||
if (sizeof(capture) <= sizeof(rec->data)) {
|
||||
/* Without these pragmas, GCC warns that there might not be
|
||||
enough space to use the placement new operator. However, the
|
||||
'if' statement above ensures that this is the case. */
|
||||
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wplacement-new"
|
||||
PYBIND11_WARNING_PUSH
|
||||
|
||||
#if defined(__GNUG__) && __GNUC__ >= 6
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wplacement-new")
|
||||
#endif
|
||||
|
||||
new ((capture *) &rec->data) capture{std::forward<Func>(f)};
|
||||
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
|
||||
#if !PYBIND11_HAS_STD_LAUNDER
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
|
||||
#endif
|
||||
|
||||
// UB without std::launder, but without breaking ABI and/or
|
||||
// a significant refactoring it's "impossible" to solve.
|
||||
if (!std::is_trivially_destructible<capture>::value) {
|
||||
|
@ -202,9 +204,7 @@ protected:
|
|||
data->~capture();
|
||||
};
|
||||
}
|
||||
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
PYBIND11_WARNING_POP
|
||||
} else {
|
||||
rec->data[0] = new capture{std::forward<Func>(f)};
|
||||
rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); };
|
||||
|
@ -245,10 +245,16 @@ protected:
|
|||
using Guard = extract_guard_t<Extra...>;
|
||||
|
||||
/* Perform the function call */
|
||||
handle result
|
||||
= cast_out::cast(std::move(args_converter).template call<Return, Guard>(cap->f),
|
||||
policy,
|
||||
call.parent);
|
||||
handle result;
|
||||
if (call.func.is_setter) {
|
||||
(void) std::move(args_converter).template call<Return, Guard>(cap->f);
|
||||
result = none().release();
|
||||
} else {
|
||||
result = cast_out::cast(
|
||||
std::move(args_converter).template call<Return, Guard>(cap->f),
|
||||
policy,
|
||||
call.parent);
|
||||
}
|
||||
|
||||
/* Invoke call policy post-call hook */
|
||||
process_attributes<Extra...>::postcall(call, result);
|
||||
|
@ -468,13 +474,20 @@ protected:
|
|||
if (rec->sibling) {
|
||||
if (PyCFunction_Check(rec->sibling.ptr())) {
|
||||
auto *self = PyCFunction_GET_SELF(rec->sibling.ptr());
|
||||
capsule rec_capsule = isinstance<capsule>(self) ? reinterpret_borrow<capsule>(self)
|
||||
: capsule(self);
|
||||
chain = (detail::function_record *) rec_capsule;
|
||||
/* Never append a method to an overload chain of a parent class;
|
||||
instead, hide the parent's overloads in this case */
|
||||
if (!chain->scope.is(rec->scope)) {
|
||||
if (!isinstance<capsule>(self)) {
|
||||
chain = nullptr;
|
||||
} else {
|
||||
auto rec_capsule = reinterpret_borrow<capsule>(self);
|
||||
if (detail::is_function_record_capsule(rec_capsule)) {
|
||||
chain = rec_capsule.get_pointer<detail::function_record>();
|
||||
/* Never append a method to an overload chain of a parent class;
|
||||
instead, hide the parent's overloads in this case */
|
||||
if (!chain->scope.is(rec->scope)) {
|
||||
chain = nullptr;
|
||||
}
|
||||
} else {
|
||||
chain = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't trigger for things like the default __init__, which are wrapper_descriptors
|
||||
|
@ -495,6 +508,7 @@ protected:
|
|||
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
||||
|
||||
capsule rec_capsule(unique_rec.release(),
|
||||
detail::get_function_record_capsule_name(),
|
||||
[](void *ptr) { destruct((detail::function_record *) ptr); });
|
||||
guarded_strdup.release();
|
||||
|
||||
|
@ -661,10 +675,13 @@ protected:
|
|||
/// Main dispatch logic for calls to functions bound using pybind11
|
||||
static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) {
|
||||
using namespace detail;
|
||||
assert(isinstance<capsule>(self));
|
||||
|
||||
/* Iterator over the list of potentially admissible overloads */
|
||||
const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr),
|
||||
const function_record *overloads = reinterpret_cast<function_record *>(
|
||||
PyCapsule_GetPointer(self, get_function_record_capsule_name())),
|
||||
*it = overloads;
|
||||
assert(overloads != nullptr);
|
||||
|
||||
/* Need to know how many arguments + keyword arguments there are to pick the right
|
||||
overload */
|
||||
|
@ -1416,9 +1433,9 @@ template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
|
|||
void call_operator_delete(T *p, size_t, size_t) {
|
||||
T::operator delete(p);
|
||||
}
|
||||
template <
|
||||
typename T,
|
||||
enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0>
|
||||
template <typename T,
|
||||
enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int>
|
||||
= 0>
|
||||
void call_operator_delete(T *p, size_t s, size_t) {
|
||||
T::operator delete(p, s);
|
||||
}
|
||||
|
@ -1578,14 +1595,14 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
template <typename T, typename... Extra, detail::enable_if_t<T::op_enable_if_hook, int> = 0>
|
||||
class_ &def(const T &op, const Extra &...extra) {
|
||||
op.execute(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
template <typename T, typename... Extra, detail::enable_if_t<T::op_enable_if_hook, int> = 0>
|
||||
class_ &def_cast(const T &op, const Extra &...extra) {
|
||||
op.execute_cast(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
@ -1719,7 +1736,8 @@ public:
|
|||
template <typename Getter, typename Setter, typename... Extra>
|
||||
class_ &
|
||||
def_property(const char *name, const Getter &fget, const Setter &fset, const Extra &...extra) {
|
||||
return def_property(name, fget, cpp_function(method_adaptor<type>(fset)), extra...);
|
||||
return def_property(
|
||||
name, fget, cpp_function(method_adaptor<type>(fset), is_setter()), extra...);
|
||||
}
|
||||
template <typename Getter, typename... Extra>
|
||||
class_ &def_property(const char *name,
|
||||
|
@ -1830,8 +1848,7 @@ private:
|
|||
if (holder_ptr) {
|
||||
init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible<holder_type>());
|
||||
v_h.set_holder_constructed();
|
||||
} else if (PYBIND11_SILENCE_MSVC_C4127(detail::always_construct_holder<holder_type>::value)
|
||||
|| inst->owned) {
|
||||
} else if (detail::always_construct_holder<holder_type>::value || inst->owned) {
|
||||
new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());
|
||||
v_h.set_holder_constructed();
|
||||
}
|
||||
|
@ -1871,9 +1888,22 @@ private:
|
|||
|
||||
static detail::function_record *get_function_record(handle h) {
|
||||
h = detail::get_function(h);
|
||||
return h ? (detail::function_record *) reinterpret_borrow<capsule>(
|
||||
PyCFunction_GET_SELF(h.ptr()))
|
||||
: nullptr;
|
||||
if (!h) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
handle func_self = PyCFunction_GET_SELF(h.ptr());
|
||||
if (!func_self) {
|
||||
throw error_already_set();
|
||||
}
|
||||
if (!isinstance<capsule>(func_self)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto cap = reinterpret_borrow<capsule>(func_self);
|
||||
if (!detail::is_function_record_capsule(cap)) {
|
||||
return nullptr;
|
||||
}
|
||||
return cap.get_pointer<detail::function_record>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1950,29 +1980,35 @@ struct enum_base {
|
|||
name("name"),
|
||||
is_method(m_base));
|
||||
|
||||
m_base.attr("__doc__") = static_property(
|
||||
cpp_function(
|
||||
[](handle arg) -> std::string {
|
||||
std::string docstring;
|
||||
dict entries = arg.attr("__entries");
|
||||
if (((PyTypeObject *) arg.ptr())->tp_doc) {
|
||||
docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n";
|
||||
}
|
||||
docstring += "Members:";
|
||||
for (auto kv : entries) {
|
||||
auto key = std::string(pybind11::str(kv.first));
|
||||
auto comment = kv.second[int_(1)];
|
||||
docstring += "\n\n " + key;
|
||||
if (!comment.is_none()) {
|
||||
docstring += " : " + (std::string) pybind11::str(comment);
|
||||
if (options::show_enum_members_docstring()) {
|
||||
m_base.attr("__doc__") = static_property(
|
||||
cpp_function(
|
||||
[](handle arg) -> std::string {
|
||||
std::string docstring;
|
||||
dict entries = arg.attr("__entries");
|
||||
if (((PyTypeObject *) arg.ptr())->tp_doc) {
|
||||
docstring += std::string(
|
||||
reinterpret_cast<PyTypeObject *>(arg.ptr())->tp_doc);
|
||||
docstring += "\n\n";
|
||||
}
|
||||
}
|
||||
return docstring;
|
||||
},
|
||||
name("__doc__")),
|
||||
none(),
|
||||
none(),
|
||||
"");
|
||||
docstring += "Members:";
|
||||
for (auto kv : entries) {
|
||||
auto key = std::string(pybind11::str(kv.first));
|
||||
auto comment = kv.second[int_(1)];
|
||||
docstring += "\n\n ";
|
||||
docstring += key;
|
||||
if (!comment.is_none()) {
|
||||
docstring += " : ";
|
||||
docstring += pybind11::str(comment).cast<std::string>();
|
||||
}
|
||||
}
|
||||
return docstring;
|
||||
},
|
||||
name("__doc__")),
|
||||
none(),
|
||||
none(),
|
||||
"");
|
||||
}
|
||||
|
||||
m_base.attr("__members__") = static_property(cpp_function(
|
||||
[](handle arg) -> dict {
|
||||
|
@ -2073,7 +2109,7 @@ struct enum_base {
|
|||
+ "\" already exists!");
|
||||
}
|
||||
|
||||
entries[name] = std::make_pair(value, doc);
|
||||
entries[name] = pybind11::make_tuple(value, doc);
|
||||
m_base.attr(std::move(name)) = std::move(value);
|
||||
}
|
||||
|
||||
|
@ -2333,7 +2369,7 @@ template <typename Access,
|
|||
typename Sentinel,
|
||||
typename ValueType,
|
||||
typename... Extra>
|
||||
iterator make_iterator_impl(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
using state = detail::iterator_state<Access, Policy, Iterator, Sentinel, ValueType, Extra...>;
|
||||
// TODO: state captures only the types of Extra, not the values
|
||||
|
||||
|
@ -2359,7 +2395,7 @@ iterator make_iterator_impl(Iterator &&first, Sentinel &&last, Extra &&...extra)
|
|||
Policy);
|
||||
}
|
||||
|
||||
return cast(state{std::forward<Iterator>(first), std::forward<Sentinel>(last), true});
|
||||
return cast(state{first, last, true});
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
@ -2370,15 +2406,13 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
|||
typename Sentinel,
|
||||
typename ValueType = typename detail::iterator_access<Iterator>::result_type,
|
||||
typename... Extra>
|
||||
iterator make_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
iterator make_iterator(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
return detail::make_iterator_impl<detail::iterator_access<Iterator>,
|
||||
Policy,
|
||||
Iterator,
|
||||
Sentinel,
|
||||
ValueType,
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
Extra...>(first, last, std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes a python iterator over the keys (`.first`) of a iterator over pairs from a
|
||||
|
@ -2388,15 +2422,13 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
|||
typename Sentinel,
|
||||
typename KeyType = typename detail::iterator_key_access<Iterator>::result_type,
|
||||
typename... Extra>
|
||||
iterator make_key_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
return detail::make_iterator_impl<detail::iterator_key_access<Iterator>,
|
||||
Policy,
|
||||
Iterator,
|
||||
Sentinel,
|
||||
KeyType,
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
Extra...>(first, last, std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes a python iterator over the values (`.second`) of a iterator over pairs from a
|
||||
|
@ -2406,15 +2438,13 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
|||
typename Sentinel,
|
||||
typename ValueType = typename detail::iterator_value_access<Iterator>::result_type,
|
||||
typename... Extra>
|
||||
iterator make_value_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
return detail::make_iterator_impl<detail::iterator_value_access<Iterator>,
|
||||
Policy,
|
||||
Iterator,
|
||||
Sentinel,
|
||||
ValueType,
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
Extra...>(first, last, std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes an iterator over values of an stl container or other container supporting
|
||||
|
@ -2858,7 +2888,3 @@ inline function get_overload(const T *this_ptr, const char *name) {
|
|||
PYBIND11_OVERRIDE_PURE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__);
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ == 7
|
||||
# pragma GCC diagnostic pop // -Wnoexcept-type
|
||||
#endif
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
/* A few forward declarations */
|
||||
class handle;
|
||||
class object;
|
||||
|
@ -155,23 +157,23 @@ public:
|
|||
object operator-() const;
|
||||
object operator~() const;
|
||||
object operator+(object_api const &other) const;
|
||||
object operator+=(object_api const &other) const;
|
||||
object operator+=(object_api const &other);
|
||||
object operator-(object_api const &other) const;
|
||||
object operator-=(object_api const &other) const;
|
||||
object operator-=(object_api const &other);
|
||||
object operator*(object_api const &other) const;
|
||||
object operator*=(object_api const &other) const;
|
||||
object operator*=(object_api const &other);
|
||||
object operator/(object_api const &other) const;
|
||||
object operator/=(object_api const &other) const;
|
||||
object operator/=(object_api const &other);
|
||||
object operator|(object_api const &other) const;
|
||||
object operator|=(object_api const &other) const;
|
||||
object operator|=(object_api const &other);
|
||||
object operator&(object_api const &other) const;
|
||||
object operator&=(object_api const &other) const;
|
||||
object operator&=(object_api const &other);
|
||||
object operator^(object_api const &other) const;
|
||||
object operator^=(object_api const &other) const;
|
||||
object operator^=(object_api const &other);
|
||||
object operator<<(object_api const &other) const;
|
||||
object operator<<=(object_api const &other) const;
|
||||
object operator<<=(object_api const &other);
|
||||
object operator>>(object_api const &other) const;
|
||||
object operator>>=(object_api const &other) const;
|
||||
object operator>>=(object_api const &other);
|
||||
|
||||
PYBIND11_DEPRECATED("Use py::str(obj) instead")
|
||||
pybind11::str str() const;
|
||||
|
@ -230,7 +232,8 @@ public:
|
|||
detail::enable_if_t<detail::all_of<detail::none_of<std::is_base_of<handle, T>,
|
||||
detail::is_pyobj_ptr_or_nullptr_t<T>>,
|
||||
std::is_convertible<T, PyObject *>>::value,
|
||||
int> = 0>
|
||||
int>
|
||||
= 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
handle(T &obj) : m_ptr(obj) {}
|
||||
|
||||
|
@ -246,6 +249,11 @@ public:
|
|||
const handle &inc_ref() const & {
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
inc_ref_counter(1);
|
||||
#endif
|
||||
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||
if (m_ptr != nullptr && !PyGILState_Check()) {
|
||||
throw_gilstate_error("pybind11::handle::inc_ref()");
|
||||
}
|
||||
#endif
|
||||
Py_XINCREF(m_ptr);
|
||||
return *this;
|
||||
|
@ -257,6 +265,11 @@ public:
|
|||
this function automatically. Returns a reference to itself.
|
||||
\endrst */
|
||||
const handle &dec_ref() const & {
|
||||
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||
if (m_ptr != nullptr && !PyGILState_Check()) {
|
||||
throw_gilstate_error("pybind11::handle::dec_ref()");
|
||||
}
|
||||
#endif
|
||||
Py_XDECREF(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
@ -283,8 +296,33 @@ public:
|
|||
protected:
|
||||
PyObject *m_ptr = nullptr;
|
||||
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
private:
|
||||
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||
void throw_gilstate_error(const std::string &function_name) const {
|
||||
fprintf(
|
||||
stderr,
|
||||
"%s is being called while the GIL is either not held or invalid. Please see "
|
||||
"https://pybind11.readthedocs.io/en/stable/advanced/"
|
||||
"misc.html#common-sources-of-global-interpreter-lock-errors for debugging advice.\n"
|
||||
"If you are convinced there is no bug in your code, you can #define "
|
||||
"PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF"
|
||||
"to disable this check. In that case you have to ensure this #define is consistently "
|
||||
"used for all translation units linked into a given pybind11 extension, otherwise "
|
||||
"there will be ODR violations.",
|
||||
function_name.c_str());
|
||||
fflush(stderr);
|
||||
if (Py_TYPE(m_ptr)->tp_name != nullptr) {
|
||||
fprintf(stderr,
|
||||
"The failing %s call was triggered on a %s object.\n",
|
||||
function_name.c_str(),
|
||||
Py_TYPE(m_ptr)->tp_name);
|
||||
fflush(stderr);
|
||||
}
|
||||
throw std::runtime_error(function_name + " PyGILState_Check() failure.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
static std::size_t inc_ref_counter(std::size_t add) {
|
||||
thread_local std::size_t counter = 0;
|
||||
counter += add;
|
||||
|
@ -334,12 +372,15 @@ public:
|
|||
}
|
||||
|
||||
object &operator=(const object &other) {
|
||||
other.inc_ref();
|
||||
// Use temporary variable to ensure `*this` remains valid while
|
||||
// `Py_XDECREF` executes, in case `*this` is accessible from Python.
|
||||
handle temp(m_ptr);
|
||||
m_ptr = other.m_ptr;
|
||||
temp.dec_ref();
|
||||
// Skip inc_ref and dec_ref if both objects are the same
|
||||
if (!this->is(other)) {
|
||||
other.inc_ref();
|
||||
// Use temporary variable to ensure `*this` remains valid while
|
||||
// `Py_XDECREF` executes, in case `*this` is accessible from Python.
|
||||
handle temp(m_ptr);
|
||||
m_ptr = other.m_ptr;
|
||||
temp.dec_ref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -353,6 +394,20 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
#define PYBIND11_INPLACE_OP(iop) \
|
||||
object iop(object_api const &other) { return operator=(handle::iop(other)); }
|
||||
|
||||
PYBIND11_INPLACE_OP(operator+=)
|
||||
PYBIND11_INPLACE_OP(operator-=)
|
||||
PYBIND11_INPLACE_OP(operator*=)
|
||||
PYBIND11_INPLACE_OP(operator/=)
|
||||
PYBIND11_INPLACE_OP(operator|=)
|
||||
PYBIND11_INPLACE_OP(operator&=)
|
||||
PYBIND11_INPLACE_OP(operator^=)
|
||||
PYBIND11_INPLACE_OP(operator<<=)
|
||||
PYBIND11_INPLACE_OP(operator>>=)
|
||||
#undef PYBIND11_INPLACE_OP
|
||||
|
||||
// Calling cast() on an object lvalue just copies (via handle::cast)
|
||||
template <typename T>
|
||||
T cast() const &;
|
||||
|
@ -413,7 +468,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
|
||||
// Equivalent to obj.__class__.__name__ (or obj.__name__ if obj is a class).
|
||||
inline const char *obj_class_name(PyObject *obj) {
|
||||
if (Py_TYPE(obj) == &PyType_Type) {
|
||||
if (PyType_Check(obj)) {
|
||||
return reinterpret_cast<PyTypeObject *>(obj)->tp_name;
|
||||
}
|
||||
return Py_TYPE(obj)->tp_name;
|
||||
|
@ -421,13 +476,24 @@ inline const char *obj_class_name(PyObject *obj) {
|
|||
|
||||
std::string error_string();
|
||||
|
||||
// The code in this struct is very unusual, to minimize the chances of
|
||||
// masking bugs (elsewhere) by errors during the error handling (here).
|
||||
// This is meant to be a lifeline for troubleshooting long-running processes
|
||||
// that crash under conditions that are virtually impossible to reproduce.
|
||||
// Low-level implementation alternatives are preferred to higher-level ones
|
||||
// that might raise cascading exceptions. Last-ditch-kind-of attempts are made
|
||||
// to report as much of the original error as possible, even if there are
|
||||
// secondary issues obtaining some of the details.
|
||||
struct error_fetch_and_normalize {
|
||||
// Immediate normalization is long-established behavior (starting with
|
||||
// https://github.com/pybind/pybind11/commit/135ba8deafb8bf64a15b24d1513899eb600e2011
|
||||
// from Sep 2016) and safest. Normalization could be deferred, but this could mask
|
||||
// errors elsewhere, the performance gain is very minor in typical situations
|
||||
// (usually the dominant bottleneck is EH unwinding), and the implementation here
|
||||
// would be more complex.
|
||||
// This comment only applies to Python <= 3.11:
|
||||
// Immediate normalization is long-established behavior (starting with
|
||||
// https://github.com/pybind/pybind11/commit/135ba8deafb8bf64a15b24d1513899eb600e2011
|
||||
// from Sep 2016) and safest. Normalization could be deferred, but this could mask
|
||||
// errors elsewhere, the performance gain is very minor in typical situations
|
||||
// (usually the dominant bottleneck is EH unwinding), and the implementation here
|
||||
// would be more complex.
|
||||
// Starting with Python 3.12, PyErr_Fetch() normalizes exceptions immediately.
|
||||
// Any errors during normalization are tracked under __notes__.
|
||||
explicit error_fetch_and_normalize(const char *called) {
|
||||
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||
if (!m_type) {
|
||||
|
@ -442,6 +508,14 @@ struct error_fetch_and_normalize {
|
|||
"of the original active exception type.");
|
||||
}
|
||||
m_lazy_error_string = exc_type_name_orig;
|
||||
#if PY_VERSION_HEX >= 0x030C0000
|
||||
// The presence of __notes__ is likely due to exception normalization
|
||||
// errors, although that is not necessarily true, therefore insert a
|
||||
// hint only:
|
||||
if (PyObject_HasAttrString(m_value.ptr(), "__notes__")) {
|
||||
m_lazy_error_string += "[WITH __notes__]";
|
||||
}
|
||||
#else
|
||||
// PyErr_NormalizeException() may change the exception type if there are cascading
|
||||
// failures. This can potentially be extremely confusing.
|
||||
PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||
|
@ -451,11 +525,17 @@ struct error_fetch_and_normalize {
|
|||
"active exception.");
|
||||
}
|
||||
const char *exc_type_name_norm = detail::obj_class_name(m_type.ptr());
|
||||
if (exc_type_name_orig == nullptr) {
|
||||
if (exc_type_name_norm == nullptr) {
|
||||
pybind11_fail("Internal error: " + std::string(called)
|
||||
+ " failed to obtain the name "
|
||||
"of the normalized active exception type.");
|
||||
}
|
||||
# if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00
|
||||
// This behavior runs the risk of masking errors in the error handling, but avoids a
|
||||
// conflict with PyPy, which relies on the normalization here to change OSError to
|
||||
// FileNotFoundError (https://github.com/pybind/pybind11/issues/4075).
|
||||
m_lazy_error_string = exc_type_name_norm;
|
||||
# else
|
||||
if (exc_type_name_norm != m_lazy_error_string) {
|
||||
std::string msg = std::string(called)
|
||||
+ ": MISMATCH of original and normalized "
|
||||
|
@ -467,6 +547,8 @@ struct error_fetch_and_normalize {
|
|||
msg += ": " + format_value_and_trace();
|
||||
pybind11_fail(msg);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
error_fetch_and_normalize(const error_fetch_and_normalize &) = delete;
|
||||
|
@ -477,12 +559,64 @@ struct error_fetch_and_normalize {
|
|||
std::string message_error_string;
|
||||
if (m_value) {
|
||||
auto value_str = reinterpret_steal<object>(PyObject_Str(m_value.ptr()));
|
||||
constexpr const char *message_unavailable_exc
|
||||
= "<MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>";
|
||||
if (!value_str) {
|
||||
message_error_string = detail::error_string();
|
||||
result = "<MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>";
|
||||
result = message_unavailable_exc;
|
||||
} else {
|
||||
result = value_str.cast<std::string>();
|
||||
// Not using `value_str.cast<std::string>()`, to not potentially throw a secondary
|
||||
// error_already_set that will then result in process termination (#4288).
|
||||
auto value_bytes = reinterpret_steal<object>(
|
||||
PyUnicode_AsEncodedString(value_str.ptr(), "utf-8", "backslashreplace"));
|
||||
if (!value_bytes) {
|
||||
message_error_string = detail::error_string();
|
||||
result = message_unavailable_exc;
|
||||
} else {
|
||||
char *buffer = nullptr;
|
||||
Py_ssize_t length = 0;
|
||||
if (PyBytes_AsStringAndSize(value_bytes.ptr(), &buffer, &length) == -1) {
|
||||
message_error_string = detail::error_string();
|
||||
result = message_unavailable_exc;
|
||||
} else {
|
||||
result = std::string(buffer, static_cast<std::size_t>(length));
|
||||
}
|
||||
}
|
||||
}
|
||||
#if PY_VERSION_HEX >= 0x030B0000
|
||||
auto notes
|
||||
= reinterpret_steal<object>(PyObject_GetAttrString(m_value.ptr(), "__notes__"));
|
||||
if (!notes) {
|
||||
PyErr_Clear(); // No notes is good news.
|
||||
} else {
|
||||
auto len_notes = PyList_Size(notes.ptr());
|
||||
if (len_notes < 0) {
|
||||
result += "\nFAILURE obtaining len(__notes__): " + detail::error_string();
|
||||
} else {
|
||||
result += "\n__notes__ (len=" + std::to_string(len_notes) + "):";
|
||||
for (ssize_t i = 0; i < len_notes; i++) {
|
||||
PyObject *note = PyList_GET_ITEM(notes.ptr(), i);
|
||||
auto note_bytes = reinterpret_steal<object>(
|
||||
PyUnicode_AsEncodedString(note, "utf-8", "backslashreplace"));
|
||||
if (!note_bytes) {
|
||||
result += "\nFAILURE obtaining __notes__[" + std::to_string(i)
|
||||
+ "]: " + detail::error_string();
|
||||
} else {
|
||||
char *buffer = nullptr;
|
||||
Py_ssize_t length = 0;
|
||||
if (PyBytes_AsStringAndSize(note_bytes.ptr(), &buffer, &length)
|
||||
== -1) {
|
||||
result += "\nFAILURE formatting __notes__[" + std::to_string(i)
|
||||
+ "]: " + detail::error_string();
|
||||
} else {
|
||||
result += '\n';
|
||||
result += std::string(buffer, static_cast<std::size_t>(length));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
result = "<MESSAGE UNAVAILABLE>";
|
||||
}
|
||||
|
@ -581,12 +715,6 @@ inline std::string error_string() {
|
|||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4275 4251)
|
||||
// warning C4275: An exported class was derived from a class that wasn't exported.
|
||||
// Can be ignored when derived from a STL class.
|
||||
#endif
|
||||
/// Fetch and hold an error which was already set in Python. An instance of this is typically
|
||||
/// thrown to propagate python-side errors back through C++ which can either be caught manually or
|
||||
/// else falls back to the function dispatcher (which then raises the captured error back to
|
||||
|
@ -646,9 +774,6 @@ private:
|
|||
/// crashes (undefined behavior) if the Python interpreter is finalizing.
|
||||
static void m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr);
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/// Replaces the current Python error indicator with the chosen error, performing a
|
||||
/// 'raise from' to indicate that the chosen error was caused by the original error.
|
||||
|
@ -851,10 +976,8 @@ object object_or_cast(T &&o);
|
|||
// Match a PyObject*, which we want to convert directly to handle via its converting constructor
|
||||
inline handle object_or_cast(PyObject *ptr) { return ptr; }
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4522) // warning C4522: multiple assignment operators specified
|
||||
#endif
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4522) // warning C4522: multiple assignment operators specified
|
||||
template <typename Policy>
|
||||
class accessor : public object_api<accessor<Policy>> {
|
||||
using key_type = typename Policy::key_type;
|
||||
|
@ -918,9 +1041,7 @@ private:
|
|||
key_type key;
|
||||
mutable object cache;
|
||||
};
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(accessor_policies)
|
||||
struct obj_attr {
|
||||
|
@ -1266,7 +1387,7 @@ public:
|
|||
|
||||
#define PYBIND11_OBJECT_CVT_DEFAULT(Name, Parent, CheckFun, ConvertFun) \
|
||||
PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
|
||||
Name() : Parent() {}
|
||||
Name() = default;
|
||||
|
||||
#define PYBIND11_OBJECT_CHECK_FAILED(Name, o_ptr) \
|
||||
::pybind11::type_error("Object of type '" \
|
||||
|
@ -1289,7 +1410,7 @@ public:
|
|||
|
||||
#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
|
||||
PYBIND11_OBJECT(Name, Parent, CheckFun) \
|
||||
Name() : Parent() {}
|
||||
Name() = default;
|
||||
|
||||
/// \addtogroup pytypes
|
||||
/// @{
|
||||
|
@ -1358,7 +1479,7 @@ public:
|
|||
private:
|
||||
void advance() {
|
||||
value = reinterpret_steal<object>(PyIter_Next(m_ptr));
|
||||
if (PyErr_Occurred()) {
|
||||
if (value.ptr() == nullptr && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
@ -1408,6 +1529,9 @@ public:
|
|||
str(const char *c, const SzType &n)
|
||||
: object(PyUnicode_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
}
|
||||
|
@ -1417,6 +1541,9 @@ public:
|
|||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
str(const char *c = "") : object(PyUnicode_FromString(c), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
}
|
||||
|
@ -1574,6 +1701,9 @@ inline str::str(const bytes &b) {
|
|||
}
|
||||
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, length));
|
||||
if (!obj) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
m_ptr = obj.release().ptr();
|
||||
|
@ -1651,7 +1781,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
|
||||
template <typename Unsigned>
|
||||
Unsigned as_unsigned(PyObject *o) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))) {
|
||||
if (sizeof(Unsigned) <= sizeof(unsigned long)) {
|
||||
unsigned long v = PyLong_AsUnsignedLong(o);
|
||||
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
|
@ -1668,7 +1798,7 @@ public:
|
|||
template <typename T, detail::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
int_(T value) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(T) <= sizeof(long))) {
|
||||
if (sizeof(T) <= sizeof(long)) {
|
||||
if (std::is_signed<T>::value) {
|
||||
m_ptr = PyLong_FromLong((long) value);
|
||||
} else {
|
||||
|
@ -1785,43 +1915,28 @@ public:
|
|||
|
||||
explicit capsule(const void *value,
|
||||
const char *name = nullptr,
|
||||
void (*destructor)(PyObject *) = nullptr)
|
||||
PyCapsule_Destructor destructor = nullptr)
|
||||
: object(PyCapsule_New(const_cast<void *>(value), name, destructor), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input")
|
||||
capsule(const void *value, void (*destruct)(PyObject *))
|
||||
: object(PyCapsule_New(const_cast<void *>(value), nullptr, destruct), stolen_t{}) {
|
||||
PYBIND11_DEPRECATED("Please use the ctor with value, name, destructor args")
|
||||
capsule(const void *value, PyCapsule_Destructor destructor)
|
||||
: object(PyCapsule_New(const_cast<void *>(value), nullptr, destructor), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
/// Capsule name is nullptr.
|
||||
capsule(const void *value, void (*destructor)(void *)) {
|
||||
m_ptr = PyCapsule_New(const_cast<void *>(value), nullptr, [](PyObject *o) {
|
||||
// guard if destructor called while err indicator is set
|
||||
error_scope error_guard;
|
||||
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
|
||||
if (destructor == nullptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
pybind11_fail("Unable to get capsule context");
|
||||
}
|
||||
const char *name = get_name_in_error_scope(o);
|
||||
void *ptr = PyCapsule_GetPointer(o, name);
|
||||
if (ptr == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
destructor(ptr);
|
||||
});
|
||||
initialize_with_void_ptr_destructor(value, nullptr, destructor);
|
||||
}
|
||||
|
||||
if (!m_ptr || PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
capsule(const void *value, const char *name, void (*destructor)(void *)) {
|
||||
initialize_with_void_ptr_destructor(value, name, destructor);
|
||||
}
|
||||
|
||||
explicit capsule(void (*destructor)()) {
|
||||
|
@ -1889,6 +2004,32 @@ private:
|
|||
|
||||
return name;
|
||||
}
|
||||
|
||||
void initialize_with_void_ptr_destructor(const void *value,
|
||||
const char *name,
|
||||
void (*destructor)(void *)) {
|
||||
m_ptr = PyCapsule_New(const_cast<void *>(value), name, [](PyObject *o) {
|
||||
// guard if destructor called while err indicator is set
|
||||
error_scope error_guard;
|
||||
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
|
||||
if (destructor == nullptr && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
const char *name = get_name_in_error_scope(o);
|
||||
void *ptr = PyCapsule_GetPointer(o, name);
|
||||
if (ptr == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
|
||||
if (destructor != nullptr) {
|
||||
destructor(ptr);
|
||||
}
|
||||
});
|
||||
|
||||
if (!m_ptr || PyCapsule_SetContext(m_ptr, reinterpret_cast<void *>(destructor)) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class tuple : public object {
|
||||
|
@ -1943,7 +2084,11 @@ public:
|
|||
void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
|
||||
template <typename T>
|
||||
bool contains(T &&key) const {
|
||||
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
|
||||
auto result = PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr());
|
||||
if (result == -1) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1998,14 +2143,20 @@ public:
|
|||
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
|
||||
template <typename T>
|
||||
void append(T &&val) /* py-non-const */ {
|
||||
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
if (PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
template <typename IdxType,
|
||||
typename ValType,
|
||||
detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
void insert(const IdxType &index, ValType &&val) /* py-non-const */ {
|
||||
PyList_Insert(
|
||||
m_ptr, ssize_t_cast(index), detail::object_or_cast(std::forward<ValType>(val)).ptr());
|
||||
if (PyList_Insert(m_ptr,
|
||||
ssize_t_cast(index),
|
||||
detail::object_or_cast(std::forward<ValType>(val)).ptr())
|
||||
!= 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2023,7 +2174,11 @@ public:
|
|||
bool empty() const { return size() == 0; }
|
||||
template <typename T>
|
||||
bool contains(T &&val) const {
|
||||
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
|
||||
auto result = PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
if (result == -1) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return result == 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2364,29 +2519,39 @@ bool object_api<D>::rich_compare(object_api const &other, int value) const {
|
|||
return result; \
|
||||
}
|
||||
|
||||
#define PYBIND11_MATH_OPERATOR_BINARY_INPLACE(iop, fn) \
|
||||
template <typename D> \
|
||||
object object_api<D>::iop(object_api const &other) { \
|
||||
object result = reinterpret_steal<object>(fn(derived().ptr(), other.derived().ptr())); \
|
||||
if (!result.ptr()) \
|
||||
throw error_already_set(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
PYBIND11_MATH_OPERATOR_UNARY(operator~, PyNumber_Invert)
|
||||
PYBIND11_MATH_OPERATOR_UNARY(operator-, PyNumber_Negative)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator+=, PyNumber_InPlaceAdd)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator-=, PyNumber_InPlaceSubtract)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator*=, PyNumber_InPlaceMultiply)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator/=, PyNumber_InPlaceTrueDivide)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator|=, PyNumber_InPlaceOr)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator&=, PyNumber_InPlaceAnd)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator^=, PyNumber_InPlaceXor)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator<<=, PyNumber_InPlaceLshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift)
|
||||
PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator>>=, PyNumber_InPlaceRshift)
|
||||
|
||||
#undef PYBIND11_MATH_OPERATOR_UNARY
|
||||
#undef PYBIND11_MATH_OPERATOR_BINARY
|
||||
#undef PYBIND11_MATH_OPERATOR_BINARY_INPLACE
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -45,21 +45,35 @@ using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
|
|||
/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
|
||||
/// used for forwarding a container's elements.
|
||||
template <typename T, typename U>
|
||||
forwarded_type<T, U> forward_like(U &&u) {
|
||||
constexpr forwarded_type<T, U> forward_like(U &&u) {
|
||||
return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
// Checks if a container has a STL style reserve method.
|
||||
// This will only return true for a `reserve()` with a `void` return.
|
||||
template <typename C>
|
||||
using has_reserve_method = std::is_same<decltype(std::declval<C>().reserve(0)), void>;
|
||||
|
||||
template <typename Type, typename Key>
|
||||
struct set_caster {
|
||||
using type = Type;
|
||||
using key_conv = make_caster<Key>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||
void reserve_maybe(const anyset &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
}
|
||||
void reserve_maybe(const anyset &, void *) {}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<anyset>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto s = reinterpret_borrow<anyset>(src);
|
||||
value.clear();
|
||||
reserve_maybe(s, &value);
|
||||
for (auto entry : s) {
|
||||
key_conv conv;
|
||||
if (!conv.load(entry, convert)) {
|
||||
|
@ -78,7 +92,7 @@ struct set_caster {
|
|||
pybind11::set s;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
key_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(std::move(value_))) {
|
||||
return handle();
|
||||
}
|
||||
|
@ -94,12 +108,21 @@ struct map_caster {
|
|||
using key_conv = make_caster<Key>;
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||
void reserve_maybe(const dict &d, Type *) {
|
||||
value.reserve(d.size());
|
||||
}
|
||||
void reserve_maybe(const dict &, void *) {}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<dict>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto d = reinterpret_borrow<dict>(src);
|
||||
value.clear();
|
||||
reserve_maybe(d, &value);
|
||||
for (auto it : d) {
|
||||
key_conv kconv;
|
||||
value_conv vconv;
|
||||
|
@ -122,9 +145,9 @@ struct map_caster {
|
|||
}
|
||||
for (auto &&kv : src) {
|
||||
auto key = reinterpret_steal<object>(
|
||||
key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
|
||||
key_conv::cast(detail::forward_like<T>(kv.first), policy_key, parent));
|
||||
auto value = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
|
||||
value_conv::cast(detail::forward_like<T>(kv.second), policy_value, parent));
|
||||
if (!key || !value) {
|
||||
return handle();
|
||||
}
|
||||
|
@ -160,9 +183,7 @@ struct list_caster {
|
|||
}
|
||||
|
||||
private:
|
||||
template <
|
||||
typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||
void reserve_maybe(const sequence &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
}
|
||||
|
@ -178,7 +199,7 @@ public:
|
|||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
value_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
return handle();
|
||||
}
|
||||
|
@ -242,7 +263,7 @@ public:
|
|||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
value_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
return handle();
|
||||
}
|
||||
|
@ -252,11 +273,11 @@ public:
|
|||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(ArrayType,
|
||||
const_name("List[") + value_conv::name
|
||||
const_name<Resizable>(const_name(""), const_name("Annotated["))
|
||||
+ const_name("List[") + value_conv::name + const_name("]")
|
||||
+ const_name<Resizable>(const_name(""),
|
||||
const_name("[") + const_name<Size>()
|
||||
+ const_name("]"))
|
||||
+ const_name("]"));
|
||||
const_name(", FixedSize(")
|
||||
+ const_name<Size>() + const_name(")]")));
|
||||
};
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
|
@ -290,11 +311,12 @@ struct optional_caster {
|
|||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
return none().inc_ref();
|
||||
return none().release();
|
||||
}
|
||||
if (!std::is_lvalue_reference<T>::value) {
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
}
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
return value_conv::cast(*std::forward<T>(src), policy, parent);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "detail/type_caster_base.h"
|
||||
#include "cast.h"
|
||||
#include "operators.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
@ -58,9 +61,11 @@ struct is_comparable<
|
|||
/* For a vector/map data structure, recursively check the value type
|
||||
(which is std::pair for maps) */
|
||||
template <typename T>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
|
||||
static constexpr const bool value = is_comparable<typename T::value_type>::value;
|
||||
};
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>>
|
||||
: is_comparable<typename recursive_container_traits<T>::type_to_check_recursively> {};
|
||||
|
||||
template <>
|
||||
struct is_comparable<recursive_bottom> : std::true_type {};
|
||||
|
||||
/* For pairs, recursively check the two data types */
|
||||
template <typename T>
|
||||
|
@ -352,13 +357,17 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
|
|||
using DiffType = typename Vector::difference_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
|
||||
if (i < 0 && (i += v.size()) < 0) {
|
||||
if (i < 0) {
|
||||
i += v.size();
|
||||
if (i < 0) {
|
||||
throw index_error();
|
||||
}
|
||||
}
|
||||
auto i_st = static_cast<SizeType>(i);
|
||||
if (i_st >= v.size()) {
|
||||
throw index_error();
|
||||
}
|
||||
if ((SizeType) i >= v.size()) {
|
||||
throw index_error();
|
||||
}
|
||||
return v[(SizeType) i];
|
||||
return v[i_st];
|
||||
});
|
||||
|
||||
cl.def(
|
||||
|
@ -636,18 +645,52 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
|||
"Return the canonical string representation of this map.");
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
template <typename KeyType>
|
||||
struct keys_view {
|
||||
Map ↦
|
||||
virtual size_t len() = 0;
|
||||
virtual iterator iter() = 0;
|
||||
virtual bool contains(const KeyType &k) = 0;
|
||||
virtual bool contains(const object &k) = 0;
|
||||
virtual ~keys_view() = default;
|
||||
};
|
||||
|
||||
template <typename Map>
|
||||
template <typename MappedType>
|
||||
struct values_view {
|
||||
virtual size_t len() = 0;
|
||||
virtual iterator iter() = 0;
|
||||
virtual ~values_view() = default;
|
||||
};
|
||||
|
||||
template <typename KeyType, typename MappedType>
|
||||
struct items_view {
|
||||
virtual size_t len() = 0;
|
||||
virtual iterator iter() = 0;
|
||||
virtual ~items_view() = default;
|
||||
};
|
||||
|
||||
template <typename Map, typename KeysView>
|
||||
struct KeysViewImpl : public KeysView {
|
||||
explicit KeysViewImpl(Map &map) : map(map) {}
|
||||
size_t len() override { return map.size(); }
|
||||
iterator iter() override { return make_key_iterator(map.begin(), map.end()); }
|
||||
bool contains(const typename Map::key_type &k) override { return map.find(k) != map.end(); }
|
||||
bool contains(const object &) override { return false; }
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename Map>
|
||||
struct items_view {
|
||||
template <typename Map, typename ValuesView>
|
||||
struct ValuesViewImpl : public ValuesView {
|
||||
explicit ValuesViewImpl(Map &map) : map(map) {}
|
||||
size_t len() override { return map.size(); }
|
||||
iterator iter() override { return make_value_iterator(map.begin(), map.end()); }
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename Map, typename ItemsView>
|
||||
struct ItemsViewImpl : public ItemsView {
|
||||
explicit ItemsViewImpl(Map &map) : map(map) {}
|
||||
size_t len() override { return map.size(); }
|
||||
iterator iter() override { return make_iterator(map.begin(), map.end()); }
|
||||
Map ↦
|
||||
};
|
||||
|
||||
|
@ -657,9 +700,11 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
|
|||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using KeysView = detail::keys_view<Map>;
|
||||
using ValuesView = detail::values_view<Map>;
|
||||
using ItemsView = detail::items_view<Map>;
|
||||
using StrippedKeyType = detail::remove_cvref_t<KeyType>;
|
||||
using StrippedMappedType = detail::remove_cvref_t<MappedType>;
|
||||
using KeysView = detail::keys_view<StrippedKeyType>;
|
||||
using ValuesView = detail::values_view<StrippedMappedType>;
|
||||
using ItemsView = detail::items_view<StrippedKeyType, StrippedMappedType>;
|
||||
using Class_ = class_<Map, holder_type>;
|
||||
|
||||
// If either type is a non-module-local bound type then make the map binding non-local as well;
|
||||
|
@ -673,12 +718,57 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
|
|||
}
|
||||
|
||||
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
|
||||
class_<KeysView> keys_view(
|
||||
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
class_<ValuesView> values_view(
|
||||
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
class_<ItemsView> items_view(
|
||||
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
static constexpr auto key_type_descr = detail::make_caster<KeyType>::name;
|
||||
static constexpr auto mapped_type_descr = detail::make_caster<MappedType>::name;
|
||||
std::string key_type_name(key_type_descr.text), mapped_type_name(mapped_type_descr.text);
|
||||
|
||||
// If key type isn't properly wrapped, fall back to C++ names
|
||||
if (key_type_name == "%") {
|
||||
key_type_name = detail::type_info_description(typeid(KeyType));
|
||||
}
|
||||
// Similarly for value type:
|
||||
if (mapped_type_name == "%") {
|
||||
mapped_type_name = detail::type_info_description(typeid(MappedType));
|
||||
}
|
||||
|
||||
// Wrap KeysView[KeyType] if it wasn't already wrapped
|
||||
if (!detail::get_type_info(typeid(KeysView))) {
|
||||
class_<KeysView> keys_view(
|
||||
scope, ("KeysView[" + key_type_name + "]").c_str(), pybind11::module_local(local));
|
||||
keys_view.def("__len__", &KeysView::len);
|
||||
keys_view.def("__iter__",
|
||||
&KeysView::iter,
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
keys_view.def("__contains__",
|
||||
static_cast<bool (KeysView::*)(const KeyType &)>(&KeysView::contains));
|
||||
// Fallback for when the object is not of the key type
|
||||
keys_view.def("__contains__",
|
||||
static_cast<bool (KeysView::*)(const object &)>(&KeysView::contains));
|
||||
}
|
||||
// Similarly for ValuesView:
|
||||
if (!detail::get_type_info(typeid(ValuesView))) {
|
||||
class_<ValuesView> values_view(scope,
|
||||
("ValuesView[" + mapped_type_name + "]").c_str(),
|
||||
pybind11::module_local(local));
|
||||
values_view.def("__len__", &ValuesView::len);
|
||||
values_view.def("__iter__",
|
||||
&ValuesView::iter,
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
}
|
||||
// Similarly for ItemsView:
|
||||
if (!detail::get_type_info(typeid(ItemsView))) {
|
||||
class_<ItemsView> items_view(
|
||||
scope,
|
||||
("ItemsView[" + key_type_name + ", ").append(mapped_type_name + "]").c_str(),
|
||||
pybind11::module_local(local));
|
||||
items_view.def("__len__", &ItemsView::len);
|
||||
items_view.def("__iter__",
|
||||
&ItemsView::iter,
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
}
|
||||
|
||||
cl.def(init<>());
|
||||
|
||||
|
@ -698,19 +788,25 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
|
|||
|
||||
cl.def(
|
||||
"keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
[](Map &m) {
|
||||
return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map, KeysView>(m));
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def(
|
||||
"values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
[](Map &m) {
|
||||
return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map, ValuesView>(m));
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def(
|
||||
"items",
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
[](Map &m) {
|
||||
return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map, ItemsView>(m));
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
|
@ -749,36 +845,6 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
|
|||
|
||||
cl.def("__len__", &Map::size);
|
||||
|
||||
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
|
||||
keys_view.def(
|
||||
"__iter__",
|
||||
[](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// Fallback for when the object is not of the key type
|
||||
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
|
||||
|
||||
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
|
||||
values_view.def(
|
||||
"__iter__",
|
||||
[](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
|
||||
items_view.def(
|
||||
"__iter__",
|
||||
[](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2023 The pybind Community.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "detail/descr.h"
|
||||
#include "cast.h"
|
||||
#include "pytypes.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <>
|
||||
class type_caster<PyObject> {
|
||||
public:
|
||||
static constexpr auto name = const_name("object"); // See discussion under PR #4601.
|
||||
|
||||
// This overload is purely to guard against accidents.
|
||||
template <typename T,
|
||||
detail::enable_if_t<!is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
|
||||
static handle cast(T &&, return_value_policy, handle /*parent*/) {
|
||||
static_assert(is_same_ignoring_cvref<T, PyObject *>::value,
|
||||
"Invalid C++ type T for to-Python conversion (type_caster<PyObject>).");
|
||||
return nullptr; // Unreachable.
|
||||
}
|
||||
|
||||
static handle cast(PyObject *src, return_value_policy policy, handle /*parent*/) {
|
||||
if (src == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
if (PyErr_Occurred()) {
|
||||
raise_from(PyExc_SystemError, "src != nullptr but PyErr_Occurred()");
|
||||
throw error_already_set();
|
||||
}
|
||||
if (policy == return_value_policy::take_ownership) {
|
||||
return src;
|
||||
}
|
||||
if (policy == return_value_policy::reference
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
return handle(src).inc_ref();
|
||||
}
|
||||
pybind11_fail("type_caster<PyObject>::cast(): unsupported return_value_policy: "
|
||||
+ std::to_string(static_cast<int>(policy)));
|
||||
}
|
||||
|
||||
bool load(handle src, bool) {
|
||||
value = reinterpret_borrow<object>(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using cast_op_type = PyObject *;
|
||||
|
||||
explicit operator PyObject *() { return value.ptr(); }
|
||||
|
||||
private:
|
||||
object value;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
@ -5,7 +5,17 @@ import nox
|
|||
nox.needs_version = ">=2022.1.7"
|
||||
nox.options.sessions = ["lint", "tests", "tests_packaging"]
|
||||
|
||||
PYTHON_VERISONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.7", "pypy3.8"]
|
||||
PYTHON_VERSIONS = [
|
||||
"3.6",
|
||||
"3.7",
|
||||
"3.8",
|
||||
"3.9",
|
||||
"3.10",
|
||||
"3.11",
|
||||
"pypy3.7",
|
||||
"pypy3.8",
|
||||
"pypy3.9",
|
||||
]
|
||||
|
||||
if os.environ.get("CI", None):
|
||||
nox.options.error_on_missing_interpreters = True
|
||||
|
@ -17,10 +27,10 @@ def lint(session: nox.Session) -> None:
|
|||
Lint the codebase (except for clang-format/tidy).
|
||||
"""
|
||||
session.install("pre-commit")
|
||||
session.run("pre-commit", "run", "-a")
|
||||
session.run("pre-commit", "run", "-a", *session.posargs)
|
||||
|
||||
|
||||
@nox.session(python=PYTHON_VERISONS)
|
||||
@nox.session(python=PYTHON_VERSIONS)
|
||||
def tests(session: nox.Session) -> None:
|
||||
"""
|
||||
Run the tests (requires a compiler).
|
||||
|
@ -48,7 +58,7 @@ def tests_packaging(session: nox.Session) -> None:
|
|||
"""
|
||||
|
||||
session.install("-r", "tests/requirements.txt", "--prefer-binary")
|
||||
session.run("pytest", "tests/extra_python_package")
|
||||
session.run("pytest", "tests/extra_python_package", *session.posargs)
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import sys
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
if sys.version_info < (3, 6): # noqa: UP036
|
||||
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
|
||||
raise ImportError(msg)
|
||||
|
||||
|
||||
from ._version import __version__, version_info
|
||||
from .commands import get_cmake_dir, get_include
|
||||
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
|
||||
|
||||
__all__ = (
|
||||
"version_info",
|
||||
"__version__",
|
||||
"get_include",
|
||||
"get_cmake_dir",
|
||||
"get_pkgconfig_dir",
|
||||
)
|
||||
|
|
|
@ -4,7 +4,8 @@ import argparse
|
|||
import sys
|
||||
import sysconfig
|
||||
|
||||
from .commands import get_cmake_dir, get_include
|
||||
from ._version import __version__
|
||||
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
|
||||
|
||||
|
||||
def print_includes() -> None:
|
||||
|
@ -24,8 +25,13 @@ def print_includes() -> None:
|
|||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=__version__,
|
||||
help="Print the version and exit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--includes",
|
||||
action="store_true",
|
||||
|
@ -36,6 +42,11 @@ def main() -> None:
|
|||
action="store_true",
|
||||
help="Print the CMake module directory, ideal for setting -Dpybind11_ROOT in CMake.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pkgconfigdir",
|
||||
action="store_true",
|
||||
help="Print the pkgconfig directory, ideal for setting $PKG_CONFIG_PATH.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if not sys.argv[1:]:
|
||||
parser.print_help()
|
||||
|
@ -43,6 +54,8 @@ def main() -> None:
|
|||
print_includes()
|
||||
if args.cmakedir:
|
||||
print(get_cmake_dir())
|
||||
if args.pkgconfigdir:
|
||||
print(get_pkgconfig_dir())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -8,5 +8,5 @@ def _to_int(s: str) -> Union[int, str]:
|
|||
return s
|
||||
|
||||
|
||||
__version__ = "2.10.0"
|
||||
__version__ = "2.11.1"
|
||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||
|
|
|
@ -3,7 +3,7 @@ import os
|
|||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def get_include(user: bool = False) -> str: # pylint: disable=unused-argument
|
||||
def get_include(user: bool = False) -> str: # noqa: ARG001
|
||||
"""
|
||||
Return the path to the pybind11 include directory. The historical "user"
|
||||
argument is unused, and may be removed.
|
||||
|
@ -23,3 +23,15 @@ def get_cmake_dir() -> str:
|
|||
|
||||
msg = "pybind11 not installed, installation required to access the CMake files"
|
||||
raise ImportError(msg)
|
||||
|
||||
|
||||
def get_pkgconfig_dir() -> str:
|
||||
"""
|
||||
Return the path to the pybind11 pkgconfig directory.
|
||||
"""
|
||||
pkgconfig_installed_path = os.path.join(DIR, "share", "pkgconfig")
|
||||
if os.path.exists(pkgconfig_installed_path):
|
||||
return pkgconfig_installed_path
|
||||
|
||||
msg = "pybind11 not installed, installation required to access the pkgconfig files"
|
||||
raise ImportError(msg)
|
||||
|
|
|
@ -66,8 +66,8 @@ try:
|
|||
from setuptools import Extension as _Extension
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
except ImportError:
|
||||
from distutils.command.build_ext import build_ext as _build_ext
|
||||
from distutils.extension import Extension as _Extension
|
||||
from distutils.command.build_ext import build_ext as _build_ext # type: ignore[assignment]
|
||||
from distutils.extension import Extension as _Extension # type: ignore[assignment]
|
||||
|
||||
import distutils.ccompiler
|
||||
import distutils.errors
|
||||
|
@ -84,7 +84,7 @@ STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
|
|||
# directory into your path if it sits beside your setup.py.
|
||||
|
||||
|
||||
class Pybind11Extension(_Extension): # type: ignore[misc]
|
||||
class Pybind11Extension(_Extension):
|
||||
"""
|
||||
Build a C++11+ Extension module with pybind11. This automatically adds the
|
||||
recommended flags when you init the extension and assumes C++ sources - you
|
||||
|
@ -118,7 +118,6 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
self.extra_link_args[:0] = flags
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
|
||||
self._cxx_level = 0
|
||||
cxx_std = kwargs.pop("cxx_std", 0)
|
||||
|
||||
|
@ -145,7 +144,6 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
self.cxx_std = cxx_std
|
||||
|
||||
cflags = []
|
||||
ldflags = []
|
||||
if WIN:
|
||||
cflags += ["/EHsc", "/bigobj"]
|
||||
else:
|
||||
|
@ -155,11 +153,7 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
c_cpp_flags = shlex.split(env_cflags) + shlex.split(env_cppflags)
|
||||
if not any(opt.startswith("-g") for opt in c_cpp_flags):
|
||||
cflags += ["-g0"]
|
||||
if MACOS:
|
||||
cflags += ["-stdlib=libc++"]
|
||||
ldflags += ["-stdlib=libc++"]
|
||||
self._add_cflags(cflags)
|
||||
self._add_ldflags(ldflags)
|
||||
|
||||
@property
|
||||
def cxx_std(self) -> int:
|
||||
|
@ -174,9 +168,10 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
|
||||
@cxx_std.setter
|
||||
def cxx_std(self, level: int) -> None:
|
||||
|
||||
if self._cxx_level:
|
||||
warnings.warn("You cannot safely change the cxx_level after setting it!")
|
||||
warnings.warn(
|
||||
"You cannot safely change the cxx_level after setting it!", stacklevel=2
|
||||
)
|
||||
|
||||
# MSVC 2015 Update 3 and later only have 14 (and later 17) modes, so
|
||||
# force a valid flag here.
|
||||
|
@ -271,7 +266,7 @@ def auto_cpp_level(compiler: Any) -> Union[str, int]:
|
|||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
class build_ext(_build_ext): # type: ignore[misc] # noqa: N801
|
||||
class build_ext(_build_ext): # noqa: N801
|
||||
"""
|
||||
Customized build_ext that allows an auto-search for the highest supported
|
||||
C++ level for Pybind11Extension. This is only needed for the auto-search
|
||||
|
@ -341,7 +336,7 @@ def naive_recompile(obj: str, src: str) -> bool:
|
|||
return os.stat(obj).st_mtime < os.stat(src).st_mtime
|
||||
|
||||
|
||||
def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument
|
||||
def no_recompile(obg: str, src: str) -> bool: # noqa: ARG001
|
||||
"""
|
||||
This is the safest but slowest choice (and is the default) - will always
|
||||
recompile sources.
|
||||
|
@ -439,7 +434,6 @@ class ParallelCompile:
|
|||
extra_postargs: Optional[List[str]] = None,
|
||||
depends: Optional[List[str]] = None,
|
||||
) -> Any:
|
||||
|
||||
# These lines are directly from distutils.ccompiler.CCompiler
|
||||
macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined]
|
||||
output_dir, macros, include_dirs, sources, depends, extra_postargs
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
|
||||
[tool.check-manifest]
|
||||
ignore = [
|
||||
"tests/**",
|
||||
|
@ -15,11 +16,6 @@ ignore = [
|
|||
"noxfile.py",
|
||||
]
|
||||
|
||||
[tool.isort]
|
||||
# Needs the compiled .so modules and env.py from tests
|
||||
known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
|
||||
# For black compatibility
|
||||
profile = "black"
|
||||
|
||||
[tool.mypy]
|
||||
files = ["pybind11"]
|
||||
|
@ -30,7 +26,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
|||
warn_unreachable = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["ghapi.*", "setuptools.*"]
|
||||
module = ["ghapi.*"]
|
||||
ignore_missing_imports = true
|
||||
|
||||
|
||||
|
@ -58,4 +54,45 @@ messages_control.disable = [
|
|||
"invalid-name",
|
||||
"protected-access",
|
||||
"missing-module-docstring",
|
||||
"unused-argument", # covered by Ruff ARG
|
||||
]
|
||||
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"E", "F", "W", # flake8
|
||||
"B", # flake8-bugbear
|
||||
"I", # isort
|
||||
"N", # pep8-naming
|
||||
"ARG", # flake8-unused-arguments
|
||||
"C4", # flake8-comprehensions
|
||||
"EM", # flake8-errmsg
|
||||
"ICN", # flake8-import-conventions
|
||||
"ISC", # flake8-implicit-str-concat
|
||||
"PGH", # pygrep-hooks
|
||||
"PIE", # flake8-pie
|
||||
"PL", # pylint
|
||||
"PT", # flake8-pytest-style
|
||||
"RET", # flake8-return
|
||||
"RUF100", # Ruff-specific
|
||||
"SIM", # flake8-simplify
|
||||
"UP", # pyupgrade
|
||||
"YTT", # flake8-2020
|
||||
]
|
||||
ignore = [
|
||||
"PLR", # Design related pylint
|
||||
"E501", # Line too long (Black is enough)
|
||||
"PT011", # Too broad with raises in pytest
|
||||
"PT004", # Fixture that doesn't return needs underscore (no, it is fine)
|
||||
"SIM118", # iter(x) is not always the same as iter(x.keys())
|
||||
]
|
||||
target-version = "py37"
|
||||
src = ["src"]
|
||||
unfixable = ["T20"]
|
||||
exclude = []
|
||||
line-length = 120
|
||||
isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"]
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"tests/**" = ["EM", "N"]
|
||||
"tests/test_call_policies.py" = ["PLC1901"]
|
||||
|
|
|
@ -20,6 +20,7 @@ classifiers =
|
|||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
Programming Language :: Python :: 3.12
|
||||
License :: OSI Approved :: BSD License
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
|
@ -40,11 +41,3 @@ project_urls =
|
|||
[options]
|
||||
python_requires = >=3.6
|
||||
zip_safe = False
|
||||
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
show_source = True
|
||||
exclude = .git, __pycache__, build, dist, docs, tools, venv
|
||||
extend-ignore = E203, E722, B950
|
||||
extend-select = B9
|
||||
|
|
|
@ -96,7 +96,7 @@ def get_and_replace(
|
|||
|
||||
# Use our input files instead when making the SDist (and anything that depends
|
||||
# on it, like a wheel)
|
||||
class SDist(setuptools.command.sdist.sdist): # type: ignore[misc]
|
||||
class SDist(setuptools.command.sdist.sdist):
|
||||
def make_release_tree(self, base_dir: str, files: List[str]) -> None:
|
||||
super().make_release_tree(base_dir, files)
|
||||
|
||||
|
@ -127,6 +127,7 @@ with remove_output("pybind11/include", "pybind11/share"):
|
|||
"-DCMAKE_INSTALL_PREFIX=pybind11",
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DPYBIND11_NOPYTHON=ON",
|
||||
"-Dprefix_for_pc_file=${pcfiledir}/../../",
|
||||
]
|
||||
if "CMAKE_ARGS" in os.environ:
|
||||
fcommand = [
|
||||
|
|
|
@ -5,20 +5,17 @@
|
|||
# All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.21)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
# Only needed for CMake < 3.5 support
|
||||
include(CMakeParseArguments)
|
||||
|
||||
# Filter out items; print an optional message if any items filtered. This ignores extensions.
|
||||
#
|
||||
# Usage:
|
||||
|
@ -128,7 +125,8 @@ set(PYBIND11_TEST_FILES
|
|||
test_custom_type_casters
|
||||
test_custom_type_setup
|
||||
test_docstring_options
|
||||
test_eigen
|
||||
test_eigen_matrix
|
||||
test_eigen_tensor
|
||||
test_enum
|
||||
test_eval
|
||||
test_exceptions
|
||||
|
@ -153,7 +151,11 @@ set(PYBIND11_TEST_FILES
|
|||
test_stl_binders
|
||||
test_tagbased_polymorphic
|
||||
test_thread
|
||||
test_type_caster_pyobject_ptr
|
||||
test_union
|
||||
test_unnamed_namespace_a
|
||||
test_unnamed_namespace_b
|
||||
test_vector_unique_ptr_member
|
||||
test_virtual_functions)
|
||||
|
||||
# Invoking cmake with something like:
|
||||
|
@ -168,7 +170,7 @@ if(PYBIND11_TEST_OVERRIDE)
|
|||
# This allows the override to be done with extensions, preserving backwards compatibility.
|
||||
foreach(test_name ${TEST_FILES_NO_EXT})
|
||||
if(NOT ${test_name} IN_LIST TEST_OVERRIDE_NO_EXT
|
||||
)# If not in the whitelist, add to be filtered out.
|
||||
)# If not in the allowlist, add to be filtered out.
|
||||
list(APPEND PYBIND11_TEST_FILTER ${test_name})
|
||||
endif()
|
||||
endforeach()
|
||||
|
@ -233,7 +235,10 @@ 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"
|
||||
# skip message).
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_matrix.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I EQUAL -1)
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
endif()
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
|
||||
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
|
||||
|
@ -288,13 +293,34 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
|||
set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING})
|
||||
endif()
|
||||
message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}")
|
||||
|
||||
if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
|
||||
tests_extra_targets("test_eigen_tensor.py" "eigen_tensor_avoid_stl_array")
|
||||
endif()
|
||||
|
||||
else()
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_matrix.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
message(
|
||||
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Some code doesn't support gcc 4
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Optional dependency for some tests (boost::variant is only supported with version >= 1.56)
|
||||
find_package(Boost 1.56)
|
||||
|
||||
|
|
|
@ -7,13 +7,36 @@ Adds docstring and exceptions message sanitizers.
|
|||
import contextlib
|
||||
import difflib
|
||||
import gc
|
||||
import multiprocessing
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
import pytest
|
||||
|
||||
# Early diagnostic for failed imports
|
||||
import pybind11_tests
|
||||
try:
|
||||
import pybind11_tests
|
||||
except Exception:
|
||||
# pytest does not show the traceback without this.
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def use_multiprocessing_forkserver_on_linux():
|
||||
if sys.platform != "linux":
|
||||
# The default on Windows and macOS is "spawn": If it's not broken, don't fix it.
|
||||
return
|
||||
|
||||
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
|
||||
# In a nutshell: fork() after starting threads == flakiness in the form of deadlocks.
|
||||
# It is actually a well-known pitfall, unfortunately without guard rails.
|
||||
# "forkserver" is more performant than "spawn" (~9s vs ~13s for tests/test_gil_scoped.py,
|
||||
# visit the issuecomment link above for details).
|
||||
multiprocessing.set_start_method("forkserver")
|
||||
|
||||
|
||||
_long_marker = re.compile(r"([0-9])L")
|
||||
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
|
||||
|
@ -59,9 +82,8 @@ class Output:
|
|||
b = _strip_and_dedent(other).splitlines()
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = _make_explanation(a, b)
|
||||
return False
|
||||
self.explanation = _make_explanation(a, b)
|
||||
return False
|
||||
|
||||
|
||||
class Unordered(Output):
|
||||
|
@ -72,9 +94,8 @@ class Unordered(Output):
|
|||
b = _split_and_sort(other)
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = _make_explanation(a, b)
|
||||
return False
|
||||
self.explanation = _make_explanation(a, b)
|
||||
return False
|
||||
|
||||
|
||||
class Capture:
|
||||
|
@ -95,9 +116,8 @@ class Capture:
|
|||
b = other
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = a.explanation
|
||||
return False
|
||||
self.explanation = a.explanation
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return self.out
|
||||
|
@ -114,7 +134,7 @@ class Capture:
|
|||
return Output(self.err)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def capture(capsys):
|
||||
"""Extended `capsys` with context manager and custom equality operators"""
|
||||
return Capture(capsys)
|
||||
|
@ -135,25 +155,22 @@ class SanitizedString:
|
|||
b = _strip_and_dedent(other)
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
self.explanation = _make_explanation(a.splitlines(), b.splitlines())
|
||||
return False
|
||||
self.explanation = _make_explanation(a.splitlines(), b.splitlines())
|
||||
return False
|
||||
|
||||
|
||||
def _sanitize_general(s):
|
||||
s = s.strip()
|
||||
s = s.replace("pybind11_tests.", "m.")
|
||||
s = _long_marker.sub(r"\1", s)
|
||||
return s
|
||||
return _long_marker.sub(r"\1", s)
|
||||
|
||||
|
||||
def _sanitize_docstring(thing):
|
||||
s = thing.__doc__
|
||||
s = _sanitize_general(s)
|
||||
return s
|
||||
return _sanitize_general(s)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def doc():
|
||||
"""Sanitize docstrings and add custom failure explanation"""
|
||||
return SanitizedString(_sanitize_docstring)
|
||||
|
@ -162,30 +179,20 @@ def doc():
|
|||
def _sanitize_message(thing):
|
||||
s = str(thing)
|
||||
s = _sanitize_general(s)
|
||||
s = _hexadecimal.sub("0", s)
|
||||
return s
|
||||
return _hexadecimal.sub("0", s)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def msg():
|
||||
"""Sanitize messages and add custom failure explanation"""
|
||||
return SanitizedString(_sanitize_message)
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def pytest_assertrepr_compare(op, left, right):
|
||||
def pytest_assertrepr_compare(op, left, right): # noqa: ARG001
|
||||
"""Hook to insert custom failure explanation"""
|
||||
if hasattr(left, "explanation"):
|
||||
return left.explanation
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def suppress(exception):
|
||||
"""Suppress the desired exception"""
|
||||
try:
|
||||
yield
|
||||
except exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def gc_collect():
|
||||
|
@ -196,7 +203,7 @@ def gc_collect():
|
|||
|
||||
|
||||
def pytest_configure():
|
||||
pytest.suppress = suppress
|
||||
pytest.suppress = contextlib.suppress
|
||||
pytest.gc_collect = gc_collect
|
||||
|
||||
|
||||
|
@ -210,4 +217,5 @@ def pytest_report_header(config):
|
|||
f" {pybind11_tests.compiler_info}"
|
||||
f" {pybind11_tests.cpp_std}"
|
||||
f" {pybind11_tests.PYBIND11_INTERNALS_ID}"
|
||||
f" PYBIND11_SIMPLE_GIL_MANAGEMENT={pybind11_tests.PYBIND11_SIMPLE_GIL_MANAGEMENT}"
|
||||
)
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
#if defined(PYPY_VERSION)
|
||||
PyObject *globals = PyEval_GetGlobals();
|
||||
PyObject *result = PyRun_String("import gc\n"
|
||||
"for i in range(2):"
|
||||
"for i in range(2):\n"
|
||||
" gc.collect()\n",
|
||||
Py_file_input,
|
||||
globals,
|
||||
|
|
|
@ -6,9 +6,15 @@
|
|||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
#if defined(PYBIND11_INTERNALS_VERSION)
|
||||
# undef PYBIND11_INTERNALS_VERSION
|
||||
#endif
|
||||
#define PYBIND11_INTERNALS_VERSION 21814642 // Ensure this module has its own `internals` instance.
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a
|
||||
// PYBIND11_MODULE. The purpose is to test that such a DSO can create a
|
||||
|
@ -21,8 +27,54 @@
|
|||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
void gil_acquire() { py::gil_scoped_acquire gil; }
|
||||
|
||||
std::string gil_multi_acquire_release(unsigned bits) {
|
||||
if ((bits & 0x1u) != 0u) {
|
||||
py::gil_scoped_acquire gil;
|
||||
}
|
||||
if ((bits & 0x2u) != 0u) {
|
||||
py::gil_scoped_release gil;
|
||||
}
|
||||
if ((bits & 0x4u) != 0u) {
|
||||
py::gil_scoped_acquire gil;
|
||||
}
|
||||
if ((bits & 0x8u) != 0u) {
|
||||
py::gil_scoped_release gil;
|
||||
}
|
||||
return PYBIND11_INTERNALS_ID;
|
||||
}
|
||||
|
||||
struct CustomAutoGIL {
|
||||
CustomAutoGIL() : gstate(PyGILState_Ensure()) {}
|
||||
~CustomAutoGIL() { PyGILState_Release(gstate); }
|
||||
|
||||
PyGILState_STATE gstate;
|
||||
};
|
||||
struct CustomAutoNoGIL {
|
||||
CustomAutoNoGIL() : save(PyEval_SaveThread()) {}
|
||||
~CustomAutoNoGIL() { PyEval_RestoreThread(save); }
|
||||
|
||||
PyThreadState *save;
|
||||
};
|
||||
|
||||
template <typename Acquire, typename Release>
|
||||
void gil_acquire_inner() {
|
||||
Acquire acquire_outer;
|
||||
Acquire acquire_inner;
|
||||
Release release;
|
||||
}
|
||||
|
||||
template <typename Acquire, typename Release>
|
||||
void gil_acquire_nested() {
|
||||
Acquire acquire_outer;
|
||||
Acquire acquire_inner;
|
||||
Release release;
|
||||
auto thread = std::thread(&gil_acquire_inner<Acquire, Release>);
|
||||
thread.join();
|
||||
}
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
|
@ -30,6 +82,9 @@ struct PyModuleDef moduledef = {
|
|||
|
||||
} // namespace
|
||||
|
||||
#define ADD_FUNCTION(Name, ...) \
|
||||
PyModule_AddObject(m, Name, PyLong_FromVoidPtr(reinterpret_cast<void *>(&__VA_ARGS__)));
|
||||
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
|
||||
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
|
@ -37,8 +92,16 @@ extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
|
|||
if (m != nullptr) {
|
||||
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(
|
||||
m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
|
||||
ADD_FUNCTION("gil_acquire_funcaddr", gil_acquire)
|
||||
ADD_FUNCTION("gil_multi_acquire_release_funcaddr", gil_multi_acquire_release)
|
||||
ADD_FUNCTION("gil_acquire_inner_custom_funcaddr",
|
||||
gil_acquire_inner<CustomAutoGIL, CustomAutoNoGIL>)
|
||||
ADD_FUNCTION("gil_acquire_nested_custom_funcaddr",
|
||||
gil_acquire_nested<CustomAutoGIL, CustomAutoNoGIL>)
|
||||
ADD_FUNCTION("gil_acquire_inner_pybind11_funcaddr",
|
||||
gil_acquire_inner<py::gil_scoped_acquire, py::gil_scoped_release>)
|
||||
ADD_FUNCTION("gil_acquire_nested_pybind11_funcaddr",
|
||||
gil_acquire_nested<py::gil_scoped_acquire, py::gil_scoped_release>)
|
||||
}
|
||||
|
||||
return m;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef EIGEN_AVOID_STL_ARRAY
|
||||
# define EIGEN_AVOID_STL_ARRAY
|
||||
#endif
|
||||
|
||||
#include "test_eigen_tensor.inl"
|
||||
|
||||
PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m) { eigen_tensor_test::test_module(m); }
|
|
@ -24,5 +24,4 @@ def deprecated_call():
|
|||
pytest_major_minor = (int(pieces[0]), int(pieces[1]))
|
||||
if pytest_major_minor < (3, 9):
|
||||
return pytest.warns((DeprecationWarning, PendingDeprecationWarning))
|
||||
else:
|
||||
return pytest.deprecated_call()
|
||||
return pytest.deprecated_call()
|
||||
|
|
|
@ -12,6 +12,16 @@ import zipfile
|
|||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
|
||||
|
||||
PKGCONFIG = """\
|
||||
prefix=${{pcfiledir}}/../../
|
||||
includedir=${{prefix}}/include
|
||||
|
||||
Name: pybind11
|
||||
Description: Seamless operability between C++11 and Python
|
||||
Version: {VERSION}
|
||||
Cflags: -I${{includedir}}
|
||||
"""
|
||||
|
||||
|
||||
main_headers = {
|
||||
"include/pybind11/attr.h",
|
||||
|
@ -33,6 +43,7 @@ main_headers = {
|
|||
"include/pybind11/pytypes.h",
|
||||
"include/pybind11/stl.h",
|
||||
"include/pybind11/stl_bind.h",
|
||||
"include/pybind11/type_caster_pyobject_ptr.h",
|
||||
}
|
||||
|
||||
detail_headers = {
|
||||
|
@ -45,6 +56,12 @@ detail_headers = {
|
|||
"include/pybind11/detail/typeid.h",
|
||||
}
|
||||
|
||||
eigen_headers = {
|
||||
"include/pybind11/eigen/common.h",
|
||||
"include/pybind11/eigen/matrix.h",
|
||||
"include/pybind11/eigen/tensor.h",
|
||||
}
|
||||
|
||||
stl_headers = {
|
||||
"include/pybind11/stl/filesystem.h",
|
||||
}
|
||||
|
@ -59,6 +76,10 @@ cmake_files = {
|
|||
"share/cmake/pybind11/pybind11Tools.cmake",
|
||||
}
|
||||
|
||||
pkgconfig_files = {
|
||||
"share/pkgconfig/pybind11.pc",
|
||||
}
|
||||
|
||||
py_files = {
|
||||
"__init__.py",
|
||||
"__main__.py",
|
||||
|
@ -68,8 +89,8 @@ py_files = {
|
|||
"setup_helpers.py",
|
||||
}
|
||||
|
||||
headers = main_headers | detail_headers | stl_headers
|
||||
src_files = headers | cmake_files
|
||||
headers = main_headers | detail_headers | eigen_headers | stl_headers
|
||||
src_files = headers | cmake_files | pkgconfig_files
|
||||
all_files = src_files | py_files
|
||||
|
||||
|
||||
|
@ -78,10 +99,12 @@ sdist_files = {
|
|||
"pybind11/include",
|
||||
"pybind11/include/pybind11",
|
||||
"pybind11/include/pybind11/detail",
|
||||
"pybind11/include/pybind11/eigen",
|
||||
"pybind11/include/pybind11/stl",
|
||||
"pybind11/share",
|
||||
"pybind11/share/cmake",
|
||||
"pybind11/share/cmake/pybind11",
|
||||
"pybind11/share/pkgconfig",
|
||||
"pyproject.toml",
|
||||
"setup.cfg",
|
||||
"setup.py",
|
||||
|
@ -89,6 +112,7 @@ sdist_files = {
|
|||
"MANIFEST.in",
|
||||
"README.rst",
|
||||
"PKG-INFO",
|
||||
"SECURITY.md",
|
||||
}
|
||||
|
||||
local_sdist_files = {
|
||||
|
@ -101,22 +125,24 @@ local_sdist_files = {
|
|||
}
|
||||
|
||||
|
||||
def test_build_sdist(monkeypatch, tmpdir):
|
||||
def read_tz_file(tar: tarfile.TarFile, name: str) -> bytes:
|
||||
start = tar.getnames()[0] + "/"
|
||||
inner_file = tar.extractfile(tar.getmember(f"{start}{name}"))
|
||||
assert inner_file
|
||||
with contextlib.closing(inner_file) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def normalize_line_endings(value: bytes) -> bytes:
|
||||
return value.replace(os.linesep.encode("utf-8"), b"\n")
|
||||
|
||||
|
||||
def test_build_sdist(monkeypatch, tmpdir):
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"build",
|
||||
"--sdist",
|
||||
"--outdir",
|
||||
str(tmpdir),
|
||||
]
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "build", "--sdist", f"--outdir={tmpdir}"], check=True
|
||||
)
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist,) = tmpdir.visit("*.tar.gz")
|
||||
|
||||
|
@ -125,25 +151,17 @@ def test_build_sdist(monkeypatch, tmpdir):
|
|||
version = start[9:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))
|
||||
) as f:
|
||||
setup_py = f.read()
|
||||
setup_py = read_tz_file(tar, "setup.py")
|
||||
pyproject_toml = read_tz_file(tar, "pyproject.toml")
|
||||
pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc")
|
||||
cmake_cfg = read_tz_file(
|
||||
tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake"
|
||||
)
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "pyproject.toml"))
|
||||
) as f:
|
||||
pyproject_toml = f.read()
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(
|
||||
tar.getmember(
|
||||
start + "pybind11/share/cmake/pybind11/pybind11Config.cmake"
|
||||
)
|
||||
)
|
||||
) as f:
|
||||
contents = f.read().decode("utf8")
|
||||
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
|
||||
assert (
|
||||
'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")'
|
||||
in cmake_cfg.decode("utf-8")
|
||||
)
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= sdist_files
|
||||
|
@ -154,9 +172,9 @@ def test_build_sdist(monkeypatch, tmpdir):
|
|||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f:
|
||||
contents = (
|
||||
string.Template(f.read().decode())
|
||||
string.Template(f.read().decode("utf-8"))
|
||||
.substitute(version=version, extra_cmd="")
|
||||
.encode()
|
||||
.encode("utf-8")
|
||||
)
|
||||
assert setup_py == contents
|
||||
|
||||
|
@ -164,25 +182,18 @@ def test_build_sdist(monkeypatch, tmpdir):
|
|||
contents = f.read()
|
||||
assert pyproject_toml == contents
|
||||
|
||||
simple_version = ".".join(version.split(".")[:3])
|
||||
pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8")
|
||||
assert normalize_line_endings(pkgconfig) == pkgconfig_expected
|
||||
|
||||
|
||||
def test_build_global_dist(monkeypatch, tmpdir):
|
||||
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"build",
|
||||
"--sdist",
|
||||
"--outdir",
|
||||
str(tmpdir),
|
||||
]
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir)], check=True
|
||||
)
|
||||
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist,) = tmpdir.visit("*.tar.gz")
|
||||
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
|
@ -190,15 +201,17 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
|||
version = start[16:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))
|
||||
) as f:
|
||||
setup_py = f.read()
|
||||
setup_py = read_tz_file(tar, "setup.py")
|
||||
pyproject_toml = read_tz_file(tar, "pyproject.toml")
|
||||
pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc")
|
||||
cmake_cfg = read_tz_file(
|
||||
tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake"
|
||||
)
|
||||
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "pyproject.toml"))
|
||||
) as f:
|
||||
pyproject_toml = f.read()
|
||||
assert (
|
||||
'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")'
|
||||
in cmake_cfg.decode("utf-8")
|
||||
)
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= sdist_files
|
||||
|
@ -209,7 +222,7 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
|||
contents = (
|
||||
string.Template(f.read().decode())
|
||||
.substitute(version=version, extra_cmd="")
|
||||
.encode()
|
||||
.encode("utf-8")
|
||||
)
|
||||
assert setup_py == contents
|
||||
|
||||
|
@ -217,12 +230,16 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
|||
contents = f.read()
|
||||
assert pyproject_toml == contents
|
||||
|
||||
simple_version = ".".join(version.split(".")[:3])
|
||||
pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8")
|
||||
assert normalize_line_endings(pkgconfig) == pkgconfig_expected
|
||||
|
||||
|
||||
def tests_build_wheel(monkeypatch, tmpdir):
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
|
||||
subprocess.check_output(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)]
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True
|
||||
)
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
@ -249,8 +266,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
|
|||
monkeypatch.chdir(MAIN_DIR)
|
||||
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
|
||||
|
||||
subprocess.check_output(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)]
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True
|
||||
)
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
|
|
@ -89,6 +89,12 @@ PYBIND11_MODULE(pybind11_tests, m) {
|
|||
#endif
|
||||
m.attr("cpp_std") = cpp_std();
|
||||
m.attr("PYBIND11_INTERNALS_ID") = PYBIND11_INTERNALS_ID;
|
||||
m.attr("PYBIND11_SIMPLE_GIL_MANAGEMENT") =
|
||||
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
bind_ConstructorStats(m);
|
||||
|
||||
|
|
|
@ -6,4 +6,4 @@ numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10"
|
|||
pytest==7.0.0
|
||||
pytest-timeout
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
|
||||
scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10"
|
||||
scipy==1.10.0; platform_python_implementation!="PyPy" and python_version=="3.10"
|
||||
|
|
|
@ -4,7 +4,7 @@ asyncio = pytest.importorskip("asyncio")
|
|||
m = pytest.importorskip("pybind11_tests.async_module")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def event_loop():
|
||||
loop = asyncio.new_event_loop()
|
||||
yield loop
|
||||
|
@ -16,7 +16,7 @@ async def get_await_result(x):
|
|||
|
||||
|
||||
def test_await(event_loop):
|
||||
assert 5 == event_loop.run_until_complete(get_await_result(m.SupportsAsync()))
|
||||
assert event_loop.run_until_complete(get_await_result(m.SupportsAsync())) == 5
|
||||
|
||||
|
||||
def test_await_missing(event_loop):
|
||||
|
|
|
@ -7,12 +7,47 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/complex.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
TEST_SUBMODULE(buffers, m) {
|
||||
m.attr("long_double_and_double_have_same_size") = (sizeof(long double) == sizeof(double));
|
||||
|
||||
m.def("format_descriptor_format_buffer_info_equiv",
|
||||
[](const std::string &cpp_name, const py::buffer &buffer) {
|
||||
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
|
||||
static auto *format_table = new std::map<std::string, std::string>;
|
||||
static auto *equiv_table
|
||||
= new std::map<std::string, bool (py::buffer_info::*)() const>;
|
||||
if (format_table->empty()) {
|
||||
#define PYBIND11_ASSIGN_HELPER(...) \
|
||||
(*format_table)[#__VA_ARGS__] = py::format_descriptor<__VA_ARGS__>::format(); \
|
||||
(*equiv_table)[#__VA_ARGS__] = &py::buffer_info::item_type_is_equivalent_to<__VA_ARGS__>;
|
||||
PYBIND11_ASSIGN_HELPER(PyObject *)
|
||||
PYBIND11_ASSIGN_HELPER(bool)
|
||||
PYBIND11_ASSIGN_HELPER(std::int8_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::uint8_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::int16_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::uint16_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::int32_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::uint32_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::int64_t)
|
||||
PYBIND11_ASSIGN_HELPER(std::uint64_t)
|
||||
PYBIND11_ASSIGN_HELPER(float)
|
||||
PYBIND11_ASSIGN_HELPER(double)
|
||||
PYBIND11_ASSIGN_HELPER(long double)
|
||||
PYBIND11_ASSIGN_HELPER(std::complex<float>)
|
||||
PYBIND11_ASSIGN_HELPER(std::complex<double>)
|
||||
PYBIND11_ASSIGN_HELPER(std::complex<long double>)
|
||||
#undef PYBIND11_ASSIGN_HELPER
|
||||
}
|
||||
return std::pair<std::string, bool>(
|
||||
(*format_table)[cpp_name], (buffer.request().*((*equiv_table)[cpp_name]))());
|
||||
});
|
||||
|
||||
// test_from_python / test_to_python:
|
||||
class Matrix {
|
||||
public:
|
||||
|
|
|
@ -10,6 +10,63 @@ from pybind11_tests import buffers as m
|
|||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
if m.long_double_and_double_have_same_size:
|
||||
# Determined by the compiler used to build the pybind11 tests
|
||||
# (e.g. MSVC gets here, but MinGW might not).
|
||||
np_float128 = None
|
||||
np_complex256 = None
|
||||
else:
|
||||
# Determined by the compiler used to build numpy (e.g. MinGW).
|
||||
np_float128 = getattr(np, *["float128"] * 2)
|
||||
np_complex256 = getattr(np, *["complex256"] * 2)
|
||||
|
||||
CPP_NAME_FORMAT_NP_DTYPE_TABLE = [
|
||||
("PyObject *", "O", object),
|
||||
("bool", "?", np.bool_),
|
||||
("std::int8_t", "b", np.int8),
|
||||
("std::uint8_t", "B", np.uint8),
|
||||
("std::int16_t", "h", np.int16),
|
||||
("std::uint16_t", "H", np.uint16),
|
||||
("std::int32_t", "i", np.int32),
|
||||
("std::uint32_t", "I", np.uint32),
|
||||
("std::int64_t", "q", np.int64),
|
||||
("std::uint64_t", "Q", np.uint64),
|
||||
("float", "f", np.float32),
|
||||
("double", "d", np.float64),
|
||||
("long double", "g", np_float128),
|
||||
("std::complex<float>", "Zf", np.complex64),
|
||||
("std::complex<double>", "Zd", np.complex128),
|
||||
("std::complex<long double>", "Zg", np_complex256),
|
||||
]
|
||||
CPP_NAME_FORMAT_TABLE = [
|
||||
(cpp_name, format)
|
||||
for cpp_name, format, np_dtype in CPP_NAME_FORMAT_NP_DTYPE_TABLE
|
||||
if np_dtype is not None
|
||||
]
|
||||
CPP_NAME_NP_DTYPE_TABLE = [
|
||||
(cpp_name, np_dtype) for cpp_name, _, np_dtype in CPP_NAME_FORMAT_NP_DTYPE_TABLE
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("cpp_name", "np_dtype"), CPP_NAME_NP_DTYPE_TABLE)
|
||||
def test_format_descriptor_format_buffer_info_equiv(cpp_name, np_dtype):
|
||||
if np_dtype is None:
|
||||
pytest.skip(
|
||||
f"cpp_name=`{cpp_name}`: `long double` and `double` have same size."
|
||||
)
|
||||
if isinstance(np_dtype, str):
|
||||
pytest.skip(f"np.{np_dtype} does not exist.")
|
||||
np_array = np.array([], dtype=np_dtype)
|
||||
for other_cpp_name, expected_format in CPP_NAME_FORMAT_TABLE:
|
||||
format, np_array_is_matching = m.format_descriptor_format_buffer_info_equiv(
|
||||
other_cpp_name, np_array
|
||||
)
|
||||
assert format == expected_format
|
||||
if other_cpp_name == cpp_name:
|
||||
assert np_array_is_matching
|
||||
else:
|
||||
assert not np_array_is_matching
|
||||
|
||||
|
||||
def test_from_python():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
|
@ -54,7 +111,8 @@ def test_to_python():
|
|||
mat2 = np.array(mat, copy=False)
|
||||
assert mat2.shape == (5, 4)
|
||||
assert abs(mat2).sum() == 11
|
||||
assert mat2[2, 3] == 4 and mat2[3, 2] == 7
|
||||
assert mat2[2, 3] == 4
|
||||
assert mat2[3, 2] == 7
|
||||
mat2[2, 3] = 5
|
||||
assert mat2[2, 3] == 5
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
||||
TEST_SUBMODULE(builtin_casters, m) {
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
// test_simple_string
|
||||
m.def("string_roundtrip", [](const char *s) { return s; });
|
||||
|
||||
|
@ -86,7 +89,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
std::wstring wstr;
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x2e18); // ⸘
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
if (sizeof(wchar_t) == 2) {
|
||||
wstr.push_back(mathbfA16_1);
|
||||
wstr.push_back(mathbfA16_2);
|
||||
} // 𝐀, utf16
|
||||
|
@ -113,7 +116,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
// Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger
|
||||
// UnicodeDecodeError
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
if (sizeof(wchar_t) == 2) {
|
||||
m.def("bad_wchar_string", [=]() {
|
||||
return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
|
||||
});
|
||||
|
@ -266,9 +269,14 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
});
|
||||
m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; });
|
||||
|
||||
static std::pair<int, std::string> int_string_pair{2, "items"};
|
||||
m.def(
|
||||
"int_string_pair", []() { return &int_string_pair; }, py::return_value_policy::reference);
|
||||
"int_string_pair",
|
||||
[]() {
|
||||
// Using no-destructor idiom to side-step warnings from overzealous compilers.
|
||||
static auto *int_string_pair = new std::pair<int, std::string>{2, "items"};
|
||||
return int_string_pair;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// test_builtins_cast_return_none
|
||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
||||
|
@ -379,4 +387,6 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; });
|
||||
m.def("takes_const_ref_wrap",
|
||||
[](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
}
|
||||
|
|
|
@ -126,8 +126,8 @@ def test_bytes_to_string():
|
|||
|
||||
assert m.strlen(b"hi") == 2
|
||||
assert m.string_length(b"world") == 5
|
||||
assert m.string_length("a\x00b".encode()) == 3
|
||||
assert m.strlen("a\x00b".encode()) == 1 # C-string limitation
|
||||
assert m.string_length(b"a\x00b") == 3
|
||||
assert m.strlen(b"a\x00b") == 1 # C-string limitation
|
||||
|
||||
# passing in a utf8 encoded string should work
|
||||
assert m.string_length("💩".encode()) == 4
|
||||
|
@ -421,13 +421,15 @@ def test_reference_wrapper():
|
|||
a2 = m.refwrap_list(copy=True)
|
||||
assert [x.value for x in a1] == [2, 3]
|
||||
assert [x.value for x in a2] == [2, 3]
|
||||
assert not a1[0] is a2[0] and not a1[1] is a2[1]
|
||||
assert a1[0] is not a2[0]
|
||||
assert a1[1] is not a2[1]
|
||||
|
||||
b1 = m.refwrap_list(copy=False)
|
||||
b2 = m.refwrap_list(copy=False)
|
||||
assert [x.value for x in b1] == [1, 2]
|
||||
assert [x.value for x in b2] == [1, 2]
|
||||
assert b1[0] is b2[0] and b1[1] is b2[1]
|
||||
assert b1[0] is b2[0]
|
||||
assert b1[1] is b2[1]
|
||||
|
||||
assert m.refwrap_iiw(IncType(5)) == 5
|
||||
assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10]
|
||||
|
|
|
@ -240,4 +240,41 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
f();
|
||||
}
|
||||
});
|
||||
|
||||
auto *custom_def = []() {
|
||||
static PyMethodDef def;
|
||||
def.ml_name = "example_name";
|
||||
def.ml_doc = "Example doc";
|
||||
def.ml_meth = [](PyObject *, PyObject *args) -> PyObject * {
|
||||
if (PyTuple_Size(args) != 1) {
|
||||
throw std::runtime_error("Invalid number of arguments for example_name");
|
||||
}
|
||||
PyObject *first = PyTuple_GetItem(args, 0);
|
||||
if (!PyLong_Check(first)) {
|
||||
throw std::runtime_error("Invalid argument to example_name");
|
||||
}
|
||||
auto result = py::cast(PyLong_AsLong(first) * 9);
|
||||
return result.release().ptr();
|
||||
};
|
||||
def.ml_flags = METH_VARARGS;
|
||||
return &def;
|
||||
}();
|
||||
|
||||
// rec_capsule with name that has the same value (but not pointer) as our internal one
|
||||
// This capsule should be detected by our code as foreign and not inspected as the pointers
|
||||
// shouldn't match
|
||||
constexpr const char *rec_capsule_name
|
||||
= pybind11::detail::internals_function_record_capsule_name;
|
||||
py::capsule rec_capsule(std::malloc(1), [](void *data) { std::free(data); });
|
||||
rec_capsule.set_name(rec_capsule_name);
|
||||
m.add_object("custom_function", PyCFunction_New(custom_def, rec_capsule.ptr()));
|
||||
|
||||
// This test requires a new ABI version to pass
|
||||
#if PYBIND11_INTERNALS_VERSION > 4
|
||||
// rec_capsule with nullptr name
|
||||
py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); });
|
||||
m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr()));
|
||||
#else
|
||||
m.add_object("custom_function2", py::none());
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import pytest
|
|||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import callbacks as m
|
||||
from pybind11_tests import detailed_error_messages_enabled
|
||||
|
||||
|
||||
def test_callbacks():
|
||||
|
@ -70,11 +71,20 @@ def test_keyword_args_and_generalized_unpacking():
|
|||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.test_arg_conversion_error1(f)
|
||||
assert "Unable to convert call argument" in str(excinfo.value)
|
||||
assert str(excinfo.value) == "Unable to convert call argument " + (
|
||||
"'1' of type 'UnregisteredType' to Python object"
|
||||
if detailed_error_messages_enabled
|
||||
else "'1' to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.test_arg_conversion_error2(f)
|
||||
assert "Unable to convert call argument" in str(excinfo.value)
|
||||
assert str(excinfo.value) == "Unable to convert call argument " + (
|
||||
"'expected_name' of type 'UnregisteredType' to Python object"
|
||||
if detailed_error_messages_enabled
|
||||
else "'expected_name' to Python object "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
||||
)
|
||||
|
||||
|
||||
def test_lambda_closure_cleanup():
|
||||
|
@ -193,3 +203,16 @@ def test_callback_num_times():
|
|||
if len(rates) > 1:
|
||||
print("Min Mean Max")
|
||||
print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}")
|
||||
|
||||
|
||||
def test_custom_func():
|
||||
assert m.custom_function(4) == 36
|
||||
assert m.roundtrip(m.custom_function)(4) == 36
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
m.custom_function2 is None, reason="Current PYBIND11_INTERNALS_VERSION too low"
|
||||
)
|
||||
def test_custom_func2():
|
||||
assert m.custom_function2(3) == 27
|
||||
assert m.roundtrip(m.custom_function2)(3) == 27
|
||||
|
|
|
@ -7,7 +7,6 @@ from pybind11_tests import chrono as m
|
|||
|
||||
|
||||
def test_chrono_system_clock():
|
||||
|
||||
# Get the time from both c++ and datetime
|
||||
date0 = datetime.datetime.today()
|
||||
date1 = m.test_chrono1()
|
||||
|
@ -122,7 +121,6 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
|
|||
|
||||
|
||||
def test_chrono_duration_roundtrip():
|
||||
|
||||
# Get the difference between two times (a timedelta)
|
||||
date1 = datetime.datetime.today()
|
||||
date2 = datetime.datetime.today()
|
||||
|
@ -143,7 +141,6 @@ def test_chrono_duration_roundtrip():
|
|||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence():
|
||||
|
||||
date1 = datetime.datetime.today()
|
||||
date2 = datetime.datetime.today()
|
||||
|
||||
|
@ -154,7 +151,6 @@ def test_chrono_duration_subtraction_equivalence():
|
|||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence_date():
|
||||
|
||||
date1 = datetime.date.today()
|
||||
date2 = datetime.date.today()
|
||||
|
||||
|
|
|
@ -22,10 +22,8 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable : 4324)
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4324)
|
||||
// warning C4324: structure was padded due to alignment specifier
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
|
@ -36,7 +34,29 @@ struct NoBraceInitialization {
|
|||
std::vector<int> vec;
|
||||
};
|
||||
|
||||
namespace test_class {
|
||||
namespace pr4220_tripped_over_this { // PR #4227
|
||||
|
||||
template <int>
|
||||
struct SoEmpty {};
|
||||
|
||||
template <typename T>
|
||||
std::string get_msg(const T &) {
|
||||
return "This is really only meant to exercise successful compilation.";
|
||||
}
|
||||
|
||||
using Empty0 = SoEmpty<0x0>;
|
||||
|
||||
void bind_empty0(py::module_ &m) {
|
||||
py::class_<Empty0>(m, "Empty0").def(py::init<>()).def("get_msg", get_msg<Empty0>);
|
||||
}
|
||||
|
||||
} // namespace pr4220_tripped_over_this
|
||||
} // namespace test_class
|
||||
|
||||
TEST_SUBMODULE(class_, m) {
|
||||
m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); });
|
||||
|
||||
// test_instance
|
||||
struct NoConstructor {
|
||||
NoConstructor() = default;
|
||||
|
@ -65,7 +85,7 @@ TEST_SUBMODULE(class_, m) {
|
|||
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
||||
|
||||
py::class_<NoConstructorNew>(m, "NoConstructorNew")
|
||||
.def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__
|
||||
.def(py::init([]() { return nullptr; })) // Need a NOOP __init__
|
||||
.def_static("__new__",
|
||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||
|
||||
|
@ -364,6 +384,8 @@ TEST_SUBMODULE(class_, m) {
|
|||
|
||||
protected:
|
||||
virtual int foo() const { return value; }
|
||||
virtual void *void_foo() { return static_cast<void *>(&value); }
|
||||
virtual void *get_self() { return static_cast<void *>(this); }
|
||||
|
||||
private:
|
||||
int value = 42;
|
||||
|
@ -372,6 +394,8 @@ TEST_SUBMODULE(class_, m) {
|
|||
class TrampolineB : public ProtectedB {
|
||||
public:
|
||||
int foo() const override { PYBIND11_OVERRIDE(int, ProtectedB, foo, ); }
|
||||
void *void_foo() override { PYBIND11_OVERRIDE(void *, ProtectedB, void_foo, ); }
|
||||
void *get_self() override { PYBIND11_OVERRIDE(void *, ProtectedB, get_self, ); }
|
||||
};
|
||||
|
||||
class PublicistB : public ProtectedB {
|
||||
|
@ -381,11 +405,23 @@ TEST_SUBMODULE(class_, m) {
|
|||
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
|
||||
~PublicistB() override{}; // NOLINT(modernize-use-equals-default)
|
||||
using ProtectedB::foo;
|
||||
using ProtectedB::get_self;
|
||||
using ProtectedB::void_foo;
|
||||
};
|
||||
|
||||
m.def("read_foo", [](const void *original) {
|
||||
const int *ptr = reinterpret_cast<const int *>(original);
|
||||
return *ptr;
|
||||
});
|
||||
|
||||
m.def("pointers_equal",
|
||||
[](const void *original, const void *comparison) { return original == comparison; });
|
||||
|
||||
py::class_<ProtectedB, TrampolineB>(m, "ProtectedB")
|
||||
.def(py::init<>())
|
||||
.def("foo", &PublicistB::foo);
|
||||
.def("foo", &PublicistB::foo)
|
||||
.def("void_foo", &PublicistB::void_foo)
|
||||
.def("get_self", &PublicistB::get_self);
|
||||
|
||||
// test_brace_initialization
|
||||
struct BraceInitialization {
|
||||
|
@ -517,6 +553,8 @@ TEST_SUBMODULE(class_, m) {
|
|||
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
|
||||
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
|
||||
});
|
||||
|
||||
test_class::pr4220_tripped_over_this::bind_empty0(m);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
import env
|
||||
from pybind11_tests import ConstructorStats, UserType
|
||||
from pybind11_tests import class_ as m
|
||||
|
||||
|
||||
def test_obj_class_name():
|
||||
expected_name = "UserType" if env.PYPY else "pybind11_tests.UserType"
|
||||
assert m.obj_class_name(UserType(1)) == expected_name
|
||||
assert m.obj_class_name(UserType) == expected_name
|
||||
|
||||
|
||||
def test_repr():
|
||||
assert "pybind11_type" in repr(type(UserType))
|
||||
assert "UserType" in repr(UserType)
|
||||
|
@ -23,7 +29,7 @@ def test_instance(msg):
|
|||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_instance_new(msg):
|
||||
def test_instance_new():
|
||||
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
|
||||
cstats = ConstructorStats.get(m.NoConstructorNew)
|
||||
assert cstats.alive() == 1
|
||||
|
@ -176,7 +182,6 @@ def test_inheritance(msg):
|
|||
|
||||
|
||||
def test_inheritance_init(msg):
|
||||
|
||||
# Single base
|
||||
class Python(m.Pet):
|
||||
def __init__(self):
|
||||
|
@ -213,7 +218,7 @@ def test_automatic_upcasting():
|
|||
|
||||
|
||||
def test_isinstance():
|
||||
objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
|
||||
objects = [(), {}, m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
|
||||
expected = (True, True, True, True, True, False, False)
|
||||
assert m.check_instances(objects) == expected
|
||||
|
||||
|
@ -313,6 +318,8 @@ def test_bind_protected_functions():
|
|||
|
||||
b = m.ProtectedB()
|
||||
assert b.foo() == 42
|
||||
assert m.read_foo(b.void_foo()) == 42
|
||||
assert m.pointers_equal(b.get_self(), b)
|
||||
|
||||
class C(m.ProtectedB):
|
||||
def __init__(self):
|
||||
|
@ -417,7 +424,7 @@ def test_exception_rvalue_abort():
|
|||
|
||||
|
||||
# https://github.com/pybind/pybind11/issues/1568
|
||||
def test_multiple_instances_with_same_pointer(capture):
|
||||
def test_multiple_instances_with_same_pointer():
|
||||
n = 100
|
||||
instances = [m.SamePointer() for _ in range(n)]
|
||||
for i in range(n):
|
||||
|
@ -469,3 +476,10 @@ def test_register_duplicate_class():
|
|||
m.register_duplicate_nested_class_type(ClassScope)
|
||||
expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!'
|
||||
assert str(exc_info.value) == expected
|
||||
|
||||
|
||||
def test_pr4220_tripped_over_this():
|
||||
assert (
|
||||
m.Empty0().get_msg()
|
||||
== "This is really only meant to exercise successful compilation."
|
||||
)
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# Built-in in CMake 3.5+
|
||||
include(CMakeParseArguments)
|
||||
|
||||
add_custom_target(test_cmake_build)
|
||||
|
||||
function(pybind11_add_build_test name)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
project(test_installed_embed CXX)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(test_installed_module CXX)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
project(test_installed_function CXX)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
project(test_installed_target CXX)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
project(test_subdirectory_embed CXX)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
project(test_subdirectory_function CXX)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
|
||||
# The `cmake_minimum_required(VERSION 3.5...3.26)` 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.18)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.26)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.26)
|
||||
endif()
|
||||
|
||||
project(test_subdirectory_target CXX)
|
||||
|
|
|
@ -3,9 +3,9 @@ import pytest
|
|||
from pybind11_tests import const_name as m
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests))
|
||||
@pytest.mark.parametrize("func", [m.const_name_tests, m.underscore_tests])
|
||||
@pytest.mark.parametrize(
|
||||
"selector, expected",
|
||||
("selector", "expected"),
|
||||
enumerate(
|
||||
(
|
||||
"",
|
||||
|
|
|
@ -52,15 +52,12 @@ int f1(int x) noexcept { return x + 1; }
|
|||
#endif
|
||||
int f2(int x) noexcept(true) { return x + 2; }
|
||||
int f3(int x) noexcept(false) { return x + 3; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
PYBIND11_WARNING_POP
|
||||
struct C {
|
||||
int m1(int x) noexcept { return x - 1; }
|
||||
int m2(int x) const noexcept { return x - 2; }
|
||||
|
@ -68,17 +65,14 @@ struct C {
|
|||
int m4(int x) const noexcept(true) { return x - 4; }
|
||||
int m5(int x) noexcept(false) { return x - 5; }
|
||||
int m6(int x) const noexcept(false) { return x - 6; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m7(int x) throw() { return x - 7; }
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m8(int x) const throw() { return x - 8; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
PYBIND11_WARNING_POP
|
||||
};
|
||||
} // namespace test_exc_sp
|
||||
|
||||
|
@ -126,14 +120,12 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
|||
.def("m8", &C::m8);
|
||||
m.def("f1", f1);
|
||||
m.def("f2", f2);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning push
|
||||
# pragma warning disable 878 // incompatible exception specifications
|
||||
#endif
|
||||
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_INTEL(878) // incompatible exception specifications
|
||||
m.def("f3", f3);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning pop
|
||||
#endif
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
m.def("f4", f4);
|
||||
|
||||
// test_function_record_leaks
|
||||
|
@ -156,4 +148,7 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
|||
py::arg_v("y", 42, "<the answer>"),
|
||||
py::arg_v("z", default_value));
|
||||
});
|
||||
|
||||
// test noexcept(true) lambda (#4565)
|
||||
m.def("l1", []() noexcept(true) { return 0; });
|
||||
}
|
||||
|
|
|
@ -50,3 +50,7 @@ def test_function_record_leaks():
|
|||
m.register_large_capture_with_invalid_arguments(m)
|
||||
with pytest.raises(RuntimeError):
|
||||
m.register_with_raising_repr(m, RaisingRepr())
|
||||
|
||||
|
||||
def test_noexcept_lambda():
|
||||
assert m.l1() == 0
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template <typename derived>
|
||||
struct empty {
|
||||
static const derived &get_one() { return instance_; }
|
||||
|
@ -293,3 +295,239 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
|||
// Make sure that cast from pytype rvalue to other pytype works
|
||||
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
|
||||
}
|
||||
|
||||
/*
|
||||
* Rest of the file:
|
||||
* static_assert based tests for pybind11 adaptations of
|
||||
* std::is_move_constructible, std::is_copy_constructible and
|
||||
* std::is_copy_assignable (no adaptation of std::is_move_assignable).
|
||||
* Difference between pybind11 and std traits: pybind11 traits will also check
|
||||
* the contained value_types.
|
||||
*/
|
||||
|
||||
struct NotMovable {
|
||||
NotMovable() = default;
|
||||
NotMovable(NotMovable const &) = default;
|
||||
NotMovable(NotMovable &&) = delete;
|
||||
NotMovable &operator=(NotMovable const &) = default;
|
||||
NotMovable &operator=(NotMovable &&) = delete;
|
||||
};
|
||||
static_assert(!std::is_move_constructible<NotMovable>::value,
|
||||
"!std::is_move_constructible<NotMovable>::value");
|
||||
static_assert(std::is_copy_constructible<NotMovable>::value,
|
||||
"std::is_copy_constructible<NotMovable>::value");
|
||||
static_assert(!pybind11::detail::is_move_constructible<NotMovable>::value,
|
||||
"!pybind11::detail::is_move_constructible<NotMovable>::value");
|
||||
static_assert(pybind11::detail::is_copy_constructible<NotMovable>::value,
|
||||
"pybind11::detail::is_copy_constructible<NotMovable>::value");
|
||||
static_assert(!std::is_move_assignable<NotMovable>::value,
|
||||
"!std::is_move_assignable<NotMovable>::value");
|
||||
static_assert(std::is_copy_assignable<NotMovable>::value,
|
||||
"std::is_copy_assignable<NotMovable>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotMovable>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotMovable>::value");
|
||||
static_assert(pybind11::detail::is_copy_assignable<NotMovable>::value,
|
||||
"pybind11::detail::is_copy_assignable<NotMovable>::value");
|
||||
|
||||
struct NotCopyable {
|
||||
NotCopyable() = default;
|
||||
NotCopyable(NotCopyable const &) = delete;
|
||||
NotCopyable(NotCopyable &&) = default;
|
||||
NotCopyable &operator=(NotCopyable const &) = delete;
|
||||
NotCopyable &operator=(NotCopyable &&) = default;
|
||||
};
|
||||
static_assert(std::is_move_constructible<NotCopyable>::value,
|
||||
"std::is_move_constructible<NotCopyable>::value");
|
||||
static_assert(!std::is_copy_constructible<NotCopyable>::value,
|
||||
"!std::is_copy_constructible<NotCopyable>::value");
|
||||
static_assert(pybind11::detail::is_move_constructible<NotCopyable>::value,
|
||||
"pybind11::detail::is_move_constructible<NotCopyable>::value");
|
||||
static_assert(!pybind11::detail::is_copy_constructible<NotCopyable>::value,
|
||||
"!pybind11::detail::is_copy_constructible<NotCopyable>::value");
|
||||
static_assert(std::is_move_assignable<NotCopyable>::value,
|
||||
"std::is_move_assignable<NotCopyable>::value");
|
||||
static_assert(!std::is_copy_assignable<NotCopyable>::value,
|
||||
"!std::is_copy_assignable<NotCopyable>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotCopyable>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotCopyable>::value");
|
||||
static_assert(!pybind11::detail::is_copy_assignable<NotCopyable>::value,
|
||||
"!pybind11::detail::is_copy_assignable<NotCopyable>::value");
|
||||
|
||||
struct NotCopyableNotMovable {
|
||||
NotCopyableNotMovable() = default;
|
||||
NotCopyableNotMovable(NotCopyableNotMovable const &) = delete;
|
||||
NotCopyableNotMovable(NotCopyableNotMovable &&) = delete;
|
||||
NotCopyableNotMovable &operator=(NotCopyableNotMovable const &) = delete;
|
||||
NotCopyableNotMovable &operator=(NotCopyableNotMovable &&) = delete;
|
||||
};
|
||||
static_assert(!std::is_move_constructible<NotCopyableNotMovable>::value,
|
||||
"!std::is_move_constructible<NotCopyableNotMovable>::value");
|
||||
static_assert(!std::is_copy_constructible<NotCopyableNotMovable>::value,
|
||||
"!std::is_copy_constructible<NotCopyableNotMovable>::value");
|
||||
static_assert(!pybind11::detail::is_move_constructible<NotCopyableNotMovable>::value,
|
||||
"!pybind11::detail::is_move_constructible<NotCopyableNotMovable>::value");
|
||||
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableNotMovable>::value,
|
||||
"!pybind11::detail::is_copy_constructible<NotCopyableNotMovable>::value");
|
||||
static_assert(!std::is_move_assignable<NotCopyableNotMovable>::value,
|
||||
"!std::is_move_assignable<NotCopyableNotMovable>::value");
|
||||
static_assert(!std::is_copy_assignable<NotCopyableNotMovable>::value,
|
||||
"!std::is_copy_assignable<NotCopyableNotMovable>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableNotMovable>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotCopyableNotMovable>::value");
|
||||
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableNotMovable>::value,
|
||||
"!pybind11::detail::is_copy_assignable<NotCopyableNotMovable>::value");
|
||||
|
||||
struct NotMovableVector : std::vector<NotMovable> {};
|
||||
static_assert(std::is_move_constructible<NotMovableVector>::value,
|
||||
"std::is_move_constructible<NotMovableVector>::value");
|
||||
static_assert(std::is_copy_constructible<NotMovableVector>::value,
|
||||
"std::is_copy_constructible<NotMovableVector>::value");
|
||||
static_assert(!pybind11::detail::is_move_constructible<NotMovableVector>::value,
|
||||
"!pybind11::detail::is_move_constructible<NotMovableVector>::value");
|
||||
static_assert(pybind11::detail::is_copy_constructible<NotMovableVector>::value,
|
||||
"pybind11::detail::is_copy_constructible<NotMovableVector>::value");
|
||||
static_assert(std::is_move_assignable<NotMovableVector>::value,
|
||||
"std::is_move_assignable<NotMovableVector>::value");
|
||||
static_assert(std::is_copy_assignable<NotMovableVector>::value,
|
||||
"std::is_copy_assignable<NotMovableVector>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotMovableVector>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotMovableVector>::value");
|
||||
static_assert(pybind11::detail::is_copy_assignable<NotMovableVector>::value,
|
||||
"pybind11::detail::is_copy_assignable<NotMovableVector>::value");
|
||||
|
||||
struct NotCopyableVector : std::vector<NotCopyable> {};
|
||||
static_assert(std::is_move_constructible<NotCopyableVector>::value,
|
||||
"std::is_move_constructible<NotCopyableVector>::value");
|
||||
static_assert(std::is_copy_constructible<NotCopyableVector>::value,
|
||||
"std::is_copy_constructible<NotCopyableVector>::value");
|
||||
static_assert(pybind11::detail::is_move_constructible<NotCopyableVector>::value,
|
||||
"pybind11::detail::is_move_constructible<NotCopyableVector>::value");
|
||||
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableVector>::value,
|
||||
"!pybind11::detail::is_copy_constructible<NotCopyableVector>::value");
|
||||
static_assert(std::is_move_assignable<NotCopyableVector>::value,
|
||||
"std::is_move_assignable<NotCopyableVector>::value");
|
||||
static_assert(std::is_copy_assignable<NotCopyableVector>::value,
|
||||
"std::is_copy_assignable<NotCopyableVector>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableVector>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotCopyableVector>::value");
|
||||
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableVector>::value,
|
||||
"!pybind11::detail::is_copy_assignable<NotCopyableVector>::value");
|
||||
|
||||
struct NotCopyableNotMovableVector : std::vector<NotCopyableNotMovable> {};
|
||||
static_assert(std::is_move_constructible<NotCopyableNotMovableVector>::value,
|
||||
"std::is_move_constructible<NotCopyableNotMovableVector>::value");
|
||||
static_assert(std::is_copy_constructible<NotCopyableNotMovableVector>::value,
|
||||
"std::is_copy_constructible<NotCopyableNotMovableVector>::value");
|
||||
static_assert(!pybind11::detail::is_move_constructible<NotCopyableNotMovableVector>::value,
|
||||
"!pybind11::detail::is_move_constructible<NotCopyableNotMovableVector>::value");
|
||||
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableNotMovableVector>::value,
|
||||
"!pybind11::detail::is_copy_constructible<NotCopyableNotMovableVector>::value");
|
||||
static_assert(std::is_move_assignable<NotCopyableNotMovableVector>::value,
|
||||
"std::is_move_assignable<NotCopyableNotMovableVector>::value");
|
||||
static_assert(std::is_copy_assignable<NotCopyableNotMovableVector>::value,
|
||||
"std::is_copy_assignable<NotCopyableNotMovableVector>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableNotMovableVector>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotCopyableNotMovableVector>::value");
|
||||
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableNotMovableVector>::value,
|
||||
"!pybind11::detail::is_copy_assignable<NotCopyableNotMovableVector>::value");
|
||||
|
||||
struct NotMovableMap : std::map<int, NotMovable> {};
|
||||
static_assert(std::is_move_constructible<NotMovableMap>::value,
|
||||
"std::is_move_constructible<NotMovableMap>::value");
|
||||
static_assert(std::is_copy_constructible<NotMovableMap>::value,
|
||||
"std::is_copy_constructible<NotMovableMap>::value");
|
||||
static_assert(!pybind11::detail::is_move_constructible<NotMovableMap>::value,
|
||||
"!pybind11::detail::is_move_constructible<NotMovableMap>::value");
|
||||
static_assert(pybind11::detail::is_copy_constructible<NotMovableMap>::value,
|
||||
"pybind11::detail::is_copy_constructible<NotMovableMap>::value");
|
||||
static_assert(std::is_move_assignable<NotMovableMap>::value,
|
||||
"std::is_move_assignable<NotMovableMap>::value");
|
||||
static_assert(std::is_copy_assignable<NotMovableMap>::value,
|
||||
"std::is_copy_assignable<NotMovableMap>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotMovableMap>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotMovableMap>::value");
|
||||
static_assert(pybind11::detail::is_copy_assignable<NotMovableMap>::value,
|
||||
"pybind11::detail::is_copy_assignable<NotMovableMap>::value");
|
||||
|
||||
struct NotCopyableMap : std::map<int, NotCopyable> {};
|
||||
static_assert(std::is_move_constructible<NotCopyableMap>::value,
|
||||
"std::is_move_constructible<NotCopyableMap>::value");
|
||||
static_assert(std::is_copy_constructible<NotCopyableMap>::value,
|
||||
"std::is_copy_constructible<NotCopyableMap>::value");
|
||||
static_assert(pybind11::detail::is_move_constructible<NotCopyableMap>::value,
|
||||
"pybind11::detail::is_move_constructible<NotCopyableMap>::value");
|
||||
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableMap>::value,
|
||||
"!pybind11::detail::is_copy_constructible<NotCopyableMap>::value");
|
||||
static_assert(std::is_move_assignable<NotCopyableMap>::value,
|
||||
"std::is_move_assignable<NotCopyableMap>::value");
|
||||
static_assert(std::is_copy_assignable<NotCopyableMap>::value,
|
||||
"std::is_copy_assignable<NotCopyableMap>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableMap>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotCopyableMap>::value");
|
||||
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableMap>::value,
|
||||
"!pybind11::detail::is_copy_assignable<NotCopyableMap>::value");
|
||||
|
||||
struct NotCopyableNotMovableMap : std::map<int, NotCopyableNotMovable> {};
|
||||
static_assert(std::is_move_constructible<NotCopyableNotMovableMap>::value,
|
||||
"std::is_move_constructible<NotCopyableNotMovableMap>::value");
|
||||
static_assert(std::is_copy_constructible<NotCopyableNotMovableMap>::value,
|
||||
"std::is_copy_constructible<NotCopyableNotMovableMap>::value");
|
||||
static_assert(!pybind11::detail::is_move_constructible<NotCopyableNotMovableMap>::value,
|
||||
"!pybind11::detail::is_move_constructible<NotCopyableNotMovableMap>::value");
|
||||
static_assert(!pybind11::detail::is_copy_constructible<NotCopyableNotMovableMap>::value,
|
||||
"!pybind11::detail::is_copy_constructible<NotCopyableNotMovableMap>::value");
|
||||
static_assert(std::is_move_assignable<NotCopyableNotMovableMap>::value,
|
||||
"std::is_move_assignable<NotCopyableNotMovableMap>::value");
|
||||
static_assert(std::is_copy_assignable<NotCopyableNotMovableMap>::value,
|
||||
"std::is_copy_assignable<NotCopyableNotMovableMap>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<NotCopyableNotMovableMap>::value,
|
||||
// "!pybind11::detail::is_move_assignable<NotCopyableNotMovableMap>::value");
|
||||
static_assert(!pybind11::detail::is_copy_assignable<NotCopyableNotMovableMap>::value,
|
||||
"!pybind11::detail::is_copy_assignable<NotCopyableNotMovableMap>::value");
|
||||
|
||||
struct RecursiveVector : std::vector<RecursiveVector> {};
|
||||
static_assert(std::is_move_constructible<RecursiveVector>::value,
|
||||
"std::is_move_constructible<RecursiveVector>::value");
|
||||
static_assert(std::is_copy_constructible<RecursiveVector>::value,
|
||||
"std::is_copy_constructible<RecursiveVector>::value");
|
||||
static_assert(pybind11::detail::is_move_constructible<RecursiveVector>::value,
|
||||
"pybind11::detail::is_move_constructible<RecursiveVector>::value");
|
||||
static_assert(pybind11::detail::is_copy_constructible<RecursiveVector>::value,
|
||||
"pybind11::detail::is_copy_constructible<RecursiveVector>::value");
|
||||
static_assert(std::is_move_assignable<RecursiveVector>::value,
|
||||
"std::is_move_assignable<RecursiveVector>::value");
|
||||
static_assert(std::is_copy_assignable<RecursiveVector>::value,
|
||||
"std::is_copy_assignable<RecursiveVector>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<RecursiveVector>::value,
|
||||
// "!pybind11::detail::is_move_assignable<RecursiveVector>::value");
|
||||
static_assert(pybind11::detail::is_copy_assignable<RecursiveVector>::value,
|
||||
"pybind11::detail::is_copy_assignable<RecursiveVector>::value");
|
||||
|
||||
struct RecursiveMap : std::map<int, RecursiveMap> {};
|
||||
static_assert(std::is_move_constructible<RecursiveMap>::value,
|
||||
"std::is_move_constructible<RecursiveMap>::value");
|
||||
static_assert(std::is_copy_constructible<RecursiveMap>::value,
|
||||
"std::is_copy_constructible<RecursiveMap>::value");
|
||||
static_assert(pybind11::detail::is_move_constructible<RecursiveMap>::value,
|
||||
"pybind11::detail::is_move_constructible<RecursiveMap>::value");
|
||||
static_assert(pybind11::detail::is_copy_constructible<RecursiveMap>::value,
|
||||
"pybind11::detail::is_copy_constructible<RecursiveMap>::value");
|
||||
static_assert(std::is_move_assignable<RecursiveMap>::value,
|
||||
"std::is_move_assignable<RecursiveMap>::value");
|
||||
static_assert(std::is_copy_assignable<RecursiveMap>::value,
|
||||
"std::is_copy_assignable<RecursiveMap>::value");
|
||||
// pybind11 does not have this
|
||||
// static_assert(!pybind11::detail::is_move_assignable<RecursiveMap>::value,
|
||||
// "!pybind11::detail::is_move_assignable<RecursiveMap>::value");
|
||||
static_assert(pybind11::detail::is_copy_assignable<RecursiveMap>::value,
|
||||
"pybind11::detail::is_copy_assignable<RecursiveMap>::value");
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
};
|
||||
class ArgAlwaysConverts {};
|
||||
|
||||
namespace pybind11 {
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<ArgInspector1> {
|
||||
|
@ -74,7 +74,7 @@ public:
|
|||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
|
||||
// test_custom_caster_destruction
|
||||
class DestructionTester {
|
||||
|
@ -92,7 +92,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
};
|
||||
namespace pybind11 {
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<DestructionTester> {
|
||||
|
@ -104,7 +104,7 @@ struct type_caster<DestructionTester> {
|
|||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
|
||||
// Define type caster outside of `pybind11::detail` and then alias it.
|
||||
namespace other_lib {
|
||||
|
@ -112,7 +112,7 @@ struct MyType {};
|
|||
// Corrupt `py` shorthand alias for surrounding context.
|
||||
namespace py {}
|
||||
// Corrupt unqualified relative `pybind11` namespace.
|
||||
namespace pybind11 {}
|
||||
namespace PYBIND11_NAMESPACE {}
|
||||
// Correct alias.
|
||||
namespace py_ = ::pybind11;
|
||||
// Define caster. This is effectively no-op, we only ensure it compiles and we
|
||||
|
@ -127,12 +127,12 @@ struct my_caster {
|
|||
};
|
||||
} // namespace other_lib
|
||||
// Effectively "alias" it into correct namespace (via inheritance).
|
||||
namespace pybind11 {
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
|
||||
TEST_SUBMODULE(custom_type_casters, m) {
|
||||
// test_custom_type_casters
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue