mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 22:52:01 +00:00
Merge branch 'master' into stable
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
This commit is contained in:
commit
0ba639d617
@ -1,6 +1,6 @@
|
||||
version: 1.0.{build}
|
||||
image:
|
||||
- Visual Studio 2015
|
||||
- Visual Studio 2017
|
||||
test: off
|
||||
skip_branch_with_pr: true
|
||||
build:
|
||||
@ -11,11 +11,9 @@ environment:
|
||||
matrix:
|
||||
- PYTHON: 36
|
||||
CONFIG: Debug
|
||||
- PYTHON: 27
|
||||
CONFIG: Debug
|
||||
install:
|
||||
- ps: |
|
||||
$env:CMAKE_GENERATOR = "Visual Studio 14 2015"
|
||||
$env:CMAKE_GENERATOR = "Visual Studio 15 2017"
|
||||
if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" }
|
||||
$env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH"
|
||||
python -W ignore -m pip install --upgrade pip wheel
|
||||
|
123
.clang-tidy
123
.clang-tidy
@ -1,72 +1,75 @@
|
||||
FormatStyle: file
|
||||
|
||||
Checks: '
|
||||
*bugprone*,
|
||||
clang-analyzer-optin.performance.Padding,
|
||||
clang-analyzer-optin.cplusplus.VirtualCall,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-prefer-member-initializer,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-slicing,
|
||||
google-explicit-constructor,
|
||||
llvm-namespace-comment,
|
||||
misc-misplaced-const,
|
||||
misc-non-copyable-objects,
|
||||
misc-static-assert,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-uniqueptr-reset-release,
|
||||
misc-unused-parameters,
|
||||
modernize-avoid-bind,
|
||||
modernize-make-shared,
|
||||
modernize-redundant-void-arg,
|
||||
modernize-replace-auto-ptr,
|
||||
modernize-replace-disallow-copy-and-assign-macro,
|
||||
modernize-replace-random-shuffle,
|
||||
modernize-shrink-to-fit,
|
||||
modernize-use-auto,
|
||||
modernize-use-bool-literals,
|
||||
modernize-use-equals-default,
|
||||
modernize-use-equals-delete,
|
||||
modernize-use-default-member-init,
|
||||
modernize-use-noexcept,
|
||||
modernize-use-emplace,
|
||||
modernize-use-override,
|
||||
modernize-use-using,
|
||||
*performance*,
|
||||
readability-avoid-const-params-in-decls,
|
||||
readability-braces-around-statements,
|
||||
readability-const-return-type,
|
||||
readability-container-size-empty,
|
||||
readability-delete-null-pointer,
|
||||
readability-else-after-return,
|
||||
readability-implicit-bool-conversion,
|
||||
readability-inconsistent-declaration-parameter-name,
|
||||
readability-make-member-function-const,
|
||||
readability-misplaced-array-index,
|
||||
readability-non-const-parameter,
|
||||
readability-qualified-auto,
|
||||
readability-redundant-function-ptr-dereference,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-string-compare,
|
||||
readability-suspicious-call-argument,
|
||||
readability-uniqueptr-delete-release,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-reserved-identifier,
|
||||
-bugprone-unused-raii,
|
||||
'
|
||||
Checks: |
|
||||
*bugprone*,
|
||||
*performance*,
|
||||
clang-analyzer-optin.cplusplus.VirtualCall,
|
||||
clang-analyzer-optin.performance.Padding,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-prefer-member-initializer,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-slicing,
|
||||
google-explicit-constructor,
|
||||
llvm-namespace-comment,
|
||||
misc-definitions-in-headers,
|
||||
misc-misplaced-const,
|
||||
misc-non-copyable-objects,
|
||||
misc-static-assert,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-uniqueptr-reset-release,
|
||||
misc-unused-parameters,
|
||||
modernize-avoid-bind,
|
||||
modernize-loop-convert,
|
||||
modernize-make-shared,
|
||||
modernize-redundant-void-arg,
|
||||
modernize-replace-auto-ptr,
|
||||
modernize-replace-disallow-copy-and-assign-macro,
|
||||
modernize-replace-random-shuffle,
|
||||
modernize-shrink-to-fit,
|
||||
modernize-use-auto,
|
||||
modernize-use-bool-literals,
|
||||
modernize-use-default-member-init,
|
||||
modernize-use-emplace,
|
||||
modernize-use-equals-default,
|
||||
modernize-use-equals-delete,
|
||||
modernize-use-noexcept,
|
||||
modernize-use-nullptr,
|
||||
modernize-use-override,
|
||||
modernize-use-using,
|
||||
readability-avoid-const-params-in-decls,
|
||||
readability-braces-around-statements,
|
||||
readability-const-return-type,
|
||||
readability-container-size-empty,
|
||||
readability-delete-null-pointer,
|
||||
readability-else-after-return,
|
||||
readability-implicit-bool-conversion,
|
||||
readability-inconsistent-declaration-parameter-name,
|
||||
readability-make-member-function-const,
|
||||
readability-misplaced-array-index,
|
||||
readability-non-const-parameter,
|
||||
readability-qualified-auto,
|
||||
readability-redundant-function-ptr-dereference,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-string-compare,
|
||||
readability-suspicious-call-argument,
|
||||
readability-uniqueptr-delete-release,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-reserved-identifier,
|
||||
-bugprone-unused-raii,
|
||||
|
||||
CheckOptions:
|
||||
- key: performance-for-range-copy.WarnOnAllAutoCopies
|
||||
value: true
|
||||
- key: performance-inefficient-string-concatenation.StrictMode
|
||||
value: true
|
||||
- key: performance-unnecessary-value-param.AllowedTypes
|
||||
value: 'exception_ptr$;'
|
||||
- key: readability-implicit-bool-conversion.AllowPointerConditions
|
||||
value: true
|
||||
|
||||
HeaderFilterRegex: 'pybind11/.*h'
|
||||
|
||||
WarningsAsErrors: '*'
|
||||
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
docs/*.svg binary
|
19
.github/CONTRIBUTING.md
vendored
19
.github/CONTRIBUTING.md
vendored
@ -93,11 +93,10 @@ cmake --build build -j4
|
||||
|
||||
Tips:
|
||||
|
||||
* You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3
|
||||
only).
|
||||
* You can use `virtualenv` (faster, from PyPI) instead of `venv`.
|
||||
* You can select any name for your environment folder; if it contains "env" it
|
||||
will be ignored by git.
|
||||
* If you don’t have CMake 3.14+, just add “cmake” to the pip install command.
|
||||
* If you don't have CMake 3.14+, just add "cmake" to the pip install command.
|
||||
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+
|
||||
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
|
||||
FindPython uses `-DPython_ROOT_DIR=/path/to` or
|
||||
@ -105,7 +104,7 @@ Tips:
|
||||
|
||||
### Configuration options
|
||||
|
||||
In CMake, configuration options are given with “-D”. Options are stored in the
|
||||
In CMake, configuration options are given with "-D". Options are stored in the
|
||||
build directory, in the `CMakeCache.txt` file, so they are remembered for each
|
||||
build directory. Two selections are special - the generator, given with `-G`,
|
||||
and the compiler, which is selected based on environment variables `CXX` and
|
||||
@ -115,7 +114,7 @@ after the initial run.
|
||||
The valid options are:
|
||||
|
||||
* `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo
|
||||
* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+’s FindPython instead of the
|
||||
* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+'s FindPython instead of the
|
||||
classic, deprecated, custom FindPythonLibs
|
||||
* `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests)
|
||||
* `-DBUILD_TESTING=ON`: Enable the tests
|
||||
@ -236,12 +235,14 @@ 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:12
|
||||
docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:13
|
||||
apt-get update && apt-get install -y python3-dev python3-pytest
|
||||
cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-fix" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17
|
||||
cmake --build build -j 2 -- --keep-going
|
||||
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
|
||||
```
|
||||
|
||||
You can add `--fix` to the options list if you want.
|
||||
|
||||
### Include what you use
|
||||
|
||||
To run include what you use, install (`brew install include-what-you-use` on
|
||||
@ -257,7 +258,7 @@ The report is sent to stderr; you can pipe it into a file if you wish.
|
||||
### Build recipes
|
||||
|
||||
This builds with the Intel compiler (assuming it is in your path, along with a
|
||||
recent CMake and Python 3):
|
||||
recent CMake and Python):
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
|
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@ -5,12 +5,3 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
# Official actions have moving tags like v1
|
||||
# that are used, so they don't need updates here
|
||||
- dependency-name: "actions/checkout"
|
||||
- dependency-name: "actions/setup-python"
|
||||
- dependency-name: "actions/cache"
|
||||
- dependency-name: "actions/upload-artifact"
|
||||
- dependency-name: "actions/download-artifact"
|
||||
- dependency-name: "actions/labeler"
|
||||
|
32
.github/matchers/pylint.json
vendored
Normal file
32
.github/matchers/pylint.json
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"severity": "warning",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^:]+):(\\d+):(\\d+): ([A-DF-Z]\\d+): \\033\\[[\\d;]+m([^\\033]+).*$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"code": 4,
|
||||
"message": 5
|
||||
}
|
||||
],
|
||||
"owner": "pylint-warning"
|
||||
},
|
||||
{
|
||||
"severity": "error",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^:]+):(\\d+):(\\d+): (E\\d+): \\033\\[[\\d;]+m([^\\033]+).*$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"code": 4,
|
||||
"message": 5
|
||||
}
|
||||
],
|
||||
"owner": "pylint-error"
|
||||
}
|
||||
]
|
||||
}
|
219
.github/workflows/ci.yml
vendored
219
.github/workflows/ci.yml
vendored
@ -15,6 +15,8 @@ concurrency:
|
||||
|
||||
env:
|
||||
PIP_ONLY_BINARY: numpy
|
||||
FORCE_COLOR: 3
|
||||
PYTEST_TIMEOUT: 300
|
||||
|
||||
jobs:
|
||||
# This is the "main" test suite, which tests a large number of different
|
||||
@ -25,13 +27,13 @@ jobs:
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, windows-2022, macos-latest]
|
||||
python:
|
||||
- '2.7'
|
||||
- '3.5'
|
||||
- '3.6'
|
||||
- '3.9'
|
||||
- '3.10'
|
||||
- '3.11-dev'
|
||||
- 'pypy-3.7'
|
||||
- 'pypy-3.8'
|
||||
- 'pypy-3.9'
|
||||
|
||||
# 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
|
||||
@ -45,26 +47,26 @@ jobs:
|
||||
args: >
|
||||
-DPYBIND11_FINDPYTHON=ON
|
||||
-DCMAKE_CXX_FLAGS="-D_=1"
|
||||
- runs-on: windows-latest
|
||||
- runs-on: ubuntu-latest
|
||||
python: 'pypy-3.8'
|
||||
args: >
|
||||
-DPYBIND11_FINDPYTHON=ON
|
||||
- runs-on: windows-2019
|
||||
python: '3.6'
|
||||
args: >
|
||||
-DPYBIND11_FINDPYTHON=ON
|
||||
- runs-on: macos-latest
|
||||
python: 'pypy-2.7'
|
||||
# Inject a couple Windows 2019 runs
|
||||
- runs-on: windows-2019
|
||||
python: '3.9'
|
||||
- runs-on: windows-2019
|
||||
python: '2.7'
|
||||
|
||||
name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}"
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
@ -82,7 +84,7 @@ jobs:
|
||||
|
||||
- name: Cache wheels
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# This path is specific to macOS - we really only need it for PyPy NumPy wheels
|
||||
# See https://github.com/actions/cache/blob/master/examples.md#python---pip
|
||||
@ -168,27 +170,11 @@ jobs:
|
||||
- name: Interface test
|
||||
run: cmake --build build2 --target test_cmake_build
|
||||
|
||||
# Eventually Microsoft might have an action for setting up
|
||||
# MSVC, but for now, this action works:
|
||||
- name: Prepare compiler environment for Windows 🐍 2.7
|
||||
if: matrix.python == 2.7 && runner.os == 'Windows'
|
||||
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
# This makes two environment variables available in the following step(s)
|
||||
- name: Set Windows 🐍 2.7 environment variables
|
||||
if: matrix.python == 2.7 && runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "DISTUTILS_USE_SDK=1" >> $GITHUB_ENV
|
||||
echo "MSSdk=1" >> $GITHUB_ENV
|
||||
|
||||
# This makes sure the setup_helpers module can build packages using
|
||||
# setuptools
|
||||
- name: Setuptools helpers test
|
||||
run: pytest tests/extra_setuptools
|
||||
if: "!(matrix.python == '3.5' && matrix.runs-on == 'windows-2022')"
|
||||
if: "!(matrix.runs-on == 'windows-2022')"
|
||||
|
||||
|
||||
deadsnakes:
|
||||
@ -200,14 +186,14 @@ jobs:
|
||||
- python-version: "3.9"
|
||||
python-debug: true
|
||||
valgrind: true
|
||||
# - python-version: "3.11-dev"
|
||||
# python-debug: false
|
||||
- python-version: "3.11-dev"
|
||||
python-debug: false
|
||||
|
||||
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
|
||||
uses: deadsnakes/action@v2.1.1
|
||||
@ -220,7 +206,7 @@ jobs:
|
||||
|
||||
- name: Valgrind cache
|
||||
if: matrix.valgrind
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
id: cache-valgrind
|
||||
with:
|
||||
path: valgrind
|
||||
@ -295,12 +281,14 @@ jobs:
|
||||
std: 20
|
||||
- clang: 10
|
||||
std: 17
|
||||
- clang: 14
|
||||
std: 20
|
||||
|
||||
name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64"
|
||||
container: "silkeh/clang:${{ matrix.clang }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Add wget and python3
|
||||
run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev
|
||||
@ -330,11 +318,11 @@ jobs:
|
||||
# Testing NVCC; forces sources to behave like .cu files
|
||||
cuda:
|
||||
runs-on: ubuntu-latest
|
||||
name: "🐍 3.8 • CUDA 11 • Ubuntu 20.04"
|
||||
container: nvidia/cuda:11.0-devel-ubuntu20.04
|
||||
name: "🐍 3.8 • CUDA 11.2 • Ubuntu 20.04"
|
||||
container: nvidia/cuda:11.2.2-devel-ubuntu20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
|
||||
- name: Install 🐍 3
|
||||
@ -358,7 +346,7 @@ jobs:
|
||||
# container: centos:8
|
||||
#
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/checkout@v3
|
||||
#
|
||||
# - name: Add Python 3 and a few requirements
|
||||
# run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules
|
||||
@ -397,17 +385,17 @@ jobs:
|
||||
# Testing on CentOS 7 + PGI compilers, which seems to require more workarounds
|
||||
centos-nvhpc7:
|
||||
runs-on: ubuntu-latest
|
||||
name: "🐍 3 • CentOS7 / PGI 20.9 • x64"
|
||||
name: "🐍 3 • CentOS7 / PGI 22.3 • x64"
|
||||
container: centos:7
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Add Python 3 and a few requirements
|
||||
run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3
|
||||
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 -y install https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-20-9-20.9-1.x86_64.rpm https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-2020-20.9-1.x86_64.rpm
|
||||
run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.3
|
||||
|
||||
# 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
|
||||
@ -417,7 +405,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
source /etc/profile.d/modules.sh
|
||||
module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.9
|
||||
module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.3
|
||||
cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \
|
||||
-DCMAKE_CXX_STANDARD=11 \
|
||||
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \
|
||||
@ -462,7 +450,7 @@ jobs:
|
||||
container: "gcc:${{ matrix.gcc }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Add Python 3
|
||||
run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev
|
||||
@ -504,7 +492,7 @@ jobs:
|
||||
name: "🐍 3 • ICC latest • x64"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Add apt repo
|
||||
run: |
|
||||
@ -599,19 +587,25 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
centos:
|
||||
- centos7 # GCC 4.8
|
||||
- stream8
|
||||
container:
|
||||
- "centos:7" # GCC 4.8
|
||||
- "almalinux:8"
|
||||
- "almalinux:9"
|
||||
|
||||
name: "🐍 3 • CentOS ${{ matrix.centos }} • x64"
|
||||
container: "quay.io/centos/centos:${{ matrix.centos }}"
|
||||
name: "🐍 3 • ${{ matrix.container }} • x64"
|
||||
container: "${{ matrix.container }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Add Python 3
|
||||
- name: Add Python 3 (RHEL 7)
|
||||
if: matrix.container == 'centos:7'
|
||||
run: yum update -y && yum install -y python3-devel gcc-c++ make git
|
||||
|
||||
- name: Add Python 3 (RHEL 8+)
|
||||
if: matrix.container != 'centos:7'
|
||||
run: dnf update -y && dnf install -y python3-devel gcc-c++ make git
|
||||
|
||||
- name: Update pip
|
||||
run: python3 -m pip install --upgrade pip
|
||||
|
||||
@ -645,18 +639,18 @@ jobs:
|
||||
|
||||
# This tests an "install" with the CMake tools
|
||||
install-classic:
|
||||
name: "🐍 3.5 • Debian • x86 • Install"
|
||||
name: "🐍 3.7 • Debian • x86 • Install"
|
||||
runs-on: ubuntu-latest
|
||||
container: i386/debian:stretch
|
||||
container: i386/debian:buster
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v1 # Required to run inside docker
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip
|
||||
pip3 install "pytest==3.1.*"
|
||||
pip3 install "pytest==6.*"
|
||||
|
||||
- name: Configure for install
|
||||
run: >
|
||||
@ -687,15 +681,17 @@ jobs:
|
||||
|
||||
|
||||
# This verifies that the documentation is not horribly broken, and does a
|
||||
# basic sanity check on the SDist.
|
||||
# basic validation check on the SDist.
|
||||
doxygen:
|
||||
name: "Documentation build test"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Install Doxygen
|
||||
run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04
|
||||
@ -725,27 +721,25 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python:
|
||||
- 3.5
|
||||
- 3.6
|
||||
- 3.7
|
||||
- 3.8
|
||||
- 3.9
|
||||
- pypy-3.6
|
||||
|
||||
include:
|
||||
- python: 3.9
|
||||
args: -DCMAKE_CXX_STANDARD=20 -DDOWNLOAD_EIGEN=OFF
|
||||
args: -DCMAKE_CXX_STANDARD=20
|
||||
- python: 3.8
|
||||
args: -DCMAKE_CXX_STANDARD=17
|
||||
|
||||
name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}"
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
architecture: x86
|
||||
@ -777,25 +771,31 @@ jobs:
|
||||
- name: Python tests
|
||||
run: cmake --build build -t pytest
|
||||
|
||||
win32-msvc2015:
|
||||
name: "🐍 ${{ matrix.python }} • MSVC 2015 • x64"
|
||||
runs-on: windows-latest
|
||||
win32-debug:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python:
|
||||
- 2.7
|
||||
- 3.6
|
||||
- 3.7
|
||||
# todo: check/cpptest does not support 3.8+ yet
|
||||
- 3.8
|
||||
- 3.9
|
||||
|
||||
include:
|
||||
- python: 3.9
|
||||
args: -DCMAKE_CXX_STANDARD=20
|
||||
- python: 3.8
|
||||
args: -DCMAKE_CXX_STANDARD=17
|
||||
|
||||
name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}"
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup 🐍 ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
architecture: x86
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
@ -803,82 +803,73 @@ jobs:
|
||||
- name: Prepare MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
with:
|
||||
toolset: 14.0
|
||||
arch: x86
|
||||
|
||||
- name: Prepare env
|
||||
run: |
|
||||
python -m pip install -r tests/requirements.txt
|
||||
|
||||
# First build - C++11 mode and inplace
|
||||
- name: Configure
|
||||
- name: Configure ${{ matrix.args }}
|
||||
run: >
|
||||
cmake -S . -B build
|
||||
-G "Visual Studio 14 2015" -A x64
|
||||
-G "Visual Studio 16 2019" -A Win32
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
${{ matrix.args }}
|
||||
- name: Build C++11
|
||||
run: cmake --build build --config Debug -j 2
|
||||
|
||||
- name: Build C++14
|
||||
run: cmake --build build -j 2
|
||||
|
||||
- name: Run all checks
|
||||
run: cmake --build build -t check
|
||||
- name: Python tests
|
||||
run: cmake --build build --config Debug -t pytest
|
||||
|
||||
|
||||
win32-msvc2017:
|
||||
name: "🐍 ${{ matrix.python }} • MSVC 2017 • x64"
|
||||
runs-on: windows-2016
|
||||
windows-2022:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python:
|
||||
- 2.7
|
||||
- 3.5
|
||||
- 3.7
|
||||
std:
|
||||
- 14
|
||||
- 3.9
|
||||
|
||||
include:
|
||||
- python: 2.7
|
||||
std: 17
|
||||
args: >
|
||||
-DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR"
|
||||
- python: 3.7
|
||||
std: 17
|
||||
args: >
|
||||
-DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR"
|
||||
name: "🐍 ${{ matrix.python }} • MSVC 2022 C++20 • x64"
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup 🐍 ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Prepare env
|
||||
run: |
|
||||
python3 -m pip install -r tests/requirements.txt
|
||||
|
||||
- name: Update CMake
|
||||
uses: jwlawson/actions-setup-cmake@v1.12
|
||||
|
||||
- name: Prepare env
|
||||
run: |
|
||||
python -m pip install -r tests/requirements.txt
|
||||
|
||||
# First build - C++11 mode and inplace
|
||||
- name: Configure
|
||||
- name: Configure C++20
|
||||
run: >
|
||||
cmake -S . -B build
|
||||
-G "Visual Studio 15 2017" -A x64
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
|
||||
${{ matrix.args }}
|
||||
-DCMAKE_CXX_STANDARD=20
|
||||
|
||||
- name: Build ${{ matrix.std }}
|
||||
- name: Build C++20
|
||||
run: cmake --build build -j 2
|
||||
|
||||
- name: Run all checks
|
||||
run: cmake --build build -t check
|
||||
- name: Python tests
|
||||
run: cmake --build build --target pytest
|
||||
|
||||
- name: C++20 tests
|
||||
run: cmake --build build --target cpptest -j 2
|
||||
|
||||
- name: Interface test C++20
|
||||
run: cmake --build build --target test_cmake_build
|
||||
|
||||
mingw:
|
||||
name: "🐍 3 • windows-latest • ${{ matrix.sys }}"
|
||||
@ -909,7 +900,7 @@ jobs:
|
||||
mingw-w64-${{matrix.env}}-boost
|
||||
mingw-w64-${{matrix.env}}-catch
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Configure C++11
|
||||
# LTO leads to many undefined reference like
|
||||
|
14
.github/workflows/configure.yml
vendored
14
.github/workflows/configure.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, macos-latest, windows-latest]
|
||||
arch: [x64]
|
||||
cmake: ["3.21"]
|
||||
cmake: ["3.23"]
|
||||
|
||||
include:
|
||||
- runs-on: ubuntu-latest
|
||||
@ -29,22 +29,18 @@ jobs:
|
||||
arch: x64
|
||||
cmake: 3.7
|
||||
|
||||
- runs-on: windows-2016
|
||||
arch: x86
|
||||
cmake: 3.8
|
||||
|
||||
- runs-on: windows-2016
|
||||
arch: x86
|
||||
- runs-on: windows-2019
|
||||
arch: x64 # x86 compilers seem to be missing on 2019 image
|
||||
cmake: 3.18
|
||||
|
||||
name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.7
|
||||
architecture: ${{ matrix.arch }}
|
||||
|
19
.github/workflows/format.yml
vendored
19
.github/workflows/format.yml
vendored
@ -12,14 +12,21 @@ on:
|
||||
- stable
|
||||
- "v*"
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 3
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
name: Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: pre-commit/action@v2.0.3
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Add matchers
|
||||
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
with:
|
||||
# Slow hooks are marked with manual - slow is okay here, run them too
|
||||
extra_args: --hook-stage manual --all-files
|
||||
@ -29,9 +36,9 @@ jobs:
|
||||
# in .github/CONTRIBUTING.md and update as needed.
|
||||
name: Clang-Tidy
|
||||
runs-on: ubuntu-latest
|
||||
container: silkeh/clang:12
|
||||
container: silkeh/clang:13
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install requirements
|
||||
run: apt-get update && apt-get install -y python3-dev python3-pytest
|
||||
@ -39,7 +46,7 @@ jobs:
|
||||
- name: Configure
|
||||
run: >
|
||||
cmake -S . -B build
|
||||
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy)"
|
||||
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*"
|
||||
-DDOWNLOAD_EIGEN=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
|
26
.github/workflows/pip.yml
vendored
26
.github/workflows/pip.yml
vendored
@ -17,19 +17,19 @@ env:
|
||||
|
||||
jobs:
|
||||
# This builds the sdists and wheels and makes sure the files are exactly as
|
||||
# expected. Using Windows and Python 2.7, since that is often the most
|
||||
# expected. Using Windows and Python 3.6, since that is often the most
|
||||
# challenging matrix element.
|
||||
test-packaging:
|
||||
name: 🐍 2.7 • 📦 tests • windows-latest
|
||||
name: 🐍 3.6 • 📦 tests • windows-latest
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup 🐍 2.7
|
||||
uses: actions/setup-python@v2
|
||||
- name: Setup 🐍 3.6
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 2.7
|
||||
python-version: 3.6
|
||||
|
||||
- name: Prepare env
|
||||
run: |
|
||||
@ -46,10 +46,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup 🐍 3.8
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
@ -69,13 +69,13 @@ jobs:
|
||||
run: twine check dist/*
|
||||
|
||||
- name: Save standard package
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: standard
|
||||
path: dist/pybind11-*
|
||||
|
||||
- name: Save global package
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: global
|
||||
path: dist/pybind11_global-*
|
||||
@ -90,10 +90,12 @@ jobs:
|
||||
needs: [packaging]
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
# Downloads all to directories matching the artifact names
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
|
||||
- name: Publish standard package
|
||||
uses: pypa/gh-action-pypi-publish@v1.5.0
|
||||
|
6
.github/workflows/upstream.yml
vendored
6
.github/workflows/upstream.yml
vendored
@ -14,15 +14,15 @@ env:
|
||||
|
||||
jobs:
|
||||
standard:
|
||||
name: "🐍 3.11 dev • ubuntu-latest • x64"
|
||||
name: "🐍 3.11 latest internals • ubuntu-latest • x64"
|
||||
runs-on: ubuntu-latest
|
||||
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python 3.11
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11-dev"
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
repos:
|
||||
# Standard hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
rev: "v4.3.0"
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
@ -29,73 +29,92 @@ repos:
|
||||
- id: mixed-line-ending
|
||||
- id: requirements-txt-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: fix-encoding-pragma
|
||||
exclude: ^noxfile.py$
|
||||
|
||||
# Upgrade old Python syntax
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.0
|
||||
rev: "v2.37.1"
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py36-plus]
|
||||
|
||||
# Nicely sort includes
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.10.1
|
||||
rev: "5.10.1"
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
# Black, the code formatter, natively supports pre-commit
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.12b0 # Keep in sync with blacken-docs
|
||||
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.0
|
||||
rev: "v1.12.1"
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies:
|
||||
- black==21.12b0 # keep in sync with black hook
|
||||
- black==22.6.0 # keep in sync with black hook
|
||||
|
||||
# Changes tabs to spaces
|
||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||
rev: v1.1.10
|
||||
rev: "v1.3.0"
|
||||
hooks:
|
||||
- id: remove-tabs
|
||||
|
||||
- repo: https://github.com/sirosen/texthooks
|
||||
rev: "0.3.1"
|
||||
hooks:
|
||||
- id: fix-ligatures
|
||||
- id: fix-smartquotes
|
||||
|
||||
# Autoremoves unused imports
|
||||
- repo: https://github.com/hadialqattan/pycln
|
||||
rev: v1.1.0
|
||||
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.9.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
|
||||
|
||||
# Flake8 also supports pre-commit natively (same author)
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 4.0.1
|
||||
# Automatically remove noqa that are not used
|
||||
- repo: https://github.com/asottile/yesqa
|
||||
rev: "v1.3.0"
|
||||
hooks:
|
||||
- id: flake8
|
||||
- id: yesqa
|
||||
additional_dependencies: &flake8_dependencies
|
||||
- flake8-bugbear
|
||||
- pep8-naming
|
||||
exclude: ^(docs/.*|tools/.*)$
|
||||
|
||||
- repo: https://github.com/asottile/yesqa
|
||||
rev: v1.3.0
|
||||
# Flake8 also supports pre-commit natively (same author)
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: "4.0.1"
|
||||
hooks:
|
||||
- id: yesqa
|
||||
- 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
|
||||
rev: "v0.6.13"
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
additional_dependencies: [pyyaml]
|
||||
@ -104,44 +123,48 @@ repos:
|
||||
|
||||
# Check static types with mypy
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.931
|
||||
rev: "v0.961"
|
||||
hooks:
|
||||
- id: mypy
|
||||
# Running per-file misbehaves a bit, so just run on all files, it's fast
|
||||
pass_filenames: false
|
||||
additional_dependencies: [typed_ast]
|
||||
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.47"
|
||||
rev: "0.48"
|
||||
hooks:
|
||||
- id: check-manifest
|
||||
# This is a slow hook, so only run this if --hook-stage manual is passed
|
||||
stages: [manual]
|
||||
additional_dependencies: [cmake, ninja]
|
||||
|
||||
# Check for spelling
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.1.0
|
||||
rev: "v2.1.0"
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude: ".supp$"
|
||||
args: ["-L", "nd,ot,thist"]
|
||||
|
||||
# Check for common shell mistakes
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.8.0.3
|
||||
rev: "v0.8.0.4"
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
|
||||
# The original pybind11 checks for a few C++ style items
|
||||
# Disallow some common capitalization mistakes
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: disallow-caps
|
||||
name: Disallow improper capitalization
|
||||
language: pygrep
|
||||
entry: PyBind|Numpy|Cmake|CCache|PyTest
|
||||
exclude: .pre-commit-config.yaml
|
||||
exclude: ^\.pre-commit-config.yaml$
|
||||
|
||||
# Clang format the codebase automatically
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: "v13.0.0"
|
||||
rev: "v14.0.6"
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c++, c, cuda]
|
||||
|
@ -1,6 +1,5 @@
|
||||
recursive-include pybind11/include/pybind11 *.h
|
||||
recursive-include pybind11 *.py
|
||||
recursive-include pybind11 py.typed
|
||||
recursive-include pybind11 *.pyi
|
||||
include pybind11/share/cmake/pybind11/*.cmake
|
||||
include LICENSE README.rst pyproject.toml setup.py setup.cfg
|
||||
|
16
README.rst
16
README.rst
@ -32,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary
|
||||
dependency.
|
||||
|
||||
Think of this library as a tiny self-contained version of Boost.Python
|
||||
with everything stripped away that isn’t relevant for binding
|
||||
with everything stripped away that isn't relevant for binding
|
||||
generation. Without comments, the core header files only require ~4K
|
||||
lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++
|
||||
lines of code and depend on Python (3.6+, or PyPy) and the C++
|
||||
standard library. This compact implementation was possible thanks to
|
||||
some of the new C++11 language features (specifically: tuples, lambda
|
||||
functions and variadic templates). Since its creation, this library has
|
||||
@ -78,8 +78,8 @@ Goodies
|
||||
In addition to the core functionality, pybind11 provides some extra
|
||||
goodies:
|
||||
|
||||
- Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an
|
||||
implementation-agnostic interface.
|
||||
- Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic
|
||||
interface (pybind11 2.9 was the last version to support Python 2 and 3.5).
|
||||
|
||||
- It is possible to bind C++11 lambda functions with captured
|
||||
variables. The lambda capture data is stored inside the resulting
|
||||
@ -88,8 +88,8 @@ goodies:
|
||||
- pybind11 uses C++11 move constructors and move assignment operators
|
||||
whenever possible to efficiently transfer custom data types.
|
||||
|
||||
- It’s easy to expose the internal storage of custom data types through
|
||||
Pythons’ buffer protocols. This is handy e.g. for fast conversion
|
||||
- It's easy to expose the internal storage of custom data types through
|
||||
Pythons' buffer protocols. This is handy e.g. for fast conversion
|
||||
between C++ matrix classes like Eigen and NumPy without expensive
|
||||
copy operations.
|
||||
|
||||
@ -119,10 +119,10 @@ goodies:
|
||||
Supported compilers
|
||||
-------------------
|
||||
|
||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode’s clang, this is 5.0.0 or
|
||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or
|
||||
newer)
|
||||
2. GCC 4.8 or newer
|
||||
3. Microsoft Visual Studio 2015 Update 3 or newer
|
||||
3. Microsoft Visual Studio 2017 or newer
|
||||
4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI)
|
||||
5. Cygwin/GCC (previously tested on 2.5.1)
|
||||
6. NVCC (CUDA 11.0 tested in CI)
|
||||
|
@ -18,5 +18,4 @@ ALIASES += "endrst=\endverbatim"
|
||||
QUIET = YES
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
PREDEFINED = PY_MAJOR_VERSION=3 \
|
||||
PYBIND11_NOINLINE
|
||||
PREDEFINED = PYBIND11_NOINLINE
|
||||
|
3
docs/_static/css/custom.css
vendored
Normal file
3
docs/_static/css/custom.css
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.highlight .go {
|
||||
color: #707070;
|
||||
}
|
11
docs/_static/theme_overrides.css
vendored
11
docs/_static/theme_overrides.css
vendored
@ -1,11 +0,0 @@
|
||||
.wy-table-responsive table td,
|
||||
.wy-table-responsive table th {
|
||||
white-space: initial !important;
|
||||
}
|
||||
.rst-content table.docutils td {
|
||||
vertical-align: top !important;
|
||||
}
|
||||
div[class^='highlight'] pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
}
|
@ -167,5 +167,4 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
|
||||
.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and
|
||||
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires
|
||||
Python 3.6 (for ``__fspath__`` support).
|
||||
``os.PathLike`` is converted to ``std::filesystem::path``.
|
||||
|
@ -87,8 +87,6 @@ included to tell pybind11 how to visit the variant.
|
||||
|
||||
pybind11 only supports the modern implementation of ``boost::variant``
|
||||
which makes use of variadic templates. This requires Boost 1.56 or newer.
|
||||
Additionally, on Windows, MSVC 2017 is required because ``boost::variant``
|
||||
falls back to the old non-variadic implementation on MSVC 2015.
|
||||
|
||||
.. _opaque:
|
||||
|
||||
|
@ -1,14 +1,6 @@
|
||||
Strings, bytes and Unicode conversions
|
||||
######################################
|
||||
|
||||
.. note::
|
||||
|
||||
This section discusses string handling in terms of Python 3 strings. For
|
||||
Python 2.7, replace all occurrences of ``str`` with ``unicode`` and
|
||||
``bytes`` with ``str``. Python 2.7 users may find it best to use ``from
|
||||
__future__ import unicode_literals`` to avoid unintentionally using ``str``
|
||||
instead of ``unicode``.
|
||||
|
||||
Passing Python strings to C++
|
||||
=============================
|
||||
|
||||
@ -58,9 +50,9 @@ Passing bytes to C++
|
||||
--------------------
|
||||
|
||||
A Python ``bytes`` object will be passed to C++ functions that accept
|
||||
``std::string`` or ``char*`` *without* conversion. On Python 3, in order to
|
||||
make a function *only* accept ``bytes`` (and not ``str``), declare it as taking
|
||||
a ``py::bytes`` argument.
|
||||
``std::string`` or ``char*`` *without* conversion. In order to make a function
|
||||
*only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes``
|
||||
argument.
|
||||
|
||||
|
||||
Returning C++ strings to Python
|
||||
@ -204,11 +196,6 @@ decoded to Python ``str``.
|
||||
}
|
||||
);
|
||||
|
||||
.. warning::
|
||||
|
||||
Wide character strings may not work as described on Python 2.7 or Python
|
||||
3.3 compiled with ``--enable-unicode=ucs2``.
|
||||
|
||||
Strings in multibyte encodings such as Shift-JIS must transcoded to a
|
||||
UTF-8/16/32 before being returned to Python.
|
||||
|
||||
|
@ -133,14 +133,14 @@ a virtual method call.
|
||||
>>> from example import *
|
||||
>>> d = Dog()
|
||||
>>> call_go(d)
|
||||
u'woof! woof! woof! '
|
||||
'woof! woof! woof! '
|
||||
>>> class Cat(Animal):
|
||||
... def go(self, n_times):
|
||||
... return "meow! " * n_times
|
||||
...
|
||||
>>> c = Cat()
|
||||
>>> call_go(c)
|
||||
u'meow! meow! meow! '
|
||||
'meow! meow! meow! '
|
||||
|
||||
If you are defining a custom constructor in a derived Python class, you *must*
|
||||
ensure that you explicitly call the bound C++ constructor using ``__init__``,
|
||||
@ -813,26 +813,21 @@ An instance can now be pickled as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
import cPickle as pickle # Use cPickle on Python 2.7
|
||||
except ImportError:
|
||||
import pickle
|
||||
import pickle
|
||||
|
||||
p = Pickleable("test_value")
|
||||
p.setExtra(15)
|
||||
data = pickle.dumps(p, 2)
|
||||
data = pickle.dumps(p)
|
||||
|
||||
|
||||
.. note::
|
||||
Note that only the cPickle module is supported on Python 2.7.
|
||||
|
||||
The second argument to ``dumps`` is also crucial: it selects the pickle
|
||||
protocol version 2, since the older version 1 is not supported. Newer
|
||||
versions are also fine—for instance, specify ``-1`` to always use the
|
||||
latest available version. Beware: failure to follow these instructions
|
||||
will cause important pybind11 memory allocation routines to be skipped
|
||||
during unpickling, which will likely lead to memory corruption and/or
|
||||
segmentation faults.
|
||||
If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are
|
||||
not supported. Newer versions are also fine; for instance, specify ``-1`` to
|
||||
always use the latest available version. Beware: failure to follow these
|
||||
instructions will cause important pybind11 memory allocation routines to be
|
||||
skipped during unpickling, which will likely lead to memory corruption
|
||||
and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and
|
||||
version 4 for Python 3.8+.
|
||||
|
||||
.. seealso::
|
||||
|
||||
@ -849,11 +844,9 @@ Python normally uses references in assignments. Sometimes a real copy is needed
|
||||
to prevent changing all copies. The ``copy`` module [#f5]_ provides these
|
||||
capabilities.
|
||||
|
||||
On Python 3, a class with pickle support is automatically also (deep)copy
|
||||
A class with pickle support is automatically also (deep)copy
|
||||
compatible. However, performance can be improved by adding custom
|
||||
``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods
|
||||
are mandatory for (deep)copy compatibility, because pybind11 only supports
|
||||
cPickle.
|
||||
``__copy__`` and ``__deepcopy__`` methods.
|
||||
|
||||
For simple classes (deep)copy can be enabled by using the copy constructor,
|
||||
which should look as follows:
|
||||
@ -1125,13 +1118,6 @@ described trampoline:
|
||||
py::class_<A, Trampoline>(m, "A") // <-- `Trampoline` here
|
||||
.def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`!
|
||||
|
||||
.. note::
|
||||
|
||||
MSVC 2015 has a compiler bug (fixed in version 2017) which
|
||||
requires a more explicit function binding in the form of
|
||||
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
|
||||
where ``int (A::*)() const`` is the type of ``A::foo``.
|
||||
|
||||
Binding final classes
|
||||
=====================
|
||||
|
||||
|
@ -328,8 +328,8 @@ an invalid state.
|
||||
Chaining exceptions ('raise from')
|
||||
==================================
|
||||
|
||||
In Python 3.3 a mechanism for indicating that exceptions were caused by other
|
||||
exceptions was introduced:
|
||||
Python has a mechanism for indicating that exceptions were caused by other
|
||||
exceptions:
|
||||
|
||||
.. code-block:: py
|
||||
|
||||
@ -340,7 +340,7 @@ exceptions was introduced:
|
||||
|
||||
To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It
|
||||
sets the current python error indicator, so to continue propagating the exception
|
||||
you should ``throw py::error_already_set()`` (Python 3 only).
|
||||
you should ``throw py::error_already_set()``.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
|
@ -372,7 +372,7 @@ like so:
|
||||
Keyword-only arguments
|
||||
======================
|
||||
|
||||
Python 3 introduced keyword-only arguments by specifying an unnamed ``*``
|
||||
Python implements keyword-only arguments by specifying an unnamed ``*``
|
||||
argument in a function definition:
|
||||
|
||||
.. code-block:: python
|
||||
@ -395,19 +395,18 @@ argument annotations when registering the function:
|
||||
m.def("f", [](int a, int b) { /* ... */ },
|
||||
py::arg("a"), py::kw_only(), py::arg("b"));
|
||||
|
||||
Note that you currently cannot combine this with a ``py::args`` argument. This
|
||||
feature does *not* require Python 3 to work.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
As of pybind11 2.9, a ``py::args`` argument implies that any following arguments
|
||||
are keyword-only, as if ``py::kw_only()`` had been specified in the same
|
||||
relative location of the argument list as the ``py::args`` argument. The
|
||||
``py::kw_only()`` may be included to be explicit about this, but is not
|
||||
required. (Prior to 2.9 ``py::args`` may only occur at the end of the argument
|
||||
list, or immediately before a ``py::kwargs`` argument at the end).
|
||||
A ``py::args`` argument implies that any following arguments are keyword-only,
|
||||
as if ``py::kw_only()`` had been specified in the same relative location of the
|
||||
argument list as the ``py::args`` argument. The ``py::kw_only()`` may be
|
||||
included to be explicit about this, but is not required.
|
||||
|
||||
.. versionchanged:: 2.9
|
||||
This can now be combined with ``py::args``. Before, ``py::args`` could only
|
||||
occur at the end of the argument list, or immediately before a ``py::kwargs``
|
||||
argument at the end.
|
||||
|
||||
.. versionadded:: 2.9
|
||||
|
||||
Positional-only arguments
|
||||
=========================
|
||||
|
@ -87,7 +87,7 @@ buffer objects (e.g. a NumPy matrix).
|
||||
/* Request a buffer descriptor from Python */
|
||||
py::buffer_info info = b.request();
|
||||
|
||||
/* Some sanity checks ... */
|
||||
/* Some basic validation checks ... */
|
||||
if (info.format != py::format_descriptor<Scalar>::format())
|
||||
throw std::runtime_error("Incompatible format: expected a double array!");
|
||||
|
||||
@ -395,11 +395,9 @@ uses of ``py::array``:
|
||||
Ellipsis
|
||||
========
|
||||
|
||||
Python 3 provides a convenient ``...`` ellipsis notation that is often used to
|
||||
Python provides a convenient ``...`` ellipsis notation that is often used to
|
||||
slice multidimensional arrays. For instance, the following snippet extracts the
|
||||
middle dimensions of a tensor with the first and last index set to zero.
|
||||
In Python 2, the syntactic sugar ``...`` is not available, but the singleton
|
||||
``Ellipsis`` (of type ``ellipsis``) can still be used directly.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -414,8 +412,6 @@ operation on the C++ side:
|
||||
py::array a = /* A NumPy array */;
|
||||
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)];
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
``py::ellipsis()`` is now also available in Python 2.
|
||||
|
||||
Memory view
|
||||
===========
|
||||
@ -455,9 +451,5 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer:
|
||||
);
|
||||
})
|
||||
|
||||
.. note::
|
||||
|
||||
``memoryview::from_memory`` is not available in Python 2.
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
``memoryview::from_memory`` added.
|
||||
|
@ -32,8 +32,7 @@ The last line will both compile and run the tests.
|
||||
Windows
|
||||
-------
|
||||
|
||||
On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies
|
||||
on various C++11 language features that break older versions of Visual Studio.
|
||||
On Windows, only **Visual Studio 2017** and newer are supported.
|
||||
|
||||
.. Note::
|
||||
|
||||
@ -166,12 +165,12 @@ load and execute the example:
|
||||
.. code-block:: pycon
|
||||
|
||||
$ python
|
||||
Python 2.7.10 (default, Aug 22 2015, 20:33:39)
|
||||
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin
|
||||
Python 3.9.10 (main, Jan 15 2022, 11:48:04)
|
||||
[Clang 13.0.0 (clang-1300.0.29.3)] on darwin
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> import example
|
||||
>>> example.add(1, 2)
|
||||
3L
|
||||
3
|
||||
>>>
|
||||
|
||||
.. _keyword_args:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime as dt
|
||||
import os
|
||||
import random
|
||||
@ -12,20 +11,20 @@ def generate_dummy_code_pybind11(nclasses=10):
|
||||
bindings = ""
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += "class cl%03i;\n" % cl
|
||||
decl += f"class cl{cl:03};\n"
|
||||
decl += "\n"
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += "class cl%03i {\n" % cl
|
||||
decl += f"class {cl:03} {{\n"
|
||||
decl += "public:\n"
|
||||
bindings += ' py::class_<cl%03i>(m, "cl%03i")\n' % (cl, cl)
|
||||
bindings += f' py::class_<cl{cl:03}>(m, "cl{cl:03}")\n'
|
||||
for fn in range(nfns):
|
||||
ret = random.randint(0, nclasses - 1)
|
||||
params = [random.randint(0, nclasses - 1) for i in range(nargs)]
|
||||
decl += " cl%03i *fn_%03i(" % (ret, fn)
|
||||
decl += ", ".join("cl%03i *" % p for p in params)
|
||||
decl += f" cl{ret:03} *fn_{fn:03}("
|
||||
decl += ", ".join(f"cl{p:03} *" for p in params)
|
||||
decl += ");\n"
|
||||
bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn)
|
||||
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n'
|
||||
decl += "};\n\n"
|
||||
bindings += " ;\n"
|
||||
|
||||
@ -43,23 +42,20 @@ def generate_dummy_code_boost(nclasses=10):
|
||||
bindings = ""
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += "class cl%03i;\n" % cl
|
||||
decl += f"class cl{cl:03};\n"
|
||||
decl += "\n"
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += "class cl%03i {\n" % cl
|
||||
decl += "public:\n"
|
||||
bindings += ' py::class_<cl%03i>("cl%03i")\n' % (cl, cl)
|
||||
bindings += f' py::class_<cl{cl:03}>("cl{cl:03}")\n'
|
||||
for fn in range(nfns):
|
||||
ret = random.randint(0, nclasses - 1)
|
||||
params = [random.randint(0, nclasses - 1) for i in range(nargs)]
|
||||
decl += " cl%03i *fn_%03i(" % (ret, fn)
|
||||
decl += ", ".join("cl%03i *" % p for p in params)
|
||||
decl += f" cl{ret:03} *fn_{fn:03}("
|
||||
decl += ", ".join(f"cl{p:03} *" for p in params)
|
||||
decl += ");\n"
|
||||
bindings += (
|
||||
' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n'
|
||||
% (fn, cl, fn)
|
||||
)
|
||||
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy<py::manage_new_object>())\n'
|
||||
decl += "};\n\n"
|
||||
bindings += " ;\n"
|
||||
|
||||
@ -75,7 +71,7 @@ def generate_dummy_code_boost(nclasses=10):
|
||||
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
|
||||
print("{")
|
||||
for i in range(0, 10):
|
||||
nclasses = 2 ** i
|
||||
nclasses = 2**i
|
||||
with open("test.cpp", "w") as f:
|
||||
f.write(codegen(nclasses))
|
||||
n1 = dt.datetime.now()
|
||||
|
@ -9,49 +9,182 @@ 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.
|
||||
|
||||
IN DEVELOPMENT
|
||||
--------------
|
||||
Version 2.10.0 (Jul 15, 2022)
|
||||
-----------------------------
|
||||
|
||||
Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC
|
||||
2017 is limited due to availability of CI runners; we highly recommend MSVC
|
||||
2019 or 2022 be used.
|
||||
2019 or 2022 be used. Initial support added for Python 3.11.
|
||||
|
||||
New features:
|
||||
|
||||
* ``py::anyset`` & ``py::frozenset`` were added, with copying (cast) to
|
||||
``std::set`` (similar to ``set``).
|
||||
`#3901 <https://github.com/pybind/pybind11/pull/3901>`_
|
||||
|
||||
* Support bytearray casting to string.
|
||||
`#3707 <https://github.com/pybind/pybind11/pull/3707>`_
|
||||
|
||||
* ``type_caster<std::monostate>`` was added. ``std::monostate`` is a tag type
|
||||
that allows ``std::variant`` to act as an optional, or allows default
|
||||
construction of a ``std::variant`` holding a non-default constructible type.
|
||||
`#3818 <https://github.com/pybind/pybind11/pull/3818>`_
|
||||
|
||||
* Support bytearray casting to string.
|
||||
`#3707 <https://github.com/pybind/pybind11/pull/3707>`_
|
||||
* ``pybind11::capsule::set_name`` added to mutate the name of the capsule instance.
|
||||
`#3866 <https://github.com/pybind/pybind11/pull/3866>`_
|
||||
|
||||
* NumPy: dtype constructor from type number added, accessors corresponding to
|
||||
Python API ``dtype.num``, ``dtype.byteorder``, ``dtype.flags`` and
|
||||
``dtype.alignment`` added.
|
||||
`#3868 <https://github.com/pybind/pybind11/pull/3868>`_
|
||||
|
||||
|
||||
Changes:
|
||||
|
||||
* Python 2 support was removed completely.
|
||||
* Python 3.6 is now the minimum supported version.
|
||||
`#3688 <https://github.com/pybind/pybind11/pull/3688>`_
|
||||
`#3719 <https://github.com/pybind/pybind11/pull/3719>`_
|
||||
|
||||
* The minimum version for MSVC is now 2017.
|
||||
`#3722 <https://github.com/pybind/pybind11/pull/3722>`_
|
||||
|
||||
* Fix issues with CPython 3.11 betas and add to supported test matrix.
|
||||
`#3923 <https://github.com/pybind/pybind11/pull/3923>`_
|
||||
|
||||
* ``error_already_set`` is now safer and more performant, especially for
|
||||
exceptions with long tracebacks, by delaying computation.
|
||||
`#1895 <https://github.com/pybind/pybind11/pull/1895>`_
|
||||
|
||||
* Improve exception handling in python ``str`` bindings.
|
||||
`#3826 <https://github.com/pybind/pybind11/pull/3826>`_
|
||||
|
||||
* The bindings for capsules now have more consistent exception handling.
|
||||
`#3825 <https://github.com/pybind/pybind11/pull/3825>`_
|
||||
|
||||
* Fix exception handling when ``pybind11::weakref()`` fails.
|
||||
`#3739 <https://github.com/pybind/pybind11/pull/3739>`_
|
||||
* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can now be
|
||||
used to define classes in namespaces other than pybind11.
|
||||
`#3797 <https://github.com/pybind/pybind11/pull/3797>`_
|
||||
|
||||
* Error printing code now uses ``PYBIND11_DETAILED_ERROR_MESSAGES`` instead of
|
||||
requiring ``NDEBUG``, allowing use with release builds if desired.
|
||||
`#3913 <https://github.com/pybind/pybind11/pull/3913>`_
|
||||
|
||||
* Implicit conversion of the literal ``0`` to ``pybind11::handle`` is now disabled.
|
||||
`#4008 <https://github.com/pybind/pybind11/pull/4008>`_
|
||||
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can be used
|
||||
to define classes in namespaces other than pybind11.
|
||||
`#3797 <https://github.com/pybind/pybind11/pull/3797>`_
|
||||
* Fix exception handling when ``pybind11::weakref()`` fails.
|
||||
`#3739 <https://github.com/pybind/pybind11/pull/3739>`_
|
||||
|
||||
* ``module_::def_submodule`` was missing proper error handling. This is fixed now.
|
||||
`#3973 <https://github.com/pybind/pybind11/pull/3973>`_
|
||||
|
||||
* The behavior or ``error_already_set`` was made safer and the highly opaque
|
||||
"Unknown internal error occurred" message was replaced with a more helpful
|
||||
message.
|
||||
`#3982 <https://github.com/pybind/pybind11/pull/3982>`_
|
||||
|
||||
* ``error_already_set::what()`` now handles non-normalized exceptions correctly.
|
||||
`#3971 <https://github.com/pybind/pybind11/pull/3971>`_
|
||||
|
||||
* Support older C++ compilers where filesystem is not yet part of the standard
|
||||
library and is instead included in ``std::experimental::filesystem``.
|
||||
`#3840 <https://github.com/pybind/pybind11/pull/3840>`_
|
||||
|
||||
* Fix ``-Wfree-nonheap-object`` warnings produced by GCC by avoiding returning
|
||||
pointers to static objects with ``return_value_policy::take_ownership``.
|
||||
`#3946 <https://github.com/pybind/pybind11/pull/3946>`_
|
||||
|
||||
* Fix cast from pytype rvalue to another pytype.
|
||||
`#3949 <https://github.com/pybind/pybind11/pull/3949>`_
|
||||
|
||||
* Ensure proper behavior when garbage collecting classes with dynamic attributes in Python >=3.9.
|
||||
`#4051 <https://github.com/pybind/pybind11/pull/4051>`_
|
||||
|
||||
* A couple long-standing ``PYBIND11_NAMESPACE``
|
||||
``__attribute__((visibility("hidden")))`` inconsistencies are now fixed
|
||||
(affects only unusual environments).
|
||||
`#4043 <https://github.com/pybind/pybind11/pull/4043>`_
|
||||
|
||||
* ``pybind11::detail::get_internals()`` is now resilient to in-flight Python
|
||||
exceptions.
|
||||
`#3981 <https://github.com/pybind/pybind11/pull/3981>`_
|
||||
|
||||
* Arrays with a dimension of size 0 are now properly converted to dynamic Eigen
|
||||
matrices (more common in NumPy 1.23).
|
||||
`#4038 <https://github.com/pybind/pybind11/pull/4038>`_
|
||||
|
||||
* Avoid catching unrelated errors when importing NumPy.
|
||||
`#3974 <https://github.com/pybind/pybind11/pull/3974>`_
|
||||
|
||||
Performance and style:
|
||||
|
||||
* Added an accessor overload of ``(object &&key)`` to reference steal the
|
||||
object when using python types as keys. This prevents unnecessary reference
|
||||
count overhead for attr, dictionary, tuple, and sequence look ups. Added
|
||||
additional regression tests. Fixed a performance bug the caused accessor
|
||||
assignments to potentially perform unnecessary copies.
|
||||
`#3970 <https://github.com/pybind/pybind11/pull/3970>`_
|
||||
|
||||
* Perfect forward all args of ``make_iterator``.
|
||||
`#3980 <https://github.com/pybind/pybind11/pull/3980>`_
|
||||
|
||||
* Avoid potential bug in pycapsule destructor by adding an ``error_guard`` to
|
||||
one of the dtors.
|
||||
`#3958 <https://github.com/pybind/pybind11/pull/3958>`_
|
||||
|
||||
* Optimize dictionary access in ``strip_padding`` for numpy.
|
||||
`#3994 <https://github.com/pybind/pybind11/pull/3994>`_
|
||||
|
||||
* ``stl_bind.h`` bindings now take slice args as a const-ref.
|
||||
`#3852 <https://github.com/pybind/pybind11/pull/3852>`_
|
||||
|
||||
* Made slice constructor more consistent, and improve performance of some
|
||||
casters by allowing reference stealing.
|
||||
`#3845 <https://github.com/pybind/pybind11/pull/3845>`_
|
||||
|
||||
* Change numpy dtype from_args method to use const ref.
|
||||
`#3878 <https://github.com/pybind/pybind11/pull/3878>`_
|
||||
|
||||
* Follow rule of three to ensure ``PyErr_Restore`` is called only once.
|
||||
`#3872 <https://github.com/pybind/pybind11/pull/3872>`_
|
||||
|
||||
* Added missing perfect forwarding for ``make_iterator`` functions.
|
||||
`#3860 <https://github.com/pybind/pybind11/pull/3860>`_
|
||||
|
||||
* Optimize c++ to python function casting by using the rvalue caster.
|
||||
`#3966 <https://github.com/pybind/pybind11/pull/3966>`_
|
||||
|
||||
* Optimize Eigen sparse matrix casting by removing unnecessary temporary.
|
||||
`#4064 <https://github.com/pybind/pybind11/pull/4064>`_
|
||||
|
||||
* Avoid potential implicit copy/assignment constructors causing double free in
|
||||
``strdup_gaurd``.
|
||||
`#3905 <https://github.com/pybind/pybind11/pull/3905>`_
|
||||
|
||||
* Enable clang-tidy checks ``misc-definitions-in-headers``,
|
||||
``modernize-loop-convert``, and ``modernize-use-nullptr``.
|
||||
`#3881 <https://github.com/pybind/pybind11/pull/3881>`_
|
||||
`#3988 <https://github.com/pybind/pybind11/pull/3988>`_
|
||||
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* CMake: Fix file extension on Windows with cp36 and cp37 using FindPython.
|
||||
`#3919 <https://github.com/pybind/pybind11/pull/3919>`_
|
||||
|
||||
* CMake: Support multiple Python targets (such as on vcpkg).
|
||||
`#3948 <https://github.com/pybind/pybind11/pull/3948>`_
|
||||
|
||||
* CMake: Fix issue with NVCC on Windows.
|
||||
`#3947 <https://github.com/pybind/pybind11/pull/3947>`_
|
||||
|
||||
* CMake: Drop the bitness check on cross compiles (like targeting WebAssembly
|
||||
via Emscripten).
|
||||
`#3959 <https://github.com/pybind/pybind11/pull/3959>`_
|
||||
|
||||
* Add MSVC builds in debug mode to CI.
|
||||
`#3784 <https://github.com/pybind/pybind11/pull/3784>`_
|
||||
|
||||
@ -59,15 +192,23 @@ Build system improvements:
|
||||
`#3732 <https://github.com/pybind/pybind11/pull/3732>`_,
|
||||
`#3741 <https://github.com/pybind/pybind11/pull/3741>`_
|
||||
|
||||
* Avoid ``setup.py <command>`` usage in internal tests.
|
||||
`#3734 <https://github.com/pybind/pybind11/pull/3734>`_
|
||||
|
||||
|
||||
Backend and tidying up:
|
||||
|
||||
* Remove idioms in code comments. Use inclusive language.
|
||||
* New theme for the documentation.
|
||||
`#3109 <https://github.com/pybind/pybind11/pull/3109>`_
|
||||
|
||||
* Remove idioms in code comments. Use more inclusive language.
|
||||
`#3809 <https://github.com/pybind/pybind11/pull/3809>`_
|
||||
|
||||
* ``#include <iostream>`` was removed from the ``pybind11/stl.h`` header. Your
|
||||
project may break if it has a transitive dependency on this include. The fix
|
||||
is to "Include What You Use".
|
||||
`#3928 <https://github.com/pybind/pybind11/pull/3928>`_
|
||||
|
||||
* Avoid ``setup.py <command>`` usage in internal tests.
|
||||
`#3734 <https://github.com/pybind/pybind11/pull/3734>`_
|
||||
|
||||
|
||||
Version 2.9.2 (Mar 29, 2022)
|
||||
----------------------------
|
||||
@ -919,7 +1060,7 @@ Packaging / building improvements:
|
||||
`#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and
|
||||
`#2370 <https://github.com/pybind/pybind11/pull/2370>`_
|
||||
|
||||
* Full integration with CMake’s C++ standard system and compile features
|
||||
* Full integration with CMake's C++ standard system and compile features
|
||||
replaces ``PYBIND11_CPP_STANDARD``.
|
||||
|
||||
* Generated config file is now portable to different Python/compiler/CMake
|
||||
|
@ -48,10 +48,10 @@ interactive Python session demonstrating this example is shown below:
|
||||
>>> print(p)
|
||||
<example.Pet object at 0x10cd98060>
|
||||
>>> p.getName()
|
||||
u'Molly'
|
||||
'Molly'
|
||||
>>> p.setName("Charly")
|
||||
>>> p.getName()
|
||||
u'Charly'
|
||||
'Charly'
|
||||
|
||||
.. seealso::
|
||||
|
||||
@ -124,10 +124,10 @@ This makes it possible to write
|
||||
|
||||
>>> p = example.Pet("Molly")
|
||||
>>> p.name
|
||||
u'Molly'
|
||||
'Molly'
|
||||
>>> p.name = "Charly"
|
||||
>>> p.name
|
||||
u'Charly'
|
||||
'Charly'
|
||||
|
||||
Now suppose that ``Pet::name`` was a private internal variable
|
||||
that can only be accessed via setters and getters.
|
||||
@ -282,9 +282,9 @@ expose fields and methods of both types:
|
||||
|
||||
>>> p = example.Dog("Molly")
|
||||
>>> p.name
|
||||
u'Molly'
|
||||
'Molly'
|
||||
>>> p.bark()
|
||||
u'woof!'
|
||||
'woof!'
|
||||
|
||||
The C++ classes defined above are regular non-polymorphic types with an
|
||||
inheritance relationship. This is reflected in Python:
|
||||
@ -332,7 +332,7 @@ will automatically recognize this:
|
||||
>>> type(p)
|
||||
PolymorphicDog # automatically downcast
|
||||
>>> p.bark()
|
||||
u'woof!'
|
||||
'woof!'
|
||||
|
||||
Given a pointer to a polymorphic base, pybind11 performs automatic downcasting
|
||||
to the actual derived type. Note that this goes beyond the usual situation in
|
||||
@ -434,8 +434,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
|
||||
.def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age")
|
||||
.def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name");
|
||||
|
||||
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag
|
||||
or Visual Studio 2015 Update 2 and newer.
|
||||
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -483,7 +482,7 @@ The binding code for this example looks as follows:
|
||||
.value("Cat", Pet::Kind::Cat)
|
||||
.export_values();
|
||||
|
||||
py::class_<Pet::Attributes> attributes(pet, "Attributes")
|
||||
py::class_<Pet::Attributes>(pet, "Attributes")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("age", &Pet::Attributes::age);
|
||||
|
||||
|
@ -417,10 +417,10 @@ existing targets instead:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.15...3.19)
|
||||
cmake_minimum_required(VERSION 3.15...3.22)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(Python COMPONENTS Interpreter Development REQUIRED)
|
||||
find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
# or add_subdirectory(pybind11)
|
||||
|
||||
@ -433,9 +433,8 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``.
|
||||
|
||||
.. warning::
|
||||
|
||||
If you use FindPython2 and FindPython3 to dual-target Python, use the
|
||||
individual targets listed below, and avoid targets that directly include
|
||||
Python parts.
|
||||
If you use FindPython to multi-target Python versions, use the individual
|
||||
targets listed below, and avoid targets that directly include Python parts.
|
||||
|
||||
There are `many ways to hint or force a discovery of a specific Python
|
||||
installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_),
|
||||
@ -462,11 +461,8 @@ available in all modes. The targets provided are:
|
||||
``pybind11::headers``
|
||||
Just the pybind11 headers and minimum compile requirements
|
||||
|
||||
``pybind11::python2_no_register``
|
||||
Quiets the warning/error when mixing C++14 or higher and Python 2
|
||||
|
||||
``pybind11::pybind11``
|
||||
Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only)
|
||||
Python headers + ``pybind11::headers``
|
||||
|
||||
``pybind11::python_link_helper``
|
||||
Just the "linking" part of pybind11:module
|
||||
@ -509,7 +505,10 @@ You can use these targets to build complex applications. For example, the
|
||||
target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras)
|
||||
|
||||
pybind11_extension(example)
|
||||
pybind11_strip(example)
|
||||
if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo)
|
||||
# Strip unnecessary sections of the binary on Linux/macOS
|
||||
pybind11_strip(example)
|
||||
endif()
|
||||
|
||||
set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden"
|
||||
CUDA_VISIBILITY_PRESET "hidden")
|
||||
@ -577,21 +576,12 @@ On Linux, you can compile an example such as the one given in
|
||||
|
||||
$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
|
||||
|
||||
The flags given here assume that you're using Python 3. For Python 2, just
|
||||
change the executable appropriately (to ``python`` or ``python2``).
|
||||
|
||||
The ``python3 -m pybind11 --includes`` command fetches the include paths for
|
||||
both pybind11 and Python headers. This assumes that pybind11 has been installed
|
||||
using ``pip`` or ``conda``. If it hasn't, you can also manually specify
|
||||
``-I <path-to-pybind11>/include`` together with the Python includes path
|
||||
``python3-config --includes``.
|
||||
|
||||
Note that Python 2.7 modules don't use a special suffix, so you should simply
|
||||
use ``example.so`` instead of ``example$(python3-config --extension-suffix)``.
|
||||
Besides, the ``--extension-suffix`` option may or may not be available, depending
|
||||
on the distribution; in the latter case, the module extension can be manually
|
||||
set to ``.so``.
|
||||
|
||||
On macOS: the build command is almost the same but it also requires passing
|
||||
the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when
|
||||
building the module:
|
||||
|
28
docs/conf.py
28
docs/conf.py
@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# pybind11 documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Oct 11 19:23:48 2015.
|
||||
@ -36,6 +35,7 @@ DIR = Path(__file__).parent.resolve()
|
||||
# ones.
|
||||
extensions = [
|
||||
"breathe",
|
||||
"sphinx_copybutton",
|
||||
"sphinxcontrib.rsvgconverter",
|
||||
"sphinxcontrib.moderncmakedomain",
|
||||
]
|
||||
@ -126,23 +126,7 @@ todo_include_todos = False
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
|
||||
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
|
||||
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
html_context = {"css_files": ["_static/theme_overrides.css"]}
|
||||
else:
|
||||
html_context = {
|
||||
"css_files": [
|
||||
"//media.readthedocs.org/css/sphinx_rtd_theme.css",
|
||||
"//media.readthedocs.org/css/readthedocs-doc-embed.css",
|
||||
"_static/theme_overrides.css",
|
||||
]
|
||||
}
|
||||
html_theme = "furo"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
@ -173,6 +157,10 @@ else:
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
|
||||
html_css_files = [
|
||||
"css/custom.css",
|
||||
]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
@ -345,9 +333,9 @@ def generate_doxygen_xml(app):
|
||||
subprocess.call(["doxygen", "--version"])
|
||||
retcode = subprocess.call(["doxygen"], cwd=app.confdir)
|
||||
if retcode < 0:
|
||||
sys.stderr.write("doxygen error code: {}\n".format(-retcode))
|
||||
sys.stderr.write(f"doxygen error code: {-retcode}\n")
|
||||
except OSError as e:
|
||||
sys.stderr.write("doxygen execution failed: {}\n".format(e))
|
||||
sys.stderr.write(f"doxygen execution failed: {e}\n")
|
||||
|
||||
|
||||
def prepare(app):
|
||||
|
42
docs/faq.rst
42
docs/faq.rst
@ -8,9 +8,7 @@ Frequently asked questions
|
||||
filename of the extension library (without suffixes such as ``.so``).
|
||||
|
||||
2. If the above did not fix the issue, you are likely using an incompatible
|
||||
version of Python (for instance, the extension library was compiled against
|
||||
Python 2, while the interpreter is running on top of some version of Python
|
||||
3, or vice versa).
|
||||
version of Python that does not match what you compiled with.
|
||||
|
||||
"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``"
|
||||
========================================================================
|
||||
@ -147,7 +145,7 @@ using C++14 template metaprogramming.
|
||||
|
||||
.. _`faq:hidden_visibility`:
|
||||
|
||||
"‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]"
|
||||
"'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]"
|
||||
============================================================================================================
|
||||
|
||||
This error typically indicates that you are compiling without the required
|
||||
@ -222,20 +220,6 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids
|
||||
potential serious issues when loading multiple modules and is required for
|
||||
proper pybind operation. See the previous FAQ entry for more details.
|
||||
|
||||
Working with ancient Visual Studio 2008 builds on Windows
|
||||
=========================================================
|
||||
|
||||
The official Windows distributions of Python are compiled using truly
|
||||
ancient versions of Visual Studio that lack good C++11 support. Some users
|
||||
implicitly assume that it would be impossible to load a plugin built with
|
||||
Visual Studio 2015 into a Python distribution that was compiled using Visual
|
||||
Studio 2008. However, no such issue exists: it's perfectly legitimate to
|
||||
interface DLLs that are built with different compilers and/or C libraries.
|
||||
Common gotchas to watch out for involve not ``free()``-ing memory region
|
||||
that that were ``malloc()``-ed in another shared library, using data
|
||||
structures with incompatible ABIs, and so on. pybind11 is very careful not
|
||||
to make these types of mistakes.
|
||||
|
||||
How can I properly handle Ctrl-C in long-running functions?
|
||||
===========================================================
|
||||
|
||||
@ -289,27 +273,7 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses
|
||||
the CMake Python detection in a system with several Python versions installed.
|
||||
|
||||
This difference may cause inconsistencies and errors if *both* mechanisms are
|
||||
used in the same project. Consider the following CMake code executed in a
|
||||
system with Python 2.7 and 3.x installed:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs)
|
||||
find_package(pybind11)
|
||||
|
||||
It will detect Python 2.7 and pybind11 will pick it as well.
|
||||
|
||||
In contrast this code:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(pybind11)
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs)
|
||||
|
||||
will detect Python 3.x for pybind11 and may crash on
|
||||
``find_package(PythonLibs)`` afterwards.
|
||||
used in the same project.
|
||||
|
||||
There are three possible solutions:
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 60 KiB |
@ -1,5 +1,6 @@
|
||||
breathe==4.31.0
|
||||
sphinx==3.5.4
|
||||
sphinx_rtd_theme==1.0.0
|
||||
sphinxcontrib-moderncmakedomain==3.19
|
||||
sphinxcontrib-svg2pdfconverter==1.1.1
|
||||
breathe==4.34.0
|
||||
furo==2022.6.21
|
||||
sphinx==5.0.2
|
||||
sphinx-copybutton==0.5.0
|
||||
sphinxcontrib-moderncmakedomain==3.21.4
|
||||
sphinxcontrib-svg2pdfconverter==1.2.0
|
||||
|
@ -524,7 +524,7 @@ include a declaration of the form:
|
||||
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
|
||||
|
||||
Continuing to do so won’t cause an error or even a deprecation warning,
|
||||
Continuing to do so won't cause an error or even a deprecation warning,
|
||||
but it's completely redundant.
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "cast.h"
|
||||
|
||||
#include <functional>
|
||||
@ -61,7 +62,7 @@ struct base {
|
||||
|
||||
PYBIND11_DEPRECATED(
|
||||
"base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
||||
base() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
base() = default;
|
||||
};
|
||||
|
||||
/// Keep patient alive while nurse lives
|
||||
@ -82,8 +83,7 @@ struct metaclass {
|
||||
handle value;
|
||||
|
||||
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
metaclass() {}
|
||||
metaclass() = default;
|
||||
|
||||
/// Override pybind11's default metaclass
|
||||
explicit metaclass(handle value) : value(value) {}
|
||||
@ -345,9 +345,11 @@ struct type_record {
|
||||
|
||||
bases.append((PyObject *) base_info->type);
|
||||
|
||||
if (base_info->type->tp_dictoffset != 0) {
|
||||
dynamic_attr = true;
|
||||
}
|
||||
#if PY_VERSION_HEX < 0x030B0000
|
||||
dynamic_attr |= base_info->type->tp_dictoffset != 0;
|
||||
#else
|
||||
dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0;
|
||||
#endif
|
||||
|
||||
if (caster) {
|
||||
base_info->implicit_casts.emplace_back(type, caster);
|
||||
@ -478,7 +480,7 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
}
|
||||
|
||||
if (!a.value) {
|
||||
#if !defined(NDEBUG)
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
std::string descr("'");
|
||||
if (a.name) {
|
||||
descr += std::string(a.name) + ": ";
|
||||
@ -499,7 +501,8 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
#else
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
"into a Python object (type not registered yet?). "
|
||||
"Compile in debug mode for more information.");
|
||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
|
||||
"more information.");
|
||||
#endif
|
||||
}
|
||||
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
|
||||
|
@ -329,7 +329,7 @@ public:
|
||||
res = 0; // None is implicitly converted to False
|
||||
}
|
||||
#if defined(PYPY_VERSION)
|
||||
// On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists
|
||||
// On PyPy, check that "__bool__" attr exists
|
||||
else if (hasattr(src, PYBIND11_BOOL_ATTR)) {
|
||||
res = PyObject_IsTrue(src.ptr());
|
||||
}
|
||||
@ -379,37 +379,16 @@ struct string_caster {
|
||||
static constexpr size_t UTF_N = 8 * sizeof(CharT);
|
||||
|
||||
bool load(handle src, bool) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
object temp;
|
||||
#endif
|
||||
handle load_src = src;
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
if (!PyUnicode_Check(load_src.ptr())) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return load_bytes(load_src);
|
||||
#else
|
||||
if (std::is_same<CharT, char>::value) {
|
||||
return load_bytes(load_src);
|
||||
}
|
||||
|
||||
// The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false
|
||||
if (!PYBIND11_BYTES_CHECK(load_src.ptr()))
|
||||
return false;
|
||||
|
||||
temp = reinterpret_steal<object>(PyUnicode_FromObject(load_src.ptr()));
|
||||
if (!temp) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
load_src = temp;
|
||||
#endif
|
||||
return load_raw(load_src);
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes`
|
||||
// object by using `PyUnicode_AsUTF8AndSize`.
|
||||
// For UTF-8 we avoid the need for a temporary `bytes` object by using
|
||||
// `PyUnicode_AsUTF8AndSize`.
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) {
|
||||
Py_ssize_t size = -1;
|
||||
const auto *buffer
|
||||
@ -421,7 +400,6 @@ struct string_caster {
|
||||
value = StringType(buffer, static_cast<size_t>(size));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto utfNbytes
|
||||
= reinterpret_steal<object>(PyUnicode_AsEncodedString(load_src.ptr(),
|
||||
@ -484,26 +462,37 @@ private:
|
||||
#endif
|
||||
}
|
||||
|
||||
// When loading into a std::string or char*, accept a bytes object as-is (i.e.
|
||||
// When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e.
|
||||
// without any encoding/decoding attempt). For other C++ char sizes this is a no-op.
|
||||
// which supports loading a unicode from a str, doesn't take this path.
|
||||
template <typename C = CharT>
|
||||
bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src) {
|
||||
bool load_raw(enable_if_t<std::is_same<C, char>::value, handle> src) {
|
||||
if (PYBIND11_BYTES_CHECK(src.ptr())) {
|
||||
// We were passed a Python 3 raw bytes; accept it into a std::string or char*
|
||||
// We were passed raw bytes; accept it into a std::string or char*
|
||||
// without any encoding attempt.
|
||||
const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr());
|
||||
if (bytes) {
|
||||
value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr()));
|
||||
return true;
|
||||
if (!bytes) {
|
||||
pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure.");
|
||||
}
|
||||
value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr()));
|
||||
return true;
|
||||
}
|
||||
if (PyByteArray_Check(src.ptr())) {
|
||||
// We were passed a bytearray; accept it into a std::string or char*
|
||||
// without any encoding attempt.
|
||||
const char *bytearray = PyByteArray_AsString(src.ptr());
|
||||
if (!bytearray) {
|
||||
pybind11_fail("Unexpected PyByteArray_AsString() failure.");
|
||||
}
|
||||
value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename C = CharT>
|
||||
bool load_bytes(enable_if_t<!std::is_same<C, char>::value, handle>) {
|
||||
bool load_raw(enable_if_t<!std::is_same<C, char>::value, handle>) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@ -525,7 +514,7 @@ struct type_caster<std::basic_string_view<CharT, Traits>,
|
||||
template <typename CharT>
|
||||
struct type_caster<CharT, enable_if_t<is_std_char_type<CharT>::value>> {
|
||||
using StringType = std::basic_string<CharT>;
|
||||
using StringCaster = type_caster<StringType>;
|
||||
using StringCaster = make_caster<StringType>;
|
||||
StringCaster str_caster;
|
||||
bool none = false;
|
||||
CharT one_char = 0;
|
||||
@ -788,8 +777,9 @@ protected:
|
||||
return true;
|
||||
}
|
||||
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
|
||||
#if defined(NDEBUG)
|
||||
"(compile in debug mode for type information)");
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
|
||||
"type information)");
|
||||
#else
|
||||
"of type '"
|
||||
+ type_id<holder_type>() + "''");
|
||||
@ -856,7 +846,7 @@ struct always_construct_holder {
|
||||
|
||||
/// Create a specialization for custom holder types (silently ignores std::shared_ptr)
|
||||
#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \
|
||||
namespace pybind11 { \
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
|
||||
namespace detail { \
|
||||
template <typename type> \
|
||||
struct always_construct_holder<holder_type> : always_construct_holder<void, ##__VA_ARGS__> { \
|
||||
@ -865,7 +855,7 @@ struct always_construct_holder {
|
||||
class type_caster<holder_type, enable_if_t<!is_shared_ptr<holder_type>::value>> \
|
||||
: public type_caster_holder<type, holder_type> {}; \
|
||||
} \
|
||||
}
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
|
||||
template <typename base, typename holder>
|
||||
@ -918,6 +908,14 @@ struct handle_type_name<kwargs> {
|
||||
|
||||
template <typename type>
|
||||
struct pyobject_caster {
|
||||
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
|
||||
pyobject_caster() : value() {}
|
||||
|
||||
// `type` may not be default constructible (e.g. frozenset, anyset). Initializing `value`
|
||||
// to a nil handle is safe since it will only be accessed if `load` succeeds.
|
||||
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
|
||||
pyobject_caster() : value(reinterpret_steal<type>(handle())) {}
|
||||
|
||||
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
|
||||
bool load(handle src, bool /* convert */) {
|
||||
value = src;
|
||||
@ -926,18 +924,6 @@ struct pyobject_caster {
|
||||
|
||||
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
|
||||
bool load(handle src, bool /* convert */) {
|
||||
#if PY_MAJOR_VERSION < 3 && !defined(PYBIND11_STR_LEGACY_PERMISSIVE)
|
||||
// For Python 2, without this implicit conversion, Python code would
|
||||
// need to be cluttered with six.ensure_text() or similar, only to be
|
||||
// un-cluttered later after Python 2 support is dropped.
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(std::is_same<T, str>::value) && isinstance<bytes>(src)) {
|
||||
PyObject *str_from_bytes = PyUnicode_FromEncodedObject(src.ptr(), "utf-8", nullptr);
|
||||
if (!str_from_bytes)
|
||||
throw error_already_set();
|
||||
value = reinterpret_steal<type>(str_from_bytes);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (!isinstance<type>(src)) {
|
||||
return false;
|
||||
}
|
||||
@ -1023,10 +1009,12 @@ struct return_value_policy_override<
|
||||
// Basic python -> C++ casting; throws if casting fails
|
||||
template <typename T, typename SFINAE>
|
||||
type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &handle) {
|
||||
static_assert(!detail::is_pyobject<T>::value,
|
||||
"Internal error: type_caster should only be used for C++ types");
|
||||
if (!conv.load(handle, true)) {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error(
|
||||
"Unable to cast Python instance to C++ type (compile in debug mode for details)");
|
||||
#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)");
|
||||
#else
|
||||
throw cast_error("Unable to cast Python instance of type "
|
||||
+ (std::string) str(type::handle_of(handle)) + " to C++ type '"
|
||||
@ -1091,10 +1079,10 @@ inline void handle::cast() const {
|
||||
template <typename T>
|
||||
detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
|
||||
if (obj.ref_count() > 1) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error(
|
||||
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
|
||||
" (compile in debug mode for details)");
|
||||
" (#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>()
|
||||
@ -1113,21 +1101,30 @@ detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
|
||||
// - If both movable and copyable, check ref count: if 1, move; otherwise copy
|
||||
// - Otherwise (not movable), copy.
|
||||
template <typename T>
|
||||
detail::enable_if_t<detail::move_always<T>::value, T> cast(object &&object) {
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_always<T>::value, T>
|
||||
cast(object &&object) {
|
||||
return move<T>(std::move(object));
|
||||
}
|
||||
template <typename T>
|
||||
detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) {
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_if_unreferenced<T>::value, T>
|
||||
cast(object &&object) {
|
||||
if (object.ref_count() > 1) {
|
||||
return cast<T>(object);
|
||||
}
|
||||
return move<T>(std::move(object));
|
||||
}
|
||||
template <typename T>
|
||||
detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) {
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_never<T>::value, T>
|
||||
cast(object &&object) {
|
||||
return cast<T>(object);
|
||||
}
|
||||
|
||||
// pytype rvalue -> pytype (calls converting constructor)
|
||||
template <typename T>
|
||||
detail::enable_if_t<detail::is_pyobject<T>::value, T> cast(object &&object) {
|
||||
return T(std::move(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T object::cast() const & {
|
||||
return pybind11::cast<T>(*this);
|
||||
@ -1178,24 +1175,27 @@ enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_ref(object &&,
|
||||
// static_assert, even though if it's in dead code, so we provide a "trampoline" to pybind11::cast
|
||||
// that only does anything in cases where pybind11::cast is valid.
|
||||
template <typename T>
|
||||
enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&o) {
|
||||
return pybind11::cast<T>(std::move(o));
|
||||
}
|
||||
template <typename T>
|
||||
enable_if_t<cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&) {
|
||||
pybind11_fail("Internal error: cast_safe fallback invoked");
|
||||
}
|
||||
template <>
|
||||
inline void cast_safe<void>(object &&) {}
|
||||
template <typename T>
|
||||
enable_if_t<std::is_same<void, intrinsic_t<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>
|
||||
cast_safe(object &&o) {
|
||||
return pybind11::cast<T>(std::move(o));
|
||||
}
|
||||
|
||||
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(NDEBUG)
|
||||
#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 (compile in debug mode for details)");
|
||||
return cast_error("Unable to convert call argument 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,
|
||||
@ -1217,7 +1217,7 @@ tuple make_tuple(Args &&...args_) {
|
||||
detail::make_caster<Args>::cast(std::forward<Args>(args_), policy, nullptr))...}};
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
if (!args[i]) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
#else
|
||||
std::array<std::string, size> argtypes{{type_id<Args>()...}};
|
||||
@ -1266,10 +1266,10 @@ struct arg_v : arg {
|
||||
private:
|
||||
template <typename T>
|
||||
arg_v(arg &&base, T &&x, const char *descr = nullptr)
|
||||
: arg(base), value(reinterpret_steal<object>(
|
||||
detail::make_caster<T>::cast(x, return_value_policy::automatic, {}))),
|
||||
: arg(base), value(reinterpret_steal<object>(detail::make_caster<T>::cast(
|
||||
std::forward<T>(x), return_value_policy::automatic, {}))),
|
||||
descr(descr)
|
||||
#if !defined(NDEBUG)
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
,
|
||||
type(type_id<T>())
|
||||
#endif
|
||||
@ -1309,7 +1309,7 @@ public:
|
||||
object value;
|
||||
/// The (optional) description of the default value
|
||||
const char *descr;
|
||||
#if !defined(NDEBUG)
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
/// The C++ type name of the default value (only available when compiled in debug mode)
|
||||
std::string type;
|
||||
#endif
|
||||
@ -1317,7 +1317,7 @@ public:
|
||||
|
||||
/// \ingroup annotations
|
||||
/// Annotation indicating that all following arguments are keyword-only; the is the equivalent of
|
||||
/// an unnamed '*' argument (in Python 3)
|
||||
/// an unnamed '*' argument
|
||||
struct kw_only {};
|
||||
|
||||
/// \ingroup annotations
|
||||
@ -1507,14 +1507,14 @@ private:
|
||||
auto o = reinterpret_steal<object>(
|
||||
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
|
||||
if (!o) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
#else
|
||||
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
|
||||
type_id<T>());
|
||||
#endif
|
||||
}
|
||||
args_list.append(o);
|
||||
args_list.append(std::move(o));
|
||||
}
|
||||
|
||||
void process(list &args_list, detail::args_proxy ap) {
|
||||
@ -1525,21 +1525,21 @@ private:
|
||||
|
||||
void process(list & /*args_list*/, arg_v a) {
|
||||
if (!a.name) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
nameless_argument_error();
|
||||
#else
|
||||
nameless_argument_error(a.type);
|
||||
#endif
|
||||
}
|
||||
if (m_kwargs.contains(a.name)) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
multiple_values_error();
|
||||
#else
|
||||
multiple_values_error(a.name);
|
||||
#endif
|
||||
}
|
||||
if (!a.value) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
#else
|
||||
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
|
||||
@ -1554,7 +1554,7 @@ private:
|
||||
}
|
||||
for (auto k : reinterpret_borrow<dict>(kp)) {
|
||||
if (m_kwargs.contains(k.first)) {
|
||||
#if defined(NDEBUG)
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
multiple_values_error();
|
||||
#else
|
||||
multiple_values_error(str(k.first));
|
||||
@ -1565,9 +1565,10 @@ private:
|
||||
}
|
||||
|
||||
[[noreturn]] static void nameless_argument_error() {
|
||||
throw type_error("Got kwargs without a name; only named arguments "
|
||||
"may be passed via py::arg() to a python function call. "
|
||||
"(compile in debug mode for details)");
|
||||
throw type_error(
|
||||
"Got kwargs without a name; only named arguments "
|
||||
"may be passed via py::arg() to a python function call. "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
}
|
||||
[[noreturn]] static void nameless_argument_error(const std::string &type) {
|
||||
throw type_error("Got kwargs without a name of type '" + type
|
||||
@ -1575,8 +1576,9 @@ private:
|
||||
"arguments may be passed via py::arg() to a python function call. ");
|
||||
}
|
||||
[[noreturn]] static void multiple_values_error() {
|
||||
throw type_error("Got multiple values for keyword argument "
|
||||
"(compile in debug mode for details)");
|
||||
throw type_error(
|
||||
"Got multiple values for keyword argument "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
}
|
||||
|
||||
[[noreturn]] static void multiple_values_error(const std::string &name) {
|
||||
@ -1623,7 +1625,7 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
|
||||
template <typename Derived>
|
||||
template <return_value_policy policy, typename... Args>
|
||||
object object_api<Derived>::operator()(Args &&...args) const {
|
||||
#if !defined(NDEBUG) && PY_VERSION_HEX >= 0x03060000
|
||||
#ifndef NDEBUG
|
||||
if (!PyGILState_Check()) {
|
||||
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
|
||||
}
|
||||
@ -1648,12 +1650,12 @@ handle type::handle_of() {
|
||||
}
|
||||
|
||||
#define PYBIND11_MAKE_OPAQUE(...) \
|
||||
namespace pybind11 { \
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
|
||||
namespace detail { \
|
||||
template <> \
|
||||
class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> {}; \
|
||||
} \
|
||||
}
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
/// Lets you pass a type containing a `,` through a macro parameter without needing a separate
|
||||
/// typedef, e.g.:
|
||||
|
@ -18,17 +18,6 @@
|
||||
#include <datetime.h>
|
||||
#include <mutex>
|
||||
|
||||
// Backport the PyDateTime_DELTA functions from Python3.3 if required
|
||||
#ifndef PyDateTime_DELTA_GET_DAYS
|
||||
# define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta *) o)->days)
|
||||
#endif
|
||||
#ifndef PyDateTime_DELTA_GET_SECONDS
|
||||
# define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta *) o)->seconds)
|
||||
#endif
|
||||
#ifndef PyDateTime_DELTA_GET_MICROSECONDS
|
||||
# define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta *) o)->microseconds)
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
|
@ -15,12 +15,12 @@
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION)
|
||||
#if !defined(PYPY_VERSION)
|
||||
# define PYBIND11_BUILTIN_QUALNAME
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
|
||||
#else
|
||||
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
|
||||
// signatures; in 3.3+ this macro expands to nothing:
|
||||
// In PyPy, we still set __qualname__ so that we can produce reliable function type
|
||||
// signatures; in CPython this macro expands to nothing:
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \
|
||||
setattr((PyObject *) obj, "__qualname__", nameobj)
|
||||
#endif
|
||||
@ -155,7 +155,6 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb
|
||||
}
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
/**
|
||||
* Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing
|
||||
* methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function,
|
||||
@ -170,7 +169,6 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
|
||||
}
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// metaclass `__call__` function that is used to create all pybind11 objects.
|
||||
extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) {
|
||||
@ -266,9 +264,7 @@ inline PyTypeObject *make_default_metaclass() {
|
||||
type->tp_call = pybind11_meta_call;
|
||||
|
||||
type->tp_setattro = pybind11_meta_setattro;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
type->tp_getattro = pybind11_meta_getattro;
|
||||
#endif
|
||||
|
||||
type->tp_dealloc = pybind11_meta_dealloc;
|
||||
|
||||
@ -459,6 +455,8 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string error_string();
|
||||
|
||||
/** Create the type which can be used as a common base for all classes. This is
|
||||
needed in order to satisfy Python's requirements for multiple inheritance.
|
||||
Return value: New reference. */
|
||||
@ -494,7 +492,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||
type->tp_weaklistoffset = offsetof(instance, weakrefs);
|
||||
|
||||
if (PyType_Ready(type) < 0) {
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string());
|
||||
}
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
@ -533,6 +531,10 @@ extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void
|
||||
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_VISIT(dict);
|
||||
// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
|
||||
#if PY_VERSION_HEX >= 0x03090000
|
||||
Py_VISIT(Py_TYPE(self));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -547,8 +549,12 @@ extern "C" inline int pybind11_clear(PyObject *self) {
|
||||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
#if PY_VERSION_HEX < 0x030B0000
|
||||
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
|
||||
#else
|
||||
type->tp_flags |= Py_TPFLAGS_MANAGED_DICT;
|
||||
#endif
|
||||
type->tp_traverse = pybind11_traverse;
|
||||
type->tp_clear = pybind11_clear;
|
||||
|
||||
@ -613,9 +619,6 @@ extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
|
||||
/// Give this type a buffer interface.
|
||||
inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
|
||||
heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer;
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
|
||||
#endif
|
||||
|
||||
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
|
||||
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
|
||||
@ -628,12 +631,8 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
||||
|
||||
auto qualname = name;
|
||||
if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
qualname = reinterpret_steal<object>(
|
||||
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
|
||||
#else
|
||||
qualname = str(rec.scope.attr("__qualname__").cast<std::string>() + "." + rec.name);
|
||||
#endif
|
||||
}
|
||||
|
||||
object module_;
|
||||
@ -697,15 +696,10 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
||||
type->tp_as_number = &heap_type->as_number;
|
||||
type->tp_as_sequence = &heap_type->as_sequence;
|
||||
type->tp_as_mapping = &heap_type->as_mapping;
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
type->tp_as_async = &heap_type->as_async;
|
||||
#endif
|
||||
|
||||
/* Flags */
|
||||
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
||||
#endif
|
||||
if (!rec.is_final) {
|
||||
type->tp_flags |= Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
@ -723,7 +717,7 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
||||
}
|
||||
|
||||
if (PyType_Ready(type) < 0) {
|
||||
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
|
||||
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string());
|
||||
}
|
||||
|
||||
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
|
@ -10,12 +10,12 @@
|
||||
#pragma once
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 9
|
||||
#define PYBIND11_VERSION_PATCH 2
|
||||
#define PYBIND11_VERSION_MINOR 10
|
||||
#define PYBIND11_VERSION_PATCH 0
|
||||
|
||||
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
|
||||
// Additional convention: 0xD = dev
|
||||
#define PYBIND11_VERSION_HEX 0x02090200
|
||||
#define PYBIND11_VERSION_HEX 0x020A0000
|
||||
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
|
||||
#define PYBIND11_NAMESPACE_END(name) }
|
||||
@ -38,6 +38,7 @@
|
||||
# define PYBIND11_CPP17
|
||||
# if __cplusplus >= 202002L
|
||||
# define PYBIND11_CPP20
|
||||
// Please update tests/pybind11_tests.cpp `cpp_std()` when adding a macro here.
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
@ -47,7 +48,7 @@
|
||||
// or newer.
|
||||
# if _MSVC_LANG >= 201402L
|
||||
# define PYBIND11_CPP14
|
||||
# if _MSVC_LANG > 201402L && _MSC_VER >= 1910
|
||||
# if _MSVC_LANG > 201402L
|
||||
# define PYBIND11_CPP17
|
||||
# if _MSVC_LANG >= 202002L
|
||||
# define PYBIND11_CPP20
|
||||
@ -81,10 +82,8 @@
|
||||
# error pybind11 requires gcc 4.8 or newer
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features
|
||||
// (e.g. std::negation) added in 2015u3:
|
||||
# if _MSC_FULL_VER < 190024210
|
||||
# error pybind11 requires MSVC 2015 update 3 or newer
|
||||
# if _MSC_VER < 1910
|
||||
# error pybind11 2.10+ requires MSVC 2017 or newer
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -149,7 +148,7 @@
|
||||
|
||||
/* Don't let Python.h #define (v)snprintf as macro because they are implemented
|
||||
properly in Visual Studio since 2015. */
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
#if defined(_MSC_VER)
|
||||
# define HAVE_SNPRINTF 1
|
||||
#endif
|
||||
|
||||
@ -211,6 +210,9 @@
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#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
|
||||
#include <frameobject.h>
|
||||
#include <pythread.h>
|
||||
|
||||
@ -266,78 +268,37 @@
|
||||
// If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and
|
||||
// pybind11::isinstance<str>() is true only for pybind11::str.
|
||||
// However, for Python 2 only (!), the pybind11::str caster
|
||||
// implicitly decodes bytes to PyUnicodeObject. This is to ease
|
||||
// implicitly decoded bytes to PyUnicodeObject. This was to ease
|
||||
// the transition from the legacy behavior to the non-permissive
|
||||
// behavior.
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
||||
# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
# define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
|
||||
# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
|
||||
# define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||
# define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
||||
# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING PyBytes_AsString
|
||||
# define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
# define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
# define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
# define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
|
||||
# define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
|
||||
# define PYBIND11_BYTES_NAME "bytes"
|
||||
# define PYBIND11_STRING_NAME "str"
|
||||
# define PYBIND11_SLICE_OBJECT PyObject
|
||||
# define PYBIND11_FROM_STRING PyUnicode_FromString
|
||||
# define PYBIND11_STR_TYPE ::pybind11::str
|
||||
# define PYBIND11_BOOL_ATTR "__bool__"
|
||||
# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
|
||||
# define PYBIND11_BUILTINS_MODULE "builtins"
|
||||
/// Compatibility macros for Python 2 / Python 3 versions TODO: remove
|
||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
|
||||
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
|
||||
#define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
|
||||
#define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
|
||||
#define PYBIND11_BYTES_NAME "bytes"
|
||||
#define PYBIND11_STRING_NAME "str"
|
||||
#define PYBIND11_SLICE_OBJECT PyObject
|
||||
#define PYBIND11_FROM_STRING PyUnicode_FromString
|
||||
#define PYBIND11_STR_TYPE ::pybind11::str
|
||||
#define PYBIND11_BOOL_ATTR "__bool__"
|
||||
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
|
||||
#define PYBIND11_BUILTINS_MODULE "builtins"
|
||||
// Providing a separate declaration to make Clang's -Wmissing-prototypes happy.
|
||||
// See comment for PYBIND11_MODULE below for why this is marked "maybe unused".
|
||||
# define PYBIND11_PLUGIN_IMPL(name) \
|
||||
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||
|
||||
#else
|
||||
# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
|
||||
# define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check
|
||||
# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION
|
||||
# define PYBIND11_BYTES_CHECK PyString_Check
|
||||
# define PYBIND11_BYTES_FROM_STRING PyString_FromString
|
||||
# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING PyString_AsString
|
||||
# define PYBIND11_BYTES_SIZE PyString_Size
|
||||
# define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
|
||||
# define PYBIND11_LONG_AS_LONGLONG(o) \
|
||||
(PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
|
||||
# define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed.
|
||||
# define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed.
|
||||
# define PYBIND11_BYTES_NAME "str"
|
||||
# define PYBIND11_STRING_NAME "unicode"
|
||||
# define PYBIND11_SLICE_OBJECT PySliceObject
|
||||
# define PYBIND11_FROM_STRING PyString_FromString
|
||||
# define PYBIND11_STR_TYPE ::pybind11::bytes
|
||||
# define PYBIND11_BOOL_ATTR "__nonzero__"
|
||||
# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero)
|
||||
# define PYBIND11_BUILTINS_MODULE "__builtin__"
|
||||
// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy.
|
||||
// See comment for PYBIND11_MODULE below for why this is marked "maybe unused".
|
||||
# define PYBIND11_PLUGIN_IMPL(name) \
|
||||
static PyObject *pybind11_init_wrapper(); \
|
||||
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \
|
||||
extern "C" PYBIND11_EXPORT void init##name() { (void) pybind11_init_wrapper(); } \
|
||||
PyObject *pybind11_init_wrapper()
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200
|
||||
extern "C" {
|
||||
struct _Py_atomic_address {
|
||||
void *value;
|
||||
};
|
||||
PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
|
||||
}
|
||||
#endif
|
||||
#define PYBIND11_PLUGIN_IMPL(name) \
|
||||
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||
|
||||
#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
|
||||
#define PYBIND11_STRINGIFY(x) #x
|
||||
@ -362,31 +323,15 @@ PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
|
||||
} \
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
# define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#endif
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
/** \rst
|
||||
***Deprecated in favor of PYBIND11_MODULE***
|
||||
@ -483,7 +428,7 @@ enum class return_value_policy : uint8_t {
|
||||
|
||||
/** Reference an existing object (i.e. do not create a new copy) and take
|
||||
ownership. Python will call the destructor and delete operator when the
|
||||
object’s reference count reaches zero. Undefined behavior ensues when
|
||||
object's reference count reaches zero. Undefined behavior ensues when
|
||||
the C++ side does the same.. */
|
||||
take_ownership,
|
||||
|
||||
@ -499,7 +444,7 @@ enum class return_value_policy : uint8_t {
|
||||
move,
|
||||
|
||||
/** Reference an existing object, but do not take ownership. The C++ side
|
||||
is responsible for managing the object’s lifetime and deallocating it
|
||||
is responsible for managing the object's lifetime and deallocating it
|
||||
when it is no longer used. Warning: undefined behavior will ensue when
|
||||
the C++ side deletes an object that is still referenced and used by
|
||||
Python. */
|
||||
@ -508,7 +453,7 @@ enum class return_value_policy : uint8_t {
|
||||
/** This policy only applies to methods and properties. It references the
|
||||
object without taking ownership similar to the above
|
||||
return_value_policy::reference policy. In contrast to that policy, the
|
||||
function or property’s implicit this argument (called the parent) is
|
||||
function or property's implicit this argument (called the parent) is
|
||||
considered to be the the owner of the return value (the child).
|
||||
pybind11 then couples the lifetime of the parent to the child via a
|
||||
reference relationship that ensures that the parent cannot be garbage
|
||||
@ -615,7 +560,7 @@ static_assert(std::is_standard_layout<instance>::value,
|
||||
"Internal error: `pybind11::detail::instance` is not standard layout!");
|
||||
|
||||
/// from __cpp_future__ import (convenient aliases from C++14/17)
|
||||
#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
|
||||
#if defined(PYBIND11_CPP14)
|
||||
using std::conditional_t;
|
||||
using std::enable_if_t;
|
||||
using std::remove_cv_t;
|
||||
@ -878,10 +823,12 @@ struct is_template_base_of_impl {
|
||||
/// Check if a template is the base of a type. For example:
|
||||
/// `is_template_base_of<Base, T>` is true if `struct T : Base<U> {}` where U can be anything
|
||||
template <template <typename...> class Base, typename T>
|
||||
// Sadly, all MSVC versions incl. 2022 need the workaround, even in C++20 mode.
|
||||
// See also: https://github.com/pybind/pybind11/pull/3741
|
||||
#if !defined(_MSC_VER)
|
||||
using is_template_base_of
|
||||
= decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr));
|
||||
#else // MSVC2015 has trouble with decltype in template aliases
|
||||
#else
|
||||
struct is_template_base_of
|
||||
: decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr)) {
|
||||
};
|
||||
@ -990,9 +937,11 @@ PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybin
|
||||
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
|
||||
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) {
|
||||
assert(!PyErr_Occurred());
|
||||
throw std::runtime_error(reason);
|
||||
}
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) {
|
||||
assert(!PyErr_Occurred());
|
||||
throw std::runtime_error(reason);
|
||||
}
|
||||
|
||||
@ -1044,6 +993,8 @@ constexpr const char
|
||||
struct error_scope {
|
||||
PyObject *type, *value, *trace;
|
||||
error_scope() { PyErr_Fetch(&type, &value, &trace); }
|
||||
error_scope(const error_scope &) = delete;
|
||||
error_scope &operator=(const error_scope &) = delete;
|
||||
~error_scope() { PyErr_Restore(type, value, trace); }
|
||||
};
|
||||
|
||||
@ -1056,9 +1007,6 @@ struct nodelete {
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
struct overload_cast_impl {
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): MSVC 2015 needs this
|
||||
constexpr overload_cast_impl() {}
|
||||
|
||||
template <typename Return>
|
||||
constexpr auto operator()(Return (*pf)(Args...)) const noexcept -> decltype(pf) {
|
||||
return pf;
|
||||
@ -1085,8 +1033,12 @@ 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 = {};
|
||||
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
|
||||
# else
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/// Const member function selector for overload_cast
|
||||
@ -1172,7 +1124,7 @@ try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) {
|
||||
|
||||
// For silencing "unused" compiler warnings in special situations.
|
||||
template <typename... Args>
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER < 1920 // MSVC 2017
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920 // MSVC 2017
|
||||
constexpr
|
||||
#endif
|
||||
inline void
|
||||
@ -1206,5 +1158,12 @@ constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_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.
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG)
|
||||
# define PYBIND11_DETAILED_ERROR_MESSAGES
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
@ -425,4 +425,4 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
|
||||
|
||||
PYBIND11_NAMESPACE_END(initimpl)
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
@ -82,7 +82,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
||||
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION)
|
||||
# if defined(PYPY_VERSION)
|
||||
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set
|
||||
// the value if it has already been set. Instead, it must first be deleted and
|
||||
// then set again.
|
||||
@ -294,7 +294,6 @@ inline internals **&get_internals_pp() {
|
||||
return internals_pp;
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// forward decl
|
||||
inline void translate_exception(std::exception_ptr);
|
||||
|
||||
@ -318,21 +317,11 @@ bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template <class T>
|
||||
bool handle_nested_exception(const T &, std::exception_ptr &) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool raise_err(PyObject *exc_type, const char *msg) {
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
if (PyErr_Occurred()) {
|
||||
raise_from(exc_type, msg);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
PyErr_SetString(exc_type, msg);
|
||||
return false;
|
||||
}
|
||||
@ -426,6 +415,7 @@ PYBIND11_NOINLINE internals &get_internals() {
|
||||
~gil_scoped_acquire_local() { PyGILState_Release(state); }
|
||||
const PyGILState_STATE state;
|
||||
} gil;
|
||||
error_scope err_scope;
|
||||
|
||||
PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID);
|
||||
auto builtins = handle(PyEval_GetBuiltins());
|
||||
|
@ -225,8 +225,8 @@ PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
|
||||
if (throw_if_missing) {
|
||||
std::string tname = tp.name();
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname
|
||||
+ "\"");
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \""
|
||||
+ std::move(tname) + '"');
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -394,15 +394,16 @@ instance::get_value_and_holder(const type_info *find_type /*= nullptr default in
|
||||
return value_and_holder();
|
||||
}
|
||||
|
||||
#if defined(NDEBUG)
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(compile in debug mode for type details)");
|
||||
#else
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `"
|
||||
+ get_fully_qualified_tp_name(find_type->type)
|
||||
+ "' is not a pybind11 base of the given `"
|
||||
+ get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
|
||||
#else
|
||||
pybind11_fail(
|
||||
"pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for type details)");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -439,21 +440,15 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
|
||||
// instance_registered)
|
||||
|
||||
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
|
||||
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6
|
||||
// they default to using pymalloc, which is designed to be efficient for small allocations
|
||||
// like the one we're doing here; in earlier versions (and for larger allocations) they are
|
||||
// just wrappers around malloc.
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
// in particular, need to be 0). Use Python's memory allocation
|
||||
// functions: Python is using pymalloc, which is designed to be
|
||||
// efficient for small allocations like the one we're doing here;
|
||||
// for larger allocations they are just wrappers around malloc.
|
||||
// TODO: is this still true for pure Python 3.6?
|
||||
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
|
||||
if (!nonsimple.values_and_holders) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
#else
|
||||
nonsimple.values_and_holders = (void **) PyMem_New(void *, space);
|
||||
if (!nonsimple.values_and_holders)
|
||||
throw std::bad_alloc();
|
||||
std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
|
||||
#endif
|
||||
nonsimple.status
|
||||
= reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
|
||||
}
|
||||
@ -475,70 +470,6 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp)
|
||||
return isinstance(obj, type);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE std::string error_string() {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred");
|
||||
return "Unknown internal error occurred";
|
||||
}
|
||||
|
||||
error_scope scope; // Preserve error state
|
||||
|
||||
std::string errorString;
|
||||
if (scope.type) {
|
||||
errorString += handle(scope.type).attr("__name__").cast<std::string>();
|
||||
errorString += ": ";
|
||||
}
|
||||
if (scope.value) {
|
||||
errorString += (std::string) str(scope.value);
|
||||
}
|
||||
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (scope.trace != nullptr) {
|
||||
PyException_SetTraceback(scope.value, scope.trace);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
if (scope.trace) {
|
||||
auto *trace = (PyTracebackObject *) scope.trace;
|
||||
|
||||
/* Get the deepest trace possible */
|
||||
while (trace->tb_next) {
|
||||
trace = trace->tb_next;
|
||||
}
|
||||
|
||||
PyFrameObject *frame = trace->tb_frame;
|
||||
Py_XINCREF(frame);
|
||||
errorString += "\n\nAt:\n";
|
||||
while (frame) {
|
||||
# if PY_VERSION_HEX >= 0x030900B1
|
||||
PyCodeObject *f_code = PyFrame_GetCode(frame);
|
||||
# else
|
||||
PyCodeObject *f_code = frame->f_code;
|
||||
Py_INCREF(f_code);
|
||||
# endif
|
||||
int lineno = PyFrame_GetLineNumber(frame);
|
||||
errorString += " " + handle(f_code->co_filename).cast<std::string>() + "("
|
||||
+ std::to_string(lineno)
|
||||
+ "): " + handle(f_code->co_name).cast<std::string>() + "\n";
|
||||
Py_DECREF(f_code);
|
||||
# if PY_VERSION_HEX >= 0x030900B1
|
||||
auto *b_frame = PyFrame_GetBack(frame);
|
||||
# else
|
||||
auto *b_frame = frame->f_back;
|
||||
Py_XINCREF(b_frame);
|
||||
# endif
|
||||
Py_DECREF(frame);
|
||||
frame = b_frame;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) {
|
||||
auto &instances = get_internals().registered_instances;
|
||||
auto range = instances.equal_range(ptr);
|
||||
@ -555,12 +486,6 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
|
||||
inline PyThreadState *get_thread_state_unchecked() {
|
||||
#if defined(PYPY_VERSION)
|
||||
return PyThreadState_GET();
|
||||
#elif PY_VERSION_HEX < 0x03000000
|
||||
return _PyThreadState_Current;
|
||||
#elif PY_VERSION_HEX < 0x03050000
|
||||
return (PyThreadState *) _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
#elif PY_VERSION_HEX < 0x03050200
|
||||
return (PyThreadState *) _PyThreadState_Current.value;
|
||||
#else
|
||||
return _PyThreadState_UncheckedGet();
|
||||
#endif
|
||||
@ -622,14 +547,15 @@ public:
|
||||
if (copy_constructor) {
|
||||
valueptr = copy_constructor(src);
|
||||
} else {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = copy, but type is "
|
||||
"non-copyable! (compile in debug mode for details)");
|
||||
#else
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = copy, but type " + type_name
|
||||
+ " is non-copyable!");
|
||||
#else
|
||||
throw cast_error("return_value_policy = copy, but type is "
|
||||
"non-copyable! (#define PYBIND11_DETAILED_ERROR_MESSAGES or "
|
||||
"compile in debug mode for details)");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
@ -641,15 +567,16 @@ public:
|
||||
} else if (copy_constructor) {
|
||||
valueptr = copy_constructor(src);
|
||||
} else {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = move, but type is neither "
|
||||
"movable nor copyable! "
|
||||
"(compile in debug mode for details)");
|
||||
#else
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = move, but type " + type_name
|
||||
+ " is neither movable nor copyable!");
|
||||
#else
|
||||
throw cast_error("return_value_policy = move, but type is neither "
|
||||
"movable nor copyable! "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in "
|
||||
"debug mode for details)");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Erase all occurrences of a substring
|
||||
inline void erase_all(std::string &string, const std::string &search) {
|
||||
for (size_t pos = 0;;) {
|
||||
@ -46,14 +47,19 @@ PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||
#endif
|
||||
detail::erase_all(name, "pybind11::");
|
||||
}
|
||||
|
||||
inline std::string clean_type_id(const char *typeid_name) {
|
||||
std::string name(typeid_name);
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Return a string representation of a C++ type
|
||||
template <typename T>
|
||||
static std::string type_id() {
|
||||
std::string name(typeid(T).name());
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
return detail::clean_type_id(typeid(T).name());
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
@ -21,10 +21,12 @@
|
||||
// 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.
|
||||
// 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>
|
||||
@ -109,10 +111,16 @@ struct EigenConformable {
|
||||
bool stride_compatible() const {
|
||||
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant)
|
||||
return !negativestrides
|
||||
&& (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
// irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant
|
||||
// (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly).
|
||||
if (negativestrides) {
|
||||
return false;
|
||||
}
|
||||
if (rows == 0 || cols == 0) {
|
||||
return true;
|
||||
}
|
||||
return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
@ -666,7 +674,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
nnz,
|
||||
std::move(nnz),
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
@ -684,8 +692,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(std::make_tuple(data, innerIndices, outerIndices),
|
||||
std::make_pair(src.rows(), src.cols()))
|
||||
return matrix_type(pybind11::make_tuple(
|
||||
std::move(data), std::move(innerIndices), std::move(outerIndices)),
|
||||
pybind11::make_tuple(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
|
@ -19,15 +19,9 @@
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
|
||||
#else
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" void pybind11_init_impl_##name(); \
|
||||
extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); }
|
||||
#endif
|
||||
#define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
|
||||
|
||||
/** \rst
|
||||
Add a new module to the table of builtins for the interpreter. Must be
|
||||
@ -67,11 +61,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
|
||||
struct embedded_module {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
using init_t = PyObject *(*) ();
|
||||
#else
|
||||
using init_t = void (*)();
|
||||
#endif
|
||||
embedded_module(const char *name, init_t init) {
|
||||
if (Py_IsInitialized() != 0) {
|
||||
pybind11_fail("Can't add new modules after the interpreter has been initialized");
|
||||
@ -86,86 +76,16 @@ struct embedded_module {
|
||||
|
||||
struct wide_char_arg_deleter {
|
||||
void operator()(wchar_t *ptr) const {
|
||||
#if PY_VERSION_HEX >= 0x030500f0
|
||||
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
|
||||
PyMem_RawFree(ptr);
|
||||
#else
|
||||
delete[] ptr;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
inline wchar_t *widen_chars(const char *safe_arg) {
|
||||
#if PY_VERSION_HEX >= 0x030500f0
|
||||
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
|
||||
#else
|
||||
wchar_t *widened_arg = nullptr;
|
||||
|
||||
// warning C4996: 'mbstowcs': This function or variable may be unsafe.
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4996)
|
||||
# endif
|
||||
|
||||
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
|
||||
size_t count = std::strlen(safe_arg);
|
||||
# else
|
||||
size_t count = std::mbstowcs(nullptr, safe_arg, 0);
|
||||
# endif
|
||||
if (count != static_cast<size_t>(-1)) {
|
||||
widened_arg = new wchar_t[count + 1];
|
||||
std::mbstowcs(widened_arg, safe_arg, count + 1);
|
||||
}
|
||||
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
return widened_arg;
|
||||
}
|
||||
|
||||
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
|
||||
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
|
||||
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||
bool special_case = (argv == nullptr || argc <= 0);
|
||||
|
||||
const char *const empty_argv[]{"\0"};
|
||||
const char *const *safe_argv = special_case ? empty_argv : argv;
|
||||
if (special_case) {
|
||||
argc = 1;
|
||||
}
|
||||
|
||||
auto argv_size = static_cast<size_t>(argc);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// SetArgv* on python 3 takes wchar_t, so we have to convert.
|
||||
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
|
||||
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
|
||||
widened_argv_entries.reserve(argv_size);
|
||||
for (size_t ii = 0; ii < argv_size; ++ii) {
|
||||
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
|
||||
if (!widened_argv_entries.back()) {
|
||||
// A null here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
return;
|
||||
}
|
||||
widened_argv[ii] = widened_argv_entries.back().get();
|
||||
}
|
||||
|
||||
auto *pysys_argv = widened_argv.get();
|
||||
#else
|
||||
// python 2.x
|
||||
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
|
||||
std::vector<char *> char_strings{argv_size};
|
||||
for (std::size_t i = 0; i < argv_size; ++i)
|
||||
char_strings[i] = &strings[i][0];
|
||||
char **pysys_argv = char_strings.data();
|
||||
#endif
|
||||
|
||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/** \rst
|
||||
@ -195,9 +115,64 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||
pybind11_fail("The interpreter is already running");
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX < 0x030B0000
|
||||
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
|
||||
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
|
||||
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||
bool special_case = (argv == nullptr || argc <= 0);
|
||||
|
||||
const char *const empty_argv[]{"\0"};
|
||||
const char *const *safe_argv = special_case ? empty_argv : argv;
|
||||
if (special_case) {
|
||||
argc = 1;
|
||||
}
|
||||
|
||||
auto argv_size = static_cast<size_t>(argc);
|
||||
// SetArgv* on python 3 takes wchar_t, so we have to convert.
|
||||
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
|
||||
std::vector<std::unique_ptr<wchar_t[], detail::wide_char_arg_deleter>> widened_argv_entries;
|
||||
widened_argv_entries.reserve(argv_size);
|
||||
for (size_t ii = 0; ii < argv_size; ++ii) {
|
||||
widened_argv_entries.emplace_back(detail::widen_chars(safe_argv[ii]));
|
||||
if (!widened_argv_entries.back()) {
|
||||
// A null here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
return;
|
||||
}
|
||||
widened_argv[ii] = widened_argv_entries.back().get();
|
||||
}
|
||||
|
||||
auto *pysys_argv = widened_argv.get();
|
||||
|
||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
||||
#else
|
||||
PyConfig config;
|
||||
PyConfig_InitIsolatedConfig(&config);
|
||||
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||
|
||||
PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast<char *const *>(argv));
|
||||
if (PyStatus_Exception(status)) {
|
||||
// A failure here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
PyConfig_Clear(&config);
|
||||
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg
|
||||
: "Failed to prepare CPython");
|
||||
}
|
||||
status = Py_InitializeFromConfig(&config);
|
||||
PyConfig_Clear(&config);
|
||||
if (PyStatus_Exception(status)) {
|
||||
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg
|
||||
: "Failed to init CPython");
|
||||
}
|
||||
if (add_program_dir_to_path) {
|
||||
PyRun_SimpleString("import sys, os.path; "
|
||||
"sys.path.insert(0, "
|
||||
"os.path.abspath(os.path.dirname(sys.argv[0])) "
|
||||
"if sys.argv and os.path.exists(sys.argv[0]) else '')");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \rst
|
||||
|
@ -20,10 +20,10 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline void ensure_builtins_in_globals(object &global) {
|
||||
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
|
||||
// Running exec and eval on Python 2 and 3 adds `builtins` module under
|
||||
// `__builtins__` key to globals if not yet present.
|
||||
// Python 3.8 made PyRun_String behave similarly. Let's also do that for
|
||||
// older versions, for consistency. This was missing from PyPy3.8 7.3.7.
|
||||
// Running exec and eval adds `builtins` module under `__builtins__` key to
|
||||
// globals if not yet present. Python 3.8 made PyRun_String behave
|
||||
// similarly. Let's also do that for older versions, for consistency. This
|
||||
// was missing from PyPy3.8 7.3.7.
|
||||
if (!global.contains("__builtins__"))
|
||||
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||
#else
|
||||
@ -94,7 +94,7 @@ void exec(const char (&s)[N], object global = globals(), object local = object()
|
||||
eval<eval_statements>(s, std::move(global), std::move(local));
|
||||
}
|
||||
|
||||
#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000
|
||||
#if defined(PYPY_VERSION)
|
||||
template <eval_mode mode = eval_statements>
|
||||
object eval_file(str, object, object) {
|
||||
pybind11_fail("eval_file not supported in PyPy3. Use eval");
|
||||
@ -133,40 +133,18 @@ object eval_file(str fname, object global = globals(), object local = object())
|
||||
|
||||
int closeFile = 1;
|
||||
std::string fname_str = (std::string) fname;
|
||||
# if PY_VERSION_HEX >= 0x03040000
|
||||
FILE *f = _Py_fopen_obj(fname.ptr(), "r");
|
||||
# elif PY_VERSION_HEX >= 0x03000000
|
||||
FILE *f = _Py_fopen(fname.ptr(), "r");
|
||||
# else
|
||||
/* No unicode support in open() :( */
|
||||
auto fobj = reinterpret_steal<object>(
|
||||
PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
|
||||
FILE *f = nullptr;
|
||||
if (fobj)
|
||||
f = PyFile_AsFile(fobj.ptr());
|
||||
closeFile = 0;
|
||||
# endif
|
||||
if (!f) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||
}
|
||||
|
||||
// In Python2, this should be encoded by getfilesystemencoding.
|
||||
// We don't boher setting it since Python2 is past EOL anyway.
|
||||
// See PR#3233
|
||||
# if PY_VERSION_HEX >= 0x03000000
|
||||
if (!global.contains("__file__")) {
|
||||
global["__file__"] = std::move(fname);
|
||||
}
|
||||
# endif
|
||||
|
||||
# if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
|
||||
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), local.ptr());
|
||||
(void) closeFile;
|
||||
# else
|
||||
PyObject *result
|
||||
= PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
|
||||
# endif
|
||||
|
||||
if (!result) {
|
||||
throw error_already_set();
|
||||
|
@ -98,9 +98,8 @@ public:
|
||||
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
|
||||
Return operator()(Args... args) const {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
/* Visual studio 2015 parser issue: need parentheses around this expression */
|
||||
return (retval.template cast<Return>());
|
||||
// casts the returned object as a rvalue to the return type
|
||||
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
# if !defined(NDEBUG)
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (!tstate) {
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
}
|
||||
@ -84,7 +84,7 @@ public:
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
# if !defined(NDEBUG)
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (detail::get_thread_state_unchecked() != tstate) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
}
|
||||
@ -93,7 +93,7 @@ public:
|
||||
}
|
||||
# endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
# if !defined(NDEBUG)
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (!release) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ private:
|
||||
|
||||
if (size > remainder) {
|
||||
str line(pbase(), size - remainder);
|
||||
pywrite(line);
|
||||
pywrite(std::move(line));
|
||||
pyflush();
|
||||
}
|
||||
|
||||
|
@ -263,11 +263,7 @@ private:
|
||||
static npy_api lookup() {
|
||||
module_ m = module_::import("numpy.core.multiarray");
|
||||
auto c = m.attr("_ARRAY_API");
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL);
|
||||
#else
|
||||
void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr());
|
||||
#endif
|
||||
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
|
||||
npy_api api;
|
||||
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
|
||||
DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
|
||||
@ -544,18 +540,18 @@ public:
|
||||
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_);
|
||||
|
||||
explicit dtype(const buffer_info &info) {
|
||||
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
|
||||
dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
|
||||
// If info.itemsize == 0, use the value calculated from the format string
|
||||
m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
|
||||
.release()
|
||||
.ptr();
|
||||
}
|
||||
|
||||
explicit dtype(const std::string &format) {
|
||||
m_ptr = from_args(pybind11::str(format)).release().ptr();
|
||||
}
|
||||
explicit dtype(const pybind11::str &format) : dtype(from_args(format)) {}
|
||||
|
||||
explicit dtype(const char *format) : dtype(std::string(format)) {}
|
||||
explicit dtype(const std::string &format) : dtype(pybind11::str(format)) {}
|
||||
|
||||
explicit dtype(const char *format) : dtype(pybind11::str(format)) {}
|
||||
|
||||
dtype(list names, list formats, list offsets, ssize_t itemsize) {
|
||||
dict args;
|
||||
@ -563,11 +559,18 @@ public:
|
||||
args["formats"] = std::move(formats);
|
||||
args["offsets"] = std::move(offsets);
|
||||
args["itemsize"] = pybind11::int_(itemsize);
|
||||
m_ptr = from_args(std::move(args)).release().ptr();
|
||||
m_ptr = from_args(args).release().ptr();
|
||||
}
|
||||
|
||||
explicit dtype(int typenum)
|
||||
: object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
|
||||
if (m_ptr == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
/// This is essentially the same as calling numpy.dtype(args) in Python.
|
||||
static dtype from_args(object args) {
|
||||
static dtype from_args(const object &args) {
|
||||
PyObject *ptr = nullptr;
|
||||
if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr) {
|
||||
throw error_already_set();
|
||||
@ -600,6 +603,23 @@ public:
|
||||
return detail::array_descriptor_proxy(m_ptr)->type;
|
||||
}
|
||||
|
||||
/// type number of dtype.
|
||||
int num() const {
|
||||
// Note: The signature, `dtype::num` follows the naming of NumPy's public
|
||||
// Python API (i.e., ``dtype.num``), rather than its internal
|
||||
// C API (``PyArray_Descr::type_num``).
|
||||
return detail::array_descriptor_proxy(m_ptr)->type_num;
|
||||
}
|
||||
|
||||
/// Single character for byteorder
|
||||
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
|
||||
|
||||
/// Alignment of the data type
|
||||
int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; }
|
||||
|
||||
/// Flags for the array descriptor
|
||||
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
|
||||
|
||||
private:
|
||||
static object _dtype_from_pep3118() {
|
||||
static PyObject *obj = module_::import("numpy.core._internal")
|
||||
@ -618,22 +638,27 @@ private:
|
||||
}
|
||||
|
||||
struct field_descr {
|
||||
PYBIND11_STR_TYPE name;
|
||||
pybind11::str name;
|
||||
object format;
|
||||
pybind11::int_ offset;
|
||||
field_descr(pybind11::str &&name, object &&format, pybind11::int_ &&offset)
|
||||
: name{std::move(name)}, format{std::move(format)}, offset{std::move(offset)} {};
|
||||
};
|
||||
auto field_dict = attr("fields").cast<dict>();
|
||||
std::vector<field_descr> field_descriptors;
|
||||
field_descriptors.reserve(field_dict.size());
|
||||
|
||||
for (auto field : attr("fields").attr("items")()) {
|
||||
for (auto field : field_dict.attr("items")()) {
|
||||
auto spec = field.cast<tuple>();
|
||||
auto name = spec[0].cast<pybind11::str>();
|
||||
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
|
||||
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
|
||||
auto spec_fo = spec[1].cast<tuple>();
|
||||
auto format = spec_fo[0].cast<dtype>();
|
||||
auto offset = spec_fo[1].cast<pybind11::int_>();
|
||||
if ((len(name) == 0u) && format.kind() == 'V') {
|
||||
continue;
|
||||
}
|
||||
field_descriptors.push_back(
|
||||
{(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset});
|
||||
field_descriptors.emplace_back(
|
||||
std::move(name), format.strip_padding(format.itemsize()), std::move(offset));
|
||||
}
|
||||
|
||||
std::sort(field_descriptors.begin(),
|
||||
@ -644,9 +669,9 @@ private:
|
||||
|
||||
list names, formats, offsets;
|
||||
for (auto &descr : field_descriptors) {
|
||||
names.append(descr.name);
|
||||
formats.append(descr.format);
|
||||
offsets.append(descr.offset);
|
||||
names.append(std::move(descr.name));
|
||||
formats.append(std::move(descr.format));
|
||||
offsets.append(std::move(descr.offset));
|
||||
}
|
||||
return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
|
||||
}
|
||||
@ -944,7 +969,7 @@ protected:
|
||||
|
||||
void fail_dim_check(ssize_t dim, const std::string &msg) const {
|
||||
throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim())
|
||||
+ ")");
|
||||
+ ')');
|
||||
}
|
||||
|
||||
template <typename... Ix>
|
||||
@ -1148,11 +1173,11 @@ struct format_descriptor<T, detail::enable_if_t<detail::is_pod_struct<T>::value>
|
||||
|
||||
template <size_t N>
|
||||
struct format_descriptor<char[N]> {
|
||||
static std::string format() { return std::to_string(N) + "s"; }
|
||||
static std::string format() { return std::to_string(N) + 's'; }
|
||||
};
|
||||
template <size_t N>
|
||||
struct format_descriptor<std::array<char, N>> {
|
||||
static std::string format() { return std::to_string(N) + "s"; }
|
||||
static std::string format() { return std::to_string(N) + 's'; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -1292,7 +1317,8 @@ public:
|
||||
static pybind11::dtype dtype() {
|
||||
list shape;
|
||||
array_info<T>::append_extents(shape);
|
||||
return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape));
|
||||
return pybind11::dtype::from_args(
|
||||
pybind11::make_tuple(base_descr::dtype(), std::move(shape)));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1338,7 +1364,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
||||
pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ "
|
||||
+ tinfo.name());
|
||||
}
|
||||
names.append(PYBIND11_STR_TYPE(field.name));
|
||||
names.append(pybind11::str(field.name));
|
||||
formats.append(field.descr);
|
||||
offsets.append(pybind11::int_(field.offset));
|
||||
}
|
||||
@ -1383,7 +1409,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
||||
}
|
||||
|
||||
auto tindex = std::type_index(tinfo);
|
||||
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, format_str};
|
||||
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
|
||||
get_internals().direct_conversions[tindex].push_back(direct_converter);
|
||||
}
|
||||
|
||||
@ -1530,7 +1556,7 @@ public:
|
||||
void *data() const { return p_ptr; }
|
||||
|
||||
private:
|
||||
char *p_ptr{0};
|
||||
char *p_ptr{nullptr};
|
||||
container_type m_strides;
|
||||
};
|
||||
|
||||
|
@ -91,16 +91,6 @@ struct op_ {
|
||||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv)
|
||||
|| PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: ot == op_l ? "__div__"
|
||||
: "__rdiv__",
|
||||
&op::execute,
|
||||
is_operator(),
|
||||
extra...);
|
||||
#endif
|
||||
}
|
||||
template <typename Class, typename... Extra>
|
||||
void execute_cast(Class &cl, const Extra &...extra) const {
|
||||
@ -109,15 +99,6 @@ struct op_ {
|
||||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute_cast, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: ot == op_l ? "__div__"
|
||||
: "__rdiv__",
|
||||
&op::execute,
|
||||
is_operator(),
|
||||
extra...);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -312,6 +312,10 @@ protected:
|
||||
// along the way.
|
||||
class strdup_guard {
|
||||
public:
|
||||
strdup_guard() = default;
|
||||
strdup_guard(const strdup_guard &) = delete;
|
||||
strdup_guard &operator=(const strdup_guard &) = delete;
|
||||
|
||||
~strdup_guard() {
|
||||
for (auto *s : strings) {
|
||||
std::free(s);
|
||||
@ -366,7 +370,7 @@ protected:
|
||||
rec->is_constructor = (std::strcmp(rec->name, "__init__") == 0)
|
||||
|| (std::strcmp(rec->name, "__setstate__") == 0);
|
||||
|
||||
#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
|
||||
if (rec->is_constructor && !rec->is_new_style_constructor) {
|
||||
const auto class_name
|
||||
= detail::get_fully_qualified_tp_name((PyTypeObject *) rec->scope.ptr());
|
||||
@ -431,9 +435,8 @@ protected:
|
||||
}
|
||||
if (auto *tinfo = detail::get_type_info(*t)) {
|
||||
handle th((PyObject *) tinfo->type);
|
||||
signature += th.attr("__module__").cast<std::string>() + "." +
|
||||
// Python 3.3+, but we backport it to earlier versions
|
||||
th.attr("__qualname__").cast<std::string>();
|
||||
signature += th.attr("__module__").cast<std::string>() + "."
|
||||
+ th.attr("__qualname__").cast<std::string>();
|
||||
} else if (rec->is_new_style_constructor && arg_index == 0) {
|
||||
// A new-style `__init__` takes `self` as `value_and_holder`.
|
||||
// Rewrite it to the proper class type.
|
||||
@ -453,15 +456,6 @@ protected:
|
||||
pybind11_fail("Internal error while parsing type signature (2)");
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (std::strcmp(rec->name, "__next__") == 0) {
|
||||
std::free(rec->name);
|
||||
rec->name = guarded_strdup("next");
|
||||
} else if (std::strcmp(rec->name, "__bool__") == 0) {
|
||||
std::free(rec->name);
|
||||
rec->name = guarded_strdup("__nonzero__");
|
||||
}
|
||||
#endif
|
||||
rec->signature = guarded_strdup(signature.c_str());
|
||||
rec->args.shrink_to_fit();
|
||||
rec->nargs = (std::uint16_t) args;
|
||||
@ -524,8 +518,9 @@ protected:
|
||||
if (chain->is_method != rec->is_method) {
|
||||
pybind11_fail(
|
||||
"overloading a method with both static and instance methods is not supported; "
|
||||
#if defined(NDEBUG)
|
||||
"compile in debug mode for more details"
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more "
|
||||
"details"
|
||||
#else
|
||||
"error while attempting to bind "
|
||||
+ std::string(rec->is_method ? "instance" : "static") + " method "
|
||||
@ -571,14 +566,14 @@ protected:
|
||||
for (auto *it = chain_start; it != nullptr; it = it->next) {
|
||||
if (options::show_function_signatures()) {
|
||||
if (index > 0) {
|
||||
signatures += "\n";
|
||||
signatures += '\n';
|
||||
}
|
||||
if (chain) {
|
||||
signatures += std::to_string(++index) + ". ";
|
||||
}
|
||||
signatures += rec->name;
|
||||
signatures += it->signature;
|
||||
signatures += "\n";
|
||||
signatures += '\n';
|
||||
}
|
||||
if (it->doc && it->doc[0] != '\0' && options::show_user_defined_docstrings()) {
|
||||
// If we're appending another docstring, and aren't printing function signatures,
|
||||
@ -587,15 +582,15 @@ protected:
|
||||
if (first_user_def) {
|
||||
first_user_def = false;
|
||||
} else {
|
||||
signatures += "\n";
|
||||
signatures += '\n';
|
||||
}
|
||||
}
|
||||
if (options::show_function_signatures()) {
|
||||
signatures += "\n";
|
||||
signatures += '\n';
|
||||
}
|
||||
signatures += it->doc;
|
||||
if (options::show_function_signatures()) {
|
||||
signatures += "\n";
|
||||
signatures += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -912,7 +907,7 @@ protected:
|
||||
|
||||
// 5. Put everything in a vector. Not technically step 5, we've been building it
|
||||
// in `call.args` all along.
|
||||
#if !defined(NDEBUG)
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) {
|
||||
pybind11_fail("Internal error: function call dispatcher inserted wrong number "
|
||||
"of arguments!");
|
||||
@ -1065,7 +1060,7 @@ protected:
|
||||
msg += it2->signature;
|
||||
}
|
||||
|
||||
msg += "\n";
|
||||
msg += '\n';
|
||||
}
|
||||
msg += "\nInvoked with: ";
|
||||
auto args_ = reinterpret_borrow<tuple>(args_in);
|
||||
@ -1107,14 +1102,12 @@ protected:
|
||||
}
|
||||
|
||||
append_note_if_missing_header_is_suspected(msg);
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// Attach additional error info to the exception if supported
|
||||
if (PyErr_Occurred()) {
|
||||
// #HelpAppreciated: unit test coverage for this branch.
|
||||
raise_from(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
@ -1123,13 +1116,11 @@ protected:
|
||||
"Python type! The signature was\n\t";
|
||||
msg += it->signature;
|
||||
append_note_if_missing_header_is_suspected(msg);
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// Attach additional error info to the exception if supported
|
||||
if (PyErr_Occurred()) {
|
||||
raise_from(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
@ -1149,11 +1140,7 @@ public:
|
||||
/// Create a new top-level Python module with the given name and docstring
|
||||
PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead")
|
||||
explicit module_(const char *name, const char *doc = nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
*this = create_extension_module(name, doc, new PyModuleDef());
|
||||
#else
|
||||
*this = create_extension_module(name, doc, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \rst
|
||||
@ -1186,9 +1173,16 @@ public:
|
||||
py::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'");
|
||||
\endrst */
|
||||
module_ def_submodule(const char *name, const char *doc = nullptr) {
|
||||
std::string full_name
|
||||
= std::string(PyModule_GetName(m_ptr)) + std::string(".") + std::string(name);
|
||||
auto result = reinterpret_borrow<module_>(PyImport_AddModule(full_name.c_str()));
|
||||
const char *this_name = PyModule_GetName(m_ptr);
|
||||
if (this_name == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
std::string full_name = std::string(this_name) + '.' + name;
|
||||
handle submodule = PyImport_AddModule(full_name.c_str());
|
||||
if (!submodule) {
|
||||
throw error_already_set();
|
||||
}
|
||||
auto result = reinterpret_borrow<module_>(submodule);
|
||||
if (doc && options::show_user_defined_docstrings()) {
|
||||
result.attr("__doc__") = pybind11::str(doc);
|
||||
}
|
||||
@ -1231,20 +1225,14 @@ public:
|
||||
PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */);
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
using module_def = PyModuleDef;
|
||||
#else
|
||||
struct module_def {};
|
||||
#endif
|
||||
using module_def = PyModuleDef; // TODO: Can this be removed (it was needed only for Python 2)?
|
||||
|
||||
/** \rst
|
||||
Create a new top-level module that can be used as the main module of a C extension.
|
||||
|
||||
For Python 3, ``def`` should point to a statically allocated module_def.
|
||||
For Python 2, ``def`` can be a nullptr and is completely ignored.
|
||||
``def`` should point to a statically allocated module_def.
|
||||
\endrst */
|
||||
static module_ create_extension_module(const char *name, const char *doc, module_def *def) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// module_def is PyModuleDef
|
||||
// Placement new (not an allocation).
|
||||
def = new (def)
|
||||
@ -1258,12 +1246,6 @@ public:
|
||||
/* m_clear */ nullptr,
|
||||
/* m_free */ nullptr};
|
||||
auto *m = PyModule_Create(def);
|
||||
#else
|
||||
// Ignore module_def *def; only necessary for Python 3
|
||||
(void) def;
|
||||
auto m = Py_InitModule3(
|
||||
name, nullptr, options::show_user_defined_docstrings() ? doc : nullptr);
|
||||
#endif
|
||||
if (m == nullptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
@ -1271,8 +1253,8 @@ public:
|
||||
pybind11_fail("Internal error in module_::create_extension_module()");
|
||||
}
|
||||
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
|
||||
// returned from PyInit_...
|
||||
// For Python 2, reinterpret_borrow is correct.
|
||||
// returned from PyInit_...
|
||||
// For Python 2, reinterpret_borrow was correct.
|
||||
return reinterpret_borrow<module_>(m);
|
||||
}
|
||||
};
|
||||
@ -1290,14 +1272,12 @@ inline dict globals() {
|
||||
return reinterpret_borrow<dict>(p ? p : module_::import("__main__").attr("__dict__").ptr());
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
template <typename... Args, typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
|
||||
PYBIND11_DEPRECATED("make_simple_namespace should be replaced with "
|
||||
"py::module_::import(\"types\").attr(\"SimpleNamespace\") ")
|
||||
object make_simple_namespace(Args &&...args_) {
|
||||
return module_::import("types").attr("SimpleNamespace")(std::forward<Args>(args_)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/// Generic support for creating new Python heap types
|
||||
@ -1593,7 +1573,8 @@ public:
|
||||
scope(*this),
|
||||
sibling(getattr(*this, name_, none())),
|
||||
extra...);
|
||||
attr(cf.name()) = staticmethod(cf);
|
||||
auto cf_name = cf.name();
|
||||
attr(std::move(cf_name)) = staticmethod(std::move(cf));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -1647,7 +1628,7 @@ public:
|
||||
if (!caster.load(obj, false)) {
|
||||
return nullptr;
|
||||
}
|
||||
return new buffer_info(((capture *) ptr)->func(caster));
|
||||
return new buffer_info(((capture *) ptr)->func(std::move(caster)));
|
||||
},
|
||||
ptr);
|
||||
weakref(m_ptr, cpp_function([ptr](handle wr) {
|
||||
@ -1849,7 +1830,8 @@ 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 (inst->owned || detail::always_construct_holder<holder_type>::value) {
|
||||
} else if (PYBIND11_SILENCE_MSVC_C4127(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();
|
||||
}
|
||||
@ -1952,7 +1934,8 @@ struct enum_base {
|
||||
[](const object &arg) -> str {
|
||||
handle type = type::handle_of(arg);
|
||||
object type_name = type.attr("__name__");
|
||||
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
|
||||
return pybind11::str("<{}.{}: {}>")
|
||||
.format(std::move(type_name), enum_name(arg), int_(arg));
|
||||
},
|
||||
name("__repr__"),
|
||||
is_method(m_base));
|
||||
@ -1962,7 +1945,7 @@ struct enum_base {
|
||||
m_base.attr("__str__") = cpp_function(
|
||||
[](handle arg) -> str {
|
||||
object type_name = type::handle_of(arg).attr("__name__");
|
||||
return pybind11::str("{}.{}").format(type_name, enum_name(arg));
|
||||
return pybind11::str("{}.{}").format(std::move(type_name), enum_name(arg));
|
||||
},
|
||||
name("name"),
|
||||
is_method(m_base));
|
||||
@ -2086,12 +2069,12 @@ struct enum_base {
|
||||
str name(name_);
|
||||
if (entries.contains(name)) {
|
||||
std::string type_name = (std::string) str(m_base.attr("__name__"));
|
||||
throw value_error(type_name + ": element \"" + std::string(name_)
|
||||
throw value_error(std::move(type_name) + ": element \"" + std::string(name_)
|
||||
+ "\" already exists!");
|
||||
}
|
||||
|
||||
entries[name] = std::make_pair(value, doc);
|
||||
m_base.attr(name) = value;
|
||||
m_base.attr(std::move(name)) = std::move(value);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void export_values() {
|
||||
@ -2173,9 +2156,6 @@ public:
|
||||
def_property_readonly("value", [](Type value) { return (Scalar) value; });
|
||||
def("__int__", [](Type value) { return (Scalar) value; });
|
||||
def("__index__", [](Type value) { return (Scalar) value; });
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
def("__long__", [](Type value) { return (Scalar) value; });
|
||||
#endif
|
||||
attr("__setstate__") = cpp_function(
|
||||
[](detail::value_and_holder &v_h, Scalar arg) {
|
||||
detail::initimpl::setstate<Base>(
|
||||
@ -2353,7 +2333,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
|
||||
|
||||
@ -2379,7 +2359,7 @@ iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
Policy);
|
||||
}
|
||||
|
||||
return cast(state{first, last, true});
|
||||
return cast(state{std::forward<Iterator>(first), std::forward<Sentinel>(last), true});
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
@ -2390,13 +2370,15 @@ 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...>(first, last, std::forward<Extra>(extra)...);
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes a python iterator over the keys (`.first`) of a iterator over pairs from a
|
||||
@ -2406,13 +2388,15 @@ 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...>(first, last, std::forward<Extra>(extra)...);
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes a python iterator over the values (`.second`) of a iterator over pairs from a
|
||||
@ -2422,13 +2406,15 @@ 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...>(first, last, std::forward<Extra>(extra)...);
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes an iterator over values of an stl container or other container supporting
|
||||
@ -2437,7 +2423,8 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
||||
typename Type,
|
||||
typename... Extra>
|
||||
iterator make_iterator(Type &value, Extra &&...extra) {
|
||||
return make_iterator<Policy>(std::begin(value), std::end(value), extra...);
|
||||
return make_iterator<Policy>(
|
||||
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting
|
||||
@ -2446,7 +2433,8 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
||||
typename Type,
|
||||
typename... Extra>
|
||||
iterator make_key_iterator(Type &value, Extra &&...extra) {
|
||||
return make_key_iterator<Policy>(std::begin(value), std::end(value), extra...);
|
||||
return make_key_iterator<Policy>(
|
||||
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes an iterator over the values (`.second`) of a stl map-like container supporting
|
||||
@ -2455,7 +2443,8 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
||||
typename Type,
|
||||
typename... Extra>
|
||||
iterator make_value_iterator(Type &value, Extra &&...extra) {
|
||||
return make_value_iterator<Policy>(std::begin(value), std::end(value), extra...);
|
||||
return make_value_iterator<Policy>(
|
||||
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
template <typename InputType, typename OutputType>
|
||||
@ -2484,7 +2473,7 @@ void implicitly_convertible() {
|
||||
};
|
||||
|
||||
if (auto *tinfo = detail::get_type_info(typeid(OutputType))) {
|
||||
tinfo->implicit_conversions.push_back(implicit_caster);
|
||||
tinfo->implicit_conversions.emplace_back(std::move(implicit_caster));
|
||||
} else {
|
||||
pybind11_fail("implicitly_convertible: Unable to find type " + type_id<OutputType>());
|
||||
}
|
||||
@ -2520,7 +2509,7 @@ public:
|
||||
exception(handle scope, const char *name, handle base = PyExc_Exception) {
|
||||
std::string full_name
|
||||
= scope.attr("__name__").cast<std::string>() + std::string(".") + name;
|
||||
m_ptr = PyErr_NewException(const_cast<char *>(full_name.c_str()), base.ptr(), NULL);
|
||||
m_ptr = PyErr_NewException(const_cast<char *>(full_name.c_str()), base.ptr(), nullptr);
|
||||
if (hasattr(scope, "__dict__") && scope.attr("__dict__").contains(name)) {
|
||||
pybind11_fail("Error during initialization: multiple incompatible "
|
||||
"definitions with name \""
|
||||
@ -2602,8 +2591,8 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
strings[i] = str(args[i]);
|
||||
}
|
||||
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
|
||||
auto line = sep.attr("join")(strings);
|
||||
auto sep = kwargs.contains("sep") ? kwargs["sep"] : str(" ");
|
||||
auto line = sep.attr("join")(std::move(strings));
|
||||
|
||||
object file;
|
||||
if (kwargs.contains("file")) {
|
||||
@ -2621,8 +2610,8 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
|
||||
}
|
||||
|
||||
auto write = file.attr("write");
|
||||
write(line);
|
||||
write(kwargs.contains("end") ? kwargs["end"] : cast("\n"));
|
||||
write(std::move(line));
|
||||
write(kwargs.contains("end") ? kwargs["end"] : str("\n"));
|
||||
|
||||
if (kwargs.contains("flush") && kwargs["flush"].cast<bool>()) {
|
||||
file.attr("flush")();
|
||||
@ -2636,17 +2625,21 @@ void print(Args &&...args) {
|
||||
detail::print(c.args(), c.kwargs());
|
||||
}
|
||||
|
||||
error_already_set::~error_already_set() {
|
||||
if (m_type) {
|
||||
gil_scoped_acquire gil;
|
||||
error_scope scope;
|
||||
m_type.release().dec_ref();
|
||||
m_value.release().dec_ref();
|
||||
m_trace.release().dec_ref();
|
||||
}
|
||||
inline void
|
||||
error_already_set::m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr) {
|
||||
gil_scoped_acquire gil;
|
||||
error_scope scope;
|
||||
delete raw_ptr;
|
||||
}
|
||||
|
||||
inline const char *error_already_set::what() const noexcept {
|
||||
gil_scoped_acquire gil;
|
||||
error_scope scope;
|
||||
return m_fetched_error->error_string().c_str();
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline function
|
||||
get_type_override(const void *this_ptr, const type_info *this_type, const char *name) {
|
||||
handle self = get_object_handle(this_ptr, this_type);
|
||||
@ -2665,7 +2658,7 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
|
||||
|
||||
function override = getattr(self, name, function());
|
||||
if (override.is_cpp_function()) {
|
||||
cache.insert(key);
|
||||
cache.insert(std::move(key));
|
||||
return function();
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,15 @@
|
||||
#include "detail/common.h"
|
||||
#include "buffer_info.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <frameobject.h>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
@ -85,7 +93,9 @@ public:
|
||||
or `object` subclass causes a call to ``__setitem__``.
|
||||
\endrst */
|
||||
item_accessor operator[](handle key) const;
|
||||
/// See above (the only difference is that they key is provided as a string literal)
|
||||
/// See above (the only difference is that the key's reference is stolen)
|
||||
item_accessor operator[](object &&key) const;
|
||||
/// See above (the only difference is that the key is provided as a string literal)
|
||||
item_accessor operator[](const char *key) const;
|
||||
|
||||
/** \rst
|
||||
@ -95,7 +105,9 @@ public:
|
||||
or `object` subclass causes a call to ``setattr``.
|
||||
\endrst */
|
||||
obj_attr_accessor attr(handle key) const;
|
||||
/// See above (the only difference is that they key is provided as a string literal)
|
||||
/// See above (the only difference is that the key's reference is stolen)
|
||||
obj_attr_accessor attr(object &&key) const;
|
||||
/// See above (the only difference is that the key is provided as a string literal)
|
||||
str_attr_accessor attr(const char *key) const;
|
||||
|
||||
/** \rst
|
||||
@ -178,8 +190,17 @@ private:
|
||||
bool rich_compare(object_api const &other, int value) const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using is_pyobj_ptr_or_nullptr_t = detail::any_of<std::is_same<T, PyObject *>,
|
||||
std::is_same<T, PyObject *const>,
|
||||
std::is_same<T, std::nullptr_t>>;
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if !defined(PYBIND11_HANDLE_REF_DEBUG) && !defined(NDEBUG)
|
||||
# define PYBIND11_HANDLE_REF_DEBUG
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
Holds a reference to a Python object (no reference counting)
|
||||
|
||||
@ -195,9 +216,23 @@ class handle : public detail::object_api<handle> {
|
||||
public:
|
||||
/// The default constructor creates a handle with a ``nullptr``-valued pointer
|
||||
handle() = default;
|
||||
/// Creates a ``handle`` from the given raw Python object pointer
|
||||
|
||||
/// Enable implicit conversion from ``PyObject *`` and ``nullptr``.
|
||||
/// Not using ``handle(PyObject *ptr)`` to avoid implicit conversion from ``0``.
|
||||
template <typename T,
|
||||
detail::enable_if_t<detail::is_pyobj_ptr_or_nullptr_t<T>::value, int> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
handle(PyObject *ptr) : m_ptr(ptr) {} // Allow implicit conversion from PyObject*
|
||||
handle(T ptr) : m_ptr(ptr) {}
|
||||
|
||||
/// Enable implicit conversion through ``T::operator PyObject *()``.
|
||||
template <
|
||||
typename T,
|
||||
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>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
handle(T &obj) : m_ptr(obj) {}
|
||||
|
||||
/// Return the underlying ``PyObject *`` pointer
|
||||
PyObject *ptr() const { return m_ptr; }
|
||||
@ -209,6 +244,9 @@ public:
|
||||
this function automatically. Returns a reference to itself.
|
||||
\endrst */
|
||||
const handle &inc_ref() const & {
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
inc_ref_counter(1);
|
||||
#endif
|
||||
Py_XINCREF(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
@ -244,6 +282,18 @@ public:
|
||||
|
||||
protected:
|
||||
PyObject *m_ptr = nullptr;
|
||||
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
private:
|
||||
static std::size_t inc_ref_counter(std::size_t add) {
|
||||
thread_local std::size_t counter = 0;
|
||||
counter += add;
|
||||
return counter;
|
||||
}
|
||||
|
||||
public:
|
||||
static std::size_t inc_ref_counter() { return inc_ref_counter(0); }
|
||||
#endif
|
||||
};
|
||||
|
||||
/** \rst
|
||||
@ -268,10 +318,7 @@ public:
|
||||
/// Copy constructor; always increases the reference count
|
||||
object(const object &o) : handle(o) { inc_ref(); }
|
||||
/// Move constructor; steals the object from ``other`` and preserves its reference count
|
||||
object(object &&other) noexcept {
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = nullptr;
|
||||
}
|
||||
object(object &&other) noexcept : handle(other) { other.m_ptr = nullptr; }
|
||||
/// Destructor; automatically calls `handle::dec_ref()`
|
||||
~object() { dec_ref(); }
|
||||
|
||||
@ -363,7 +410,175 @@ T reinterpret_steal(handle h) {
|
||||
}
|
||||
|
||||
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) {
|
||||
return reinterpret_cast<PyTypeObject *>(obj)->tp_name;
|
||||
}
|
||||
return Py_TYPE(obj)->tp_name;
|
||||
}
|
||||
|
||||
std::string error_string();
|
||||
|
||||
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.
|
||||
explicit error_fetch_and_normalize(const char *called) {
|
||||
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||
if (!m_type) {
|
||||
pybind11_fail("Internal error: " + std::string(called)
|
||||
+ " called while "
|
||||
"Python error indicator not set.");
|
||||
}
|
||||
const char *exc_type_name_orig = detail::obj_class_name(m_type.ptr());
|
||||
if (exc_type_name_orig == nullptr) {
|
||||
pybind11_fail("Internal error: " + std::string(called)
|
||||
+ " failed to obtain the name "
|
||||
"of the original active exception type.");
|
||||
}
|
||||
m_lazy_error_string = exc_type_name_orig;
|
||||
// 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());
|
||||
if (m_type.ptr() == nullptr) {
|
||||
pybind11_fail("Internal error: " + std::string(called)
|
||||
+ " failed to normalize the "
|
||||
"active exception.");
|
||||
}
|
||||
const char *exc_type_name_norm = detail::obj_class_name(m_type.ptr());
|
||||
if (exc_type_name_orig == nullptr) {
|
||||
pybind11_fail("Internal error: " + std::string(called)
|
||||
+ " failed to obtain the name "
|
||||
"of the normalized active exception type.");
|
||||
}
|
||||
if (exc_type_name_norm != m_lazy_error_string) {
|
||||
std::string msg = std::string(called)
|
||||
+ ": MISMATCH of original and normalized "
|
||||
"active exception types: ";
|
||||
msg += "ORIGINAL ";
|
||||
msg += m_lazy_error_string;
|
||||
msg += " REPLACED BY ";
|
||||
msg += exc_type_name_norm;
|
||||
msg += ": " + format_value_and_trace();
|
||||
pybind11_fail(msg);
|
||||
}
|
||||
}
|
||||
|
||||
error_fetch_and_normalize(const error_fetch_and_normalize &) = delete;
|
||||
error_fetch_and_normalize(error_fetch_and_normalize &&) = delete;
|
||||
|
||||
std::string format_value_and_trace() const {
|
||||
std::string result;
|
||||
std::string message_error_string;
|
||||
if (m_value) {
|
||||
auto value_str = reinterpret_steal<object>(PyObject_Str(m_value.ptr()));
|
||||
if (!value_str) {
|
||||
message_error_string = detail::error_string();
|
||||
result = "<MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>";
|
||||
} else {
|
||||
result = value_str.cast<std::string>();
|
||||
}
|
||||
} else {
|
||||
result = "<MESSAGE UNAVAILABLE>";
|
||||
}
|
||||
if (result.empty()) {
|
||||
result = "<EMPTY MESSAGE>";
|
||||
}
|
||||
|
||||
bool have_trace = false;
|
||||
if (m_trace) {
|
||||
#if !defined(PYPY_VERSION)
|
||||
auto *tb = reinterpret_cast<PyTracebackObject *>(m_trace.ptr());
|
||||
|
||||
// Get the deepest trace possible.
|
||||
while (tb->tb_next) {
|
||||
tb = tb->tb_next;
|
||||
}
|
||||
|
||||
PyFrameObject *frame = tb->tb_frame;
|
||||
Py_XINCREF(frame);
|
||||
result += "\n\nAt:\n";
|
||||
while (frame) {
|
||||
# if PY_VERSION_HEX >= 0x030900B1
|
||||
PyCodeObject *f_code = PyFrame_GetCode(frame);
|
||||
# else
|
||||
PyCodeObject *f_code = frame->f_code;
|
||||
Py_INCREF(f_code);
|
||||
# endif
|
||||
int lineno = PyFrame_GetLineNumber(frame);
|
||||
result += " ";
|
||||
result += handle(f_code->co_filename).cast<std::string>();
|
||||
result += '(';
|
||||
result += std::to_string(lineno);
|
||||
result += "): ";
|
||||
result += handle(f_code->co_name).cast<std::string>();
|
||||
result += '\n';
|
||||
Py_DECREF(f_code);
|
||||
# if PY_VERSION_HEX >= 0x030900B1
|
||||
auto *b_frame = PyFrame_GetBack(frame);
|
||||
# else
|
||||
auto *b_frame = frame->f_back;
|
||||
Py_XINCREF(b_frame);
|
||||
# endif
|
||||
Py_DECREF(frame);
|
||||
frame = b_frame;
|
||||
}
|
||||
|
||||
have_trace = true;
|
||||
#endif //! defined(PYPY_VERSION)
|
||||
}
|
||||
|
||||
if (!message_error_string.empty()) {
|
||||
if (!have_trace) {
|
||||
result += '\n';
|
||||
}
|
||||
result += "\nMESSAGE UNAVAILABLE DUE TO EXCEPTION: " + message_error_string;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string const &error_string() const {
|
||||
if (!m_lazy_error_string_completed) {
|
||||
m_lazy_error_string += ": " + format_value_and_trace();
|
||||
m_lazy_error_string_completed = true;
|
||||
}
|
||||
return m_lazy_error_string;
|
||||
}
|
||||
|
||||
void restore() {
|
||||
if (m_restore_called) {
|
||||
pybind11_fail("Internal error: pybind11::detail::error_fetch_and_normalize::restore() "
|
||||
"called a second time. ORIGINAL ERROR: "
|
||||
+ error_string());
|
||||
}
|
||||
PyErr_Restore(m_type.inc_ref().ptr(), m_value.inc_ref().ptr(), m_trace.inc_ref().ptr());
|
||||
m_restore_called = true;
|
||||
}
|
||||
|
||||
bool matches(handle exc) const {
|
||||
return (PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()) != 0);
|
||||
}
|
||||
|
||||
// Not protecting these for simplicity.
|
||||
object m_type, m_value, m_trace;
|
||||
|
||||
private:
|
||||
// Only protecting invariants.
|
||||
mutable std::string m_lazy_error_string;
|
||||
mutable bool m_lazy_error_string_completed = false;
|
||||
mutable bool m_restore_called = false;
|
||||
};
|
||||
|
||||
inline std::string error_string() {
|
||||
return error_fetch_and_normalize("pybind11::detail::error_string").error_string();
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@ -376,38 +591,37 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
/// 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
|
||||
/// python).
|
||||
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
|
||||
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
|
||||
public:
|
||||
/// Constructs a new exception from the current Python error indicator, if any. The current
|
||||
/// Python error indicator will be cleared.
|
||||
error_already_set() : std::runtime_error(detail::error_string()) {
|
||||
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||
}
|
||||
/// Fetches the current Python exception (using PyErr_Fetch()), which will clear the
|
||||
/// current Python error indicator.
|
||||
error_already_set()
|
||||
: m_fetched_error{new detail::error_fetch_and_normalize("pybind11::error_already_set"),
|
||||
m_fetched_error_deleter} {}
|
||||
|
||||
error_already_set(const error_already_set &) = default;
|
||||
error_already_set(error_already_set &&) = default;
|
||||
/// The what() result is built lazily on demand.
|
||||
/// WARNING: This member function needs to acquire the Python GIL. This can lead to
|
||||
/// crashes (undefined behavior) if the Python interpreter is finalizing.
|
||||
const char *what() const noexcept override;
|
||||
|
||||
inline ~error_already_set() override;
|
||||
|
||||
/// Give the currently-held error back to Python, if any. If there is currently a Python error
|
||||
/// already set it is cleared first. After this call, the current object no longer stores the
|
||||
/// error variables (but the `.what()` string is still available).
|
||||
void restore() {
|
||||
PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr());
|
||||
}
|
||||
/// Restores the currently-held Python error (which will clear the Python error indicator first
|
||||
/// if already set).
|
||||
/// NOTE: This member function will always restore the normalized exception, which may or may
|
||||
/// not be the original Python exception.
|
||||
/// WARNING: The GIL must be held when this member function is called!
|
||||
void restore() { m_fetched_error->restore(); }
|
||||
|
||||
/// If it is impossible to raise the currently-held error, such as in a destructor, we can
|
||||
/// write it out using Python's unraisable hook (`sys.unraisablehook`). The error context
|
||||
/// should be some object whose `repr()` helps identify the location of the error. Python
|
||||
/// already knows the type and value of the error, so there is no need to repeat that. After
|
||||
/// this call, the current object no longer stores the error variables, and neither does
|
||||
/// Python.
|
||||
/// already knows the type and value of the error, so there is no need to repeat that.
|
||||
void discard_as_unraisable(object err_context) {
|
||||
restore();
|
||||
PyErr_WriteUnraisable(err_context.ptr());
|
||||
}
|
||||
/// An alternate version of `discard_as_unraisable()`, where a string provides information on
|
||||
/// the location of the error. For example, `__func__` could be helpful.
|
||||
/// WARNING: The GIL must be held when this member function is called!
|
||||
void discard_as_unraisable(const char *err_context) {
|
||||
discard_as_unraisable(reinterpret_steal<object>(PYBIND11_FROM_STRING(err_context)));
|
||||
}
|
||||
@ -419,23 +633,23 @@ public:
|
||||
/// Check if the currently trapped error type matches the given Python exception class (or a
|
||||
/// subclass thereof). May also be passed a tuple to search for any exception class matches in
|
||||
/// the given tuple.
|
||||
bool matches(handle exc) const {
|
||||
return (PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()) != 0);
|
||||
}
|
||||
bool matches(handle exc) const { return m_fetched_error->matches(exc); }
|
||||
|
||||
const object &type() const { return m_type; }
|
||||
const object &value() const { return m_value; }
|
||||
const object &trace() const { return m_trace; }
|
||||
const object &type() const { return m_fetched_error->m_type; }
|
||||
const object &value() const { return m_fetched_error->m_value; }
|
||||
const object &trace() const { return m_fetched_error->m_trace; }
|
||||
|
||||
private:
|
||||
object m_type, m_value, m_trace;
|
||||
std::shared_ptr<detail::error_fetch_and_normalize> m_fetched_error;
|
||||
|
||||
/// WARNING: This custom deleter needs to acquire the Python GIL. This can lead to
|
||||
/// 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
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
/// 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.
|
||||
inline void raise_from(PyObject *type, const char *message) {
|
||||
@ -466,15 +680,12 @@ inline void raise_from(PyObject *type, const char *message) {
|
||||
|
||||
/// Sets the current Python error indicator with the chosen error, performing a 'raise from'
|
||||
/// from the error contained in error_already_set to indicate that the chosen error was
|
||||
/// caused by the original error. After this function is called error_already_set will
|
||||
/// no longer contain an error.
|
||||
/// caused by the original error.
|
||||
inline void raise_from(error_already_set &err, PyObject *type, const char *message) {
|
||||
err.restore();
|
||||
raise_from(type, message);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \defgroup python_builtins const_name
|
||||
Unless stated otherwise, the following C++ functions behave the same
|
||||
as their Python counterparts.
|
||||
@ -591,12 +802,9 @@ inline ssize_t hash(handle obj) {
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
inline handle get_function(handle value) {
|
||||
if (value) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (PyInstanceMethod_Check(value.ptr())) {
|
||||
value = PyInstanceMethod_GET_FUNCTION(value.ptr());
|
||||
} else
|
||||
#endif
|
||||
if (PyMethod_Check(value.ptr())) {
|
||||
} else if (PyMethod_Check(value.ptr())) {
|
||||
value = PyMethod_GET_FUNCTION(value.ptr());
|
||||
}
|
||||
}
|
||||
@ -608,34 +816,26 @@ inline handle get_function(handle value) {
|
||||
|
||||
// copied from cpython _PyDict_GetItemStringWithError
|
||||
inline PyObject *dict_getitemstring(PyObject *v, const char *key) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *kv = nullptr, *rv = nullptr;
|
||||
kv = PyUnicode_FromString(key);
|
||||
if (kv == NULL) {
|
||||
if (kv == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
|
||||
rv = PyDict_GetItemWithError(v, kv);
|
||||
Py_DECREF(kv);
|
||||
if (rv == NULL && PyErr_Occurred()) {
|
||||
if (rv == nullptr && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return rv;
|
||||
#else
|
||||
return PyDict_GetItemString(v, key);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline PyObject *dict_getitem(PyObject *v, PyObject *key) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *rv = PyDict_GetItemWithError(v, key);
|
||||
if (rv == NULL && PyErr_Occurred()) {
|
||||
if (rv == nullptr && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return rv;
|
||||
#else
|
||||
return PyDict_GetItem(v, key);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper aliases/functions to support implicit casting of values given to python
|
||||
@ -675,7 +875,7 @@ public:
|
||||
}
|
||||
template <typename T>
|
||||
void operator=(T &&value) & {
|
||||
get_cache() = reinterpret_borrow<object>(object_or_cast(std::forward<T>(value)));
|
||||
get_cache() = ensure_object(object_or_cast(std::forward<T>(value)));
|
||||
}
|
||||
|
||||
template <typename T = Policy>
|
||||
@ -703,6 +903,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static object ensure_object(object &&o) { return std::move(o); }
|
||||
static object ensure_object(handle h) { return reinterpret_borrow<object>(h); }
|
||||
|
||||
object &get_cache() const {
|
||||
if (!cache) {
|
||||
cache = Policy::get(obj, key);
|
||||
@ -1053,12 +1256,12 @@ public:
|
||||
Name(const object &o) \
|
||||
: Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) { \
|
||||
if (!m_ptr) \
|
||||
throw error_already_set(); \
|
||||
throw ::pybind11::error_already_set(); \
|
||||
} \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(object &&o) : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) { \
|
||||
if (!m_ptr) \
|
||||
throw error_already_set(); \
|
||||
throw ::pybind11::error_already_set(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_OBJECT_CVT_DEFAULT(Name, Parent, CheckFun, ConvertFun) \
|
||||
@ -1258,8 +1461,8 @@ public:
|
||||
}
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) {
|
||||
pybind11_fail("Unable to extract string contents! (invalid type)");
|
||||
if (PyBytes_AsStringAndSize(temp.ptr(), &buffer, &length) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return std::string(buffer, (size_t) length);
|
||||
}
|
||||
@ -1273,13 +1476,6 @@ private:
|
||||
/// Return string representation -- always returns a new reference, even if already a str
|
||||
static PyObject *raw_str(PyObject *op) {
|
||||
PyObject *str_value = PyObject_Str(op);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (!str_value)
|
||||
throw error_already_set();
|
||||
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
|
||||
Py_XDECREF(str_value);
|
||||
str_value = unicode;
|
||||
#endif
|
||||
return str_value;
|
||||
}
|
||||
};
|
||||
@ -1321,14 +1517,7 @@ public:
|
||||
explicit bytes(const pybind11::str &s);
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string() const {
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) {
|
||||
pybind11_fail("Unable to extract bytes contents!");
|
||||
}
|
||||
return std::string(buffer, (size_t) length);
|
||||
}
|
||||
operator std::string() const { return string_op<std::string>(); }
|
||||
|
||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||
// enable_if is needed to avoid "ambiguous conversion" errors (see PR #3521).
|
||||
@ -1340,15 +1529,18 @@ public:
|
||||
// valid so long as the `bytes` instance remains alive and so generally should not outlive the
|
||||
// lifetime of the `bytes` instance.
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string_view() const {
|
||||
operator std::string_view() const { return string_op<std::string_view>(); }
|
||||
#endif
|
||||
private:
|
||||
template <typename T>
|
||||
T string_op() const {
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) {
|
||||
pybind11_fail("Unable to extract bytes contents!");
|
||||
if (PyBytes_AsStringAndSize(m_ptr, &buffer, &length) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return {buffer, static_cast<size_t>(length)};
|
||||
}
|
||||
#endif
|
||||
};
|
||||
// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
|
||||
// are included in the doxygen group; close here and reopen after as a workaround
|
||||
@ -1359,13 +1551,13 @@ inline bytes::bytes(const pybind11::str &s) {
|
||||
if (PyUnicode_Check(s.ptr())) {
|
||||
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(s.ptr()));
|
||||
if (!temp) {
|
||||
pybind11_fail("Unable to extract string contents! (encoding issue)");
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) {
|
||||
pybind11_fail("Unable to extract string contents! (invalid type)");
|
||||
if (PyBytes_AsStringAndSize(temp.ptr(), &buffer, &length) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length));
|
||||
if (!obj) {
|
||||
@ -1377,8 +1569,8 @@ inline bytes::bytes(const pybind11::str &s) {
|
||||
inline str::str(const bytes &b) {
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) {
|
||||
pybind11_fail("Unable to extract bytes contents!");
|
||||
if (PyBytes_AsStringAndSize(b.ptr(), &buffer, &length) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, length));
|
||||
if (!obj) {
|
||||
@ -1459,11 +1651,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 PY_VERSION_HEX < 0x03000000
|
||||
|| PyInt_Check(o)
|
||||
#endif
|
||||
) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))) {
|
||||
unsigned long v = PyLong_AsUnsignedLong(o);
|
||||
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
@ -1535,6 +1723,9 @@ public:
|
||||
explicit weakref(handle obj, handle callback = {})
|
||||
: object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
pybind11_fail("Could not allocate weak reference!");
|
||||
}
|
||||
}
|
||||
@ -1546,8 +1737,8 @@ private:
|
||||
class slice : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
|
||||
slice(handle start, handle stop, handle step) {
|
||||
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
|
||||
slice(handle start, handle stop, handle step)
|
||||
: object(PySlice_New(start.ptr(), stop.ptr(), step.ptr()), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
pybind11_fail("Could not allocate slice object!");
|
||||
}
|
||||
@ -1597,7 +1788,7 @@ public:
|
||||
void (*destructor)(PyObject *) = nullptr)
|
||||
: object(PyCapsule_New(const_cast<void *>(value), name, destructor), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
pybind11_fail("Could not allocate capsule object!");
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1605,34 +1796,46 @@ public:
|
||||
capsule(const void *value, void (*destruct)(PyObject *))
|
||||
: object(PyCapsule_New(const_cast<void *>(value), nullptr, destruct), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
pybind11_fail("Could not allocate capsule object!");
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
void *ptr = PyCapsule_GetPointer(o, nullptr);
|
||||
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);
|
||||
});
|
||||
|
||||
if (!m_ptr) {
|
||||
pybind11_fail("Could not allocate capsule object!");
|
||||
}
|
||||
|
||||
if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) {
|
||||
pybind11_fail("Could not set capsule context!");
|
||||
if (!m_ptr || PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
explicit capsule(void (*destructor)()) {
|
||||
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
|
||||
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
|
||||
const char *name = get_name_in_error_scope(o);
|
||||
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, name));
|
||||
if (destructor == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
destructor();
|
||||
});
|
||||
|
||||
if (!m_ptr) {
|
||||
pybind11_fail("Could not allocate capsule object!");
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1647,8 +1850,7 @@ public:
|
||||
const auto *name = this->name();
|
||||
T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name));
|
||||
if (!result) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("Unable to extract capsule contents!");
|
||||
throw error_already_set();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1656,12 +1858,37 @@ public:
|
||||
/// Replaces a capsule's pointer *without* calling the destructor on the existing one.
|
||||
void set_pointer(const void *value) {
|
||||
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("Could not set capsule pointer");
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
const char *name() const { return PyCapsule_GetName(m_ptr); }
|
||||
const char *name() const {
|
||||
const char *name = PyCapsule_GetName(m_ptr);
|
||||
if ((name == nullptr) && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Replaces a capsule's name *without* calling the destructor on the existing one.
|
||||
void set_name(const char *new_name) {
|
||||
if (PyCapsule_SetName(m_ptr, new_name) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const char *get_name_in_error_scope(PyObject *o) {
|
||||
error_scope error_guard;
|
||||
|
||||
const char *name = PyCapsule_GetName(o);
|
||||
if ((name == nullptr) && PyErr_Occurred()) {
|
||||
// write out and consume error raised by call to PyCapsule_GetName
|
||||
PyErr_WriteUnraisable(o);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
class tuple : public object {
|
||||
@ -1678,7 +1905,10 @@ public:
|
||||
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
detail::item_accessor operator[](T &&o) const {
|
||||
return object::operator[](std::forward<T>(o));
|
||||
}
|
||||
detail::tuple_iterator begin() const { return {*this, 0}; }
|
||||
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
|
||||
};
|
||||
@ -1738,7 +1968,10 @@ public:
|
||||
}
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::sequence_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
detail::item_accessor operator[](T &&o) const {
|
||||
return object::operator[](std::forward<T>(o));
|
||||
}
|
||||
detail::sequence_iterator begin() const { return {*this, 0}; }
|
||||
detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; }
|
||||
};
|
||||
@ -1757,7 +1990,10 @@ public:
|
||||
size_t size() const { return (size_t) PyList_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::list_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
detail::item_accessor operator[](T &&o) const {
|
||||
return object::operator[](std::forward<T>(o));
|
||||
}
|
||||
detail::list_iterator begin() const { return {*this, 0}; }
|
||||
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
|
||||
template <typename T>
|
||||
@ -1780,25 +2016,35 @@ class kwargs : public dict {
|
||||
PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check)
|
||||
};
|
||||
|
||||
class set : public object {
|
||||
class anyset : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New)
|
||||
set() : object(PySet_New(nullptr), stolen_t{}) {
|
||||
PYBIND11_OBJECT(anyset, object, PyAnySet_Check)
|
||||
size_t size() const { return static_cast<size_t>(PySet_Size(m_ptr)); }
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
class set : public anyset {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(set, anyset, PySet_Check, PySet_New)
|
||||
set() : anyset(PySet_New(nullptr), stolen_t{}) {
|
||||
if (!m_ptr) {
|
||||
pybind11_fail("Could not allocate set object!");
|
||||
}
|
||||
}
|
||||
size_t size() const { return (size_t) PySet_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
template <typename T>
|
||||
bool add(T &&val) /* py-non-const */ {
|
||||
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
|
||||
}
|
||||
void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
|
||||
template <typename T>
|
||||
bool contains(T &&val) const {
|
||||
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
|
||||
}
|
||||
};
|
||||
|
||||
class frozenset : public anyset {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(frozenset, anyset, PyFrozenSet_Check, PyFrozenSet_New)
|
||||
};
|
||||
|
||||
class function : public object {
|
||||
@ -1910,8 +2156,8 @@ public:
|
||||
return memoryview::from_buffer(reinterpret_cast<void *>(ptr),
|
||||
sizeof(T),
|
||||
format_descriptor<T>::value,
|
||||
shape,
|
||||
strides,
|
||||
std::move(shape),
|
||||
std::move(strides),
|
||||
readonly);
|
||||
}
|
||||
|
||||
@ -1919,10 +2165,10 @@ public:
|
||||
static memoryview from_buffer(const T *ptr,
|
||||
detail::any_container<ssize_t> shape,
|
||||
detail::any_container<ssize_t> strides) {
|
||||
return memoryview::from_buffer(const_cast<T *>(ptr), shape, strides, true);
|
||||
return memoryview::from_buffer(
|
||||
const_cast<T *>(ptr), std::move(shape), std::move(strides), true);
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
/** \rst
|
||||
Creates ``memoryview`` from static memory.
|
||||
|
||||
@ -1930,8 +2176,6 @@ public:
|
||||
managed by Python. The caller is responsible for managing the lifetime
|
||||
of ``mem``, which MUST outlive the memoryview constructed here.
|
||||
|
||||
This method is not available in Python 2.
|
||||
|
||||
See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
|
||||
|
||||
.. _PyMemoryView_FromMemory:
|
||||
@ -1950,12 +2194,10 @@ public:
|
||||
return memoryview::from_memory(const_cast<void *>(mem), size, true);
|
||||
}
|
||||
|
||||
# ifdef PYBIND11_HAS_STRING_VIEW
|
||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||
static memoryview from_memory(std::string_view mem) {
|
||||
return from_memory(const_cast<char *>(mem.data()), static_cast<ssize_t>(mem.size()), true);
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -2010,11 +2252,7 @@ inline size_t len(handle h) {
|
||||
/// Get the length hint of a Python object.
|
||||
/// Returns 0 when this cannot be determined.
|
||||
inline size_t len_hint(handle h) {
|
||||
#if PY_VERSION_HEX >= 0x03040000
|
||||
ssize_t result = PyObject_LengthHint(h.ptr(), 0);
|
||||
#else
|
||||
ssize_t result = PyObject_Length(h.ptr());
|
||||
#endif
|
||||
if (result < 0) {
|
||||
// Sometimes a length can't be determined at all (eg generators)
|
||||
// In which case simply return 0
|
||||
@ -2029,13 +2267,6 @@ inline str repr(handle h) {
|
||||
if (!str_value) {
|
||||
throw error_already_set();
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
|
||||
Py_XDECREF(str_value);
|
||||
str_value = unicode;
|
||||
if (!str_value)
|
||||
throw error_already_set();
|
||||
#endif
|
||||
return reinterpret_steal<str>(str_value);
|
||||
}
|
||||
|
||||
@ -2062,6 +2293,10 @@ item_accessor object_api<D>::operator[](handle key) const {
|
||||
return {derived(), reinterpret_borrow<object>(key)};
|
||||
}
|
||||
template <typename D>
|
||||
item_accessor object_api<D>::operator[](object &&key) const {
|
||||
return {derived(), std::move(key)};
|
||||
}
|
||||
template <typename D>
|
||||
item_accessor object_api<D>::operator[](const char *key) const {
|
||||
return {derived(), pybind11::str(key)};
|
||||
}
|
||||
@ -2070,6 +2305,10 @@ obj_attr_accessor object_api<D>::attr(handle key) const {
|
||||
return {derived(), reinterpret_borrow<object>(key)};
|
||||
}
|
||||
template <typename D>
|
||||
obj_attr_accessor object_api<D>::attr(object &&key) const {
|
||||
return {derived(), std::move(key)};
|
||||
}
|
||||
template <typename D>
|
||||
str_attr_accessor object_api<D>::attr(const char *key) const {
|
||||
return {derived(), key};
|
||||
}
|
||||
|
@ -13,9 +13,9 @@
|
||||
#include "detail/common.h"
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@ -55,10 +55,10 @@ struct set_caster {
|
||||
using key_conv = make_caster<Key>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<pybind11::set>(src)) {
|
||||
if (!isinstance<anyset>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto s = reinterpret_borrow<pybind11::set>(src);
|
||||
auto s = reinterpret_borrow<anyset>(src);
|
||||
value.clear();
|
||||
for (auto entry : s) {
|
||||
key_conv conv;
|
||||
@ -79,7 +79,7 @@ struct set_caster {
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(value_)) {
|
||||
if (!value_ || !s.add(std::move(value_))) {
|
||||
return handle();
|
||||
}
|
||||
}
|
||||
@ -128,7 +128,7 @@ struct map_caster {
|
||||
if (!key || !value) {
|
||||
return handle();
|
||||
}
|
||||
d[key] = value;
|
||||
d[std::move(key)] = std::move(value);
|
||||
}
|
||||
return d.release();
|
||||
}
|
||||
@ -372,7 +372,7 @@ struct variant_caster<V<Ts...>> {
|
||||
bool load_alternative(handle src, bool convert, type_list<U, Us...>) {
|
||||
auto caster = make_caster<U>();
|
||||
if (caster.load(src, convert)) {
|
||||
value = cast_op<U>(caster);
|
||||
value = cast_op<U>(std::move(caster));
|
||||
return true;
|
||||
}
|
||||
return load_alternative(src, convert, type_list<Us...>{});
|
||||
@ -406,6 +406,9 @@ struct variant_caster<V<Ts...>> {
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
template <typename... Ts>
|
||||
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {};
|
||||
|
||||
template <>
|
||||
struct type_caster<std::monostate> : public void_caster<std::monostate> {};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
@ -13,22 +13,28 @@
|
||||
#include <string>
|
||||
|
||||
#ifdef __has_include
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# if defined(PYBIND11_CPP17)
|
||||
# if __has_include(<filesystem>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# elif __has_include(<experimental/filesystem>)
|
||||
# include <experimental/filesystem>
|
||||
# define PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
|
||||
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) \
|
||||
&& !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
|
||||
# error \
|
||||
"#include <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
|
||||
"Neither #include <filesystem> nor #include <experimental/filesystem is available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
template <typename T>
|
||||
struct path_caster {
|
||||
|
||||
@ -95,9 +101,16 @@ public:
|
||||
PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
|
||||
};
|
||||
|
||||
#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
template <>
|
||||
struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {};
|
||||
#endif // PYBIND11_HAS_FILESYSTEM
|
||||
#elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
template <>
|
||||
struct type_caster<std::experimental::filesystem::path>
|
||||
: public path_caster<std::experimental::filesystem::path> {};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
@ -232,7 +232,7 @@ void vector_modifiers(
|
||||
/// Slicing protocol
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[](const Vector &v, slice slice) -> Vector * {
|
||||
[](const Vector &v, const slice &slice) -> Vector * {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||
@ -253,7 +253,7 @@ void vector_modifiers(
|
||||
|
||||
cl.def(
|
||||
"__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
[](Vector &v, const slice &slice, const Vector &value) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||
throw error_already_set();
|
||||
@ -281,7 +281,7 @@ void vector_modifiers(
|
||||
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[](Vector &v, slice slice) {
|
||||
[](Vector &v, const slice &slice) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||
|
13
noxfile.py
13
noxfile.py
@ -1,9 +1,14 @@
|
||||
import os
|
||||
|
||||
import nox
|
||||
|
||||
nox.needs_version = ">=2022.1.7"
|
||||
nox.options.sessions = ["lint", "tests", "tests_packaging"]
|
||||
|
||||
PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
PYTHON_VERISONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.7", "pypy3.8"]
|
||||
|
||||
if os.environ.get("CI", None):
|
||||
nox.options.error_on_missing_interpreters = True
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
@ -15,7 +20,7 @@ def lint(session: nox.Session) -> None:
|
||||
session.run("pre-commit", "run", "-a")
|
||||
|
||||
|
||||
@nox.session(python=PYTHON_VERSIONS)
|
||||
@nox.session(python=PYTHON_VERISONS)
|
||||
def tests(session: nox.Session) -> None:
|
||||
"""
|
||||
Run the tests (requires a compiler).
|
||||
@ -56,10 +61,10 @@ def docs(session: nox.Session) -> None:
|
||||
session.chdir("docs")
|
||||
|
||||
if "pdf" in session.posargs:
|
||||
session.run("sphinx-build", "-b", "latexpdf", ".", "_build")
|
||||
session.run("sphinx-build", "-M", "latexpdf", ".", "_build")
|
||||
return
|
||||
|
||||
session.run("sphinx-build", "-b", "html", ".", "_build")
|
||||
session.run("sphinx-build", "-M", "html", ".", "_build")
|
||||
|
||||
if "serve" in session.posargs:
|
||||
session.log("Launching docs at http://localhost:8000/ - use Ctrl-C to quit")
|
||||
|
@ -1,4 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
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
|
||||
|
@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
# pylint: disable=missing-function-docstring
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
@ -8,8 +7,7 @@ import sysconfig
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
|
||||
def print_includes():
|
||||
# type: () -> None
|
||||
def print_includes() -> None:
|
||||
dirs = [
|
||||
sysconfig.get_path("include"),
|
||||
sysconfig.get_path("platinclude"),
|
||||
@ -25,8 +23,7 @@ def print_includes():
|
||||
print(" ".join("-I" + d for d in unique_dirs))
|
||||
|
||||
|
||||
def main():
|
||||
# type: () -> None
|
||||
def main() -> None:
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
|
@ -1,12 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Union
|
||||
|
||||
|
||||
def _to_int(s):
|
||||
def _to_int(s: str) -> Union[int, str]:
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
return s
|
||||
|
||||
|
||||
__version__ = "2.9.2"
|
||||
__version__ = "2.10.0"
|
||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||
|
@ -1,6 +0,0 @@
|
||||
from typing import Tuple, Union
|
||||
|
||||
def _to_int(s: str) -> Union[int, str]: ...
|
||||
|
||||
__version__: str
|
||||
version_info: Tuple[Union[int, str], ...]
|
@ -1,21 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def get_include(user=False):
|
||||
# type: (bool) -> str
|
||||
def get_include(user: bool = False) -> str: # pylint: disable=unused-argument
|
||||
"""
|
||||
Return the path to the pybind11 include directory. The historical "user"
|
||||
argument is unused, and may be removed.
|
||||
"""
|
||||
installed_path = os.path.join(DIR, "include")
|
||||
source_path = os.path.join(os.path.dirname(DIR), "include")
|
||||
return installed_path if os.path.exists(installed_path) else source_path
|
||||
|
||||
|
||||
def get_cmake_dir():
|
||||
# type: () -> str
|
||||
def get_cmake_dir() -> str:
|
||||
"""
|
||||
Return the path to the pybind11 CMake module directory.
|
||||
"""
|
||||
cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11")
|
||||
if os.path.exists(cmake_installed_path):
|
||||
return cmake_installed_path
|
||||
else:
|
||||
msg = "pybind11 not installed, installation required to access the CMake files"
|
||||
raise ImportError(msg)
|
||||
|
||||
msg = "pybind11 not installed, installation required to access the CMake files"
|
||||
raise ImportError(msg)
|
||||
|
@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This module provides helpers for C++11+ projects using pybind11.
|
||||
|
||||
@ -49,6 +47,20 @@ import sysconfig
|
||||
import tempfile
|
||||
import threading
|
||||
import warnings
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
try:
|
||||
from setuptools import Extension as _Extension
|
||||
@ -61,7 +73,6 @@ import distutils.ccompiler
|
||||
import distutils.errors
|
||||
|
||||
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
|
||||
PY2 = sys.version_info[0] < 3
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
|
||||
|
||||
@ -73,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):
|
||||
class Pybind11Extension(_Extension): # type: ignore[misc]
|
||||
"""
|
||||
Build a C++11+ Extension module with pybind11. This automatically adds the
|
||||
recommended flags when you init the extension and assumes C++ sources - you
|
||||
@ -95,21 +106,18 @@ class Pybind11Extension(_Extension):
|
||||
|
||||
If you want to add pybind11 headers manually, for example for an exact
|
||||
git checkout, then set ``include_pybind11=False``.
|
||||
|
||||
Warning: do not use property-based access to the instance on Python 2 -
|
||||
this is an ugly old-style class due to Distutils.
|
||||
"""
|
||||
|
||||
# flags are prepended, so that they can be further overridden, e.g. by
|
||||
# ``extra_compile_args=["-g"]``.
|
||||
|
||||
def _add_cflags(self, flags):
|
||||
def _add_cflags(self, flags: List[str]) -> None:
|
||||
self.extra_compile_args[:0] = flags
|
||||
|
||||
def _add_ldflags(self, flags):
|
||||
def _add_ldflags(self, flags: List[str]) -> None:
|
||||
self.extra_link_args[:0] = flags
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
|
||||
self._cxx_level = 0
|
||||
cxx_std = kwargs.pop("cxx_std", 0)
|
||||
@ -119,9 +127,7 @@ class Pybind11Extension(_Extension):
|
||||
|
||||
include_pybind11 = kwargs.pop("include_pybind11", True)
|
||||
|
||||
# Can't use super here because distutils has old-style classes in
|
||||
# Python 2!
|
||||
_Extension.__init__(self, *args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Include the installed package pybind11 headers
|
||||
if include_pybind11:
|
||||
@ -133,11 +139,10 @@ class Pybind11Extension(_Extension):
|
||||
|
||||
if pyinc not in self.include_dirs:
|
||||
self.include_dirs.append(pyinc)
|
||||
except ImportError:
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
# Have to use the accessor manually to support Python 2 distutils
|
||||
Pybind11Extension.cxx_std.__set__(self, cxx_std)
|
||||
self.cxx_std = cxx_std
|
||||
|
||||
cflags = []
|
||||
ldflags = []
|
||||
@ -157,18 +162,18 @@ class Pybind11Extension(_Extension):
|
||||
self._add_ldflags(ldflags)
|
||||
|
||||
@property
|
||||
def cxx_std(self):
|
||||
def cxx_std(self) -> int:
|
||||
"""
|
||||
The CXX standard level. If set, will add the required flags. If left
|
||||
at 0, it will trigger an automatic search when pybind11's build_ext
|
||||
is used. If None, will have no effect. Besides just the flags, this
|
||||
may add a register warning/error fix for Python 2 or macos-min 10.9
|
||||
or 10.14.
|
||||
The CXX standard level. If set, will add the required flags. If left at
|
||||
0, it will trigger an automatic search when pybind11's build_ext is
|
||||
used. If None, will have no effect. Besides just the flags, this may
|
||||
add a macos-min 10.9 or 10.14 flag if MACOSX_DEPLOYMENT_TARGET is
|
||||
unset.
|
||||
"""
|
||||
return self._cxx_level
|
||||
|
||||
@cxx_std.setter
|
||||
def cxx_std(self, level):
|
||||
def cxx_std(self, level: int) -> None:
|
||||
|
||||
if self._cxx_level:
|
||||
warnings.warn("You cannot safely change the cxx_level after setting it!")
|
||||
@ -195,31 +200,20 @@ class Pybind11Extension(_Extension):
|
||||
current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2])
|
||||
desired_macos = (10, 9) if level < 17 else (10, 14)
|
||||
macos_string = ".".join(str(x) for x in min(current_macos, desired_macos))
|
||||
macosx_min = "-mmacosx-version-min=" + macos_string
|
||||
macosx_min = f"-mmacosx-version-min={macos_string}"
|
||||
cflags += [macosx_min]
|
||||
ldflags += [macosx_min]
|
||||
|
||||
if PY2:
|
||||
if WIN:
|
||||
# Will be ignored on MSVC 2015, where C++17 is not supported so
|
||||
# this flag is not valid.
|
||||
cflags += ["/wd5033"]
|
||||
elif level >= 17:
|
||||
cflags += ["-Wno-register"]
|
||||
elif level >= 14:
|
||||
cflags += ["-Wno-deprecated-register"]
|
||||
|
||||
self._add_cflags(cflags)
|
||||
self._add_ldflags(ldflags)
|
||||
|
||||
|
||||
# Just in case someone clever tries to multithread
|
||||
tmp_chdir_lock = threading.Lock()
|
||||
cpp_cache_lock = threading.Lock()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tmp_chdir():
|
||||
def tmp_chdir() -> Iterator[str]:
|
||||
"Prepare and enter a temporary directory, cleanup when done"
|
||||
|
||||
# Threadsafe
|
||||
@ -235,7 +229,7 @@ def tmp_chdir():
|
||||
|
||||
|
||||
# cf http://bugs.python.org/issue26689
|
||||
def has_flag(compiler, flag):
|
||||
def has_flag(compiler: Any, flag: str) -> bool:
|
||||
"""
|
||||
Return the flag if a flag name is supported on the
|
||||
specified compiler, otherwise None (can be used as a boolean).
|
||||
@ -243,13 +237,12 @@ def has_flag(compiler, flag):
|
||||
"""
|
||||
|
||||
with tmp_chdir():
|
||||
fname = "flagcheck.cpp"
|
||||
with open(fname, "w") as f:
|
||||
# Don't trigger -Wunused-parameter.
|
||||
f.write("int main (int, char **) { return 0; }")
|
||||
fname = Path("flagcheck.cpp")
|
||||
# Don't trigger -Wunused-parameter.
|
||||
fname.write_text("int main (int, char **) { return 0; }", encoding="utf-8")
|
||||
|
||||
try:
|
||||
compiler.compile([fname], extra_postargs=[flag])
|
||||
compiler.compile([str(fname)], extra_postargs=[flag])
|
||||
except distutils.errors.CompileError:
|
||||
return False
|
||||
return True
|
||||
@ -259,7 +252,8 @@ def has_flag(compiler, flag):
|
||||
cpp_flag_cache = None
|
||||
|
||||
|
||||
def auto_cpp_level(compiler):
|
||||
@lru_cache()
|
||||
def auto_cpp_level(compiler: Any) -> Union[str, int]:
|
||||
"""
|
||||
Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows.
|
||||
"""
|
||||
@ -267,48 +261,38 @@ def auto_cpp_level(compiler):
|
||||
if WIN:
|
||||
return "latest"
|
||||
|
||||
global cpp_flag_cache
|
||||
|
||||
# If this has been previously calculated with the same args, return that
|
||||
with cpp_cache_lock:
|
||||
if cpp_flag_cache:
|
||||
return cpp_flag_cache
|
||||
|
||||
levels = [17, 14, 11]
|
||||
|
||||
for level in levels:
|
||||
if has_flag(compiler, STD_TMPL.format(level)):
|
||||
with cpp_cache_lock:
|
||||
cpp_flag_cache = level
|
||||
return level
|
||||
|
||||
msg = "Unsupported compiler -- at least C++11 support is needed!"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
class build_ext(_build_ext): # noqa: N801
|
||||
class build_ext(_build_ext): # type: ignore[misc] # 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
|
||||
for now, and is completely optional otherwise.
|
||||
"""
|
||||
|
||||
def build_extensions(self):
|
||||
def build_extensions(self) -> None:
|
||||
"""
|
||||
Build extensions, injecting C++ std for Pybind11Extension if needed.
|
||||
"""
|
||||
|
||||
for ext in self.extensions:
|
||||
if hasattr(ext, "_cxx_level") and ext._cxx_level == 0:
|
||||
# Python 2 syntax - old-style distutils class
|
||||
ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler))
|
||||
ext.cxx_std = auto_cpp_level(self.compiler)
|
||||
|
||||
# Python 2 doesn't allow super here, since distutils uses old-style
|
||||
# classes!
|
||||
_build_ext.build_extensions(self)
|
||||
super().build_extensions()
|
||||
|
||||
|
||||
def intree_extensions(paths, package_dir=None):
|
||||
def intree_extensions(
|
||||
paths: Iterable[str], package_dir: Optional[Dict[str, str]] = None
|
||||
) -> List[Pybind11Extension]:
|
||||
"""
|
||||
Generate Pybind11Extensions from source files directly located in a Python
|
||||
source tree.
|
||||
@ -318,33 +302,37 @@ def intree_extensions(paths, package_dir=None):
|
||||
not contain an ``__init__.py`` file.
|
||||
"""
|
||||
exts = []
|
||||
for path in paths:
|
||||
if package_dir is None:
|
||||
|
||||
if package_dir is None:
|
||||
for path in paths:
|
||||
parent, _ = os.path.split(path)
|
||||
while os.path.exists(os.path.join(parent, "__init__.py")):
|
||||
parent, _ = os.path.split(parent)
|
||||
relname, _ = os.path.splitext(os.path.relpath(path, parent))
|
||||
qualified_name = relname.replace(os.path.sep, ".")
|
||||
exts.append(Pybind11Extension(qualified_name, [path]))
|
||||
return exts
|
||||
|
||||
for path in paths:
|
||||
for prefix, parent in package_dir.items():
|
||||
if path.startswith(parent):
|
||||
relname, _ = os.path.splitext(os.path.relpath(path, parent))
|
||||
qualified_name = relname.replace(os.path.sep, ".")
|
||||
if prefix:
|
||||
qualified_name = prefix + "." + qualified_name
|
||||
exts.append(Pybind11Extension(qualified_name, [path]))
|
||||
break
|
||||
else:
|
||||
found = False
|
||||
for prefix, parent in package_dir.items():
|
||||
if path.startswith(parent):
|
||||
found = True
|
||||
relname, _ = os.path.splitext(os.path.relpath(path, parent))
|
||||
qualified_name = relname.replace(os.path.sep, ".")
|
||||
if prefix:
|
||||
qualified_name = prefix + "." + qualified_name
|
||||
exts.append(Pybind11Extension(qualified_name, [path]))
|
||||
if not found:
|
||||
raise ValueError(
|
||||
"path {} is not a child of any of the directories listed "
|
||||
"in 'package_dir' ({})".format(path, package_dir)
|
||||
)
|
||||
msg = (
|
||||
f"path {path} is not a child of any of the directories listed "
|
||||
f"in 'package_dir' ({package_dir})"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
return exts
|
||||
|
||||
|
||||
def naive_recompile(obj, src):
|
||||
def naive_recompile(obj: str, src: str) -> bool:
|
||||
"""
|
||||
This will recompile only if the source file changes. It does not check
|
||||
header files, so a more advanced function or Ccache is better if you have
|
||||
@ -353,7 +341,7 @@ def naive_recompile(obj, src):
|
||||
return os.stat(obj).st_mtime < os.stat(src).st_mtime
|
||||
|
||||
|
||||
def no_recompile(obg, src):
|
||||
def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument
|
||||
"""
|
||||
This is the safest but slowest choice (and is the default) - will always
|
||||
recompile sources.
|
||||
@ -361,15 +349,33 @@ def no_recompile(obg, src):
|
||||
return True
|
||||
|
||||
|
||||
S = TypeVar("S", bound="ParallelCompile")
|
||||
|
||||
CCompilerMethod = Callable[
|
||||
[
|
||||
distutils.ccompiler.CCompiler,
|
||||
List[str],
|
||||
Optional[str],
|
||||
Optional[Union[Tuple[str], Tuple[str, Optional[str]]]],
|
||||
Optional[List[str]],
|
||||
bool,
|
||||
Optional[List[str]],
|
||||
Optional[List[str]],
|
||||
Optional[List[str]],
|
||||
],
|
||||
List[str],
|
||||
]
|
||||
|
||||
|
||||
# Optional parallel compile utility
|
||||
# inspired by: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils
|
||||
# and: https://github.com/tbenthompson/cppimport/blob/stable/cppimport/build_module.py
|
||||
# and NumPy's parallel distutils module:
|
||||
# https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py
|
||||
class ParallelCompile(object):
|
||||
class ParallelCompile:
|
||||
"""
|
||||
Make a parallel compile function. Inspired by
|
||||
numpy.distutils.ccompiler.CCompiler_compile and cppimport.
|
||||
numpy.distutils.ccompiler.CCompiler.compile and cppimport.
|
||||
|
||||
This takes several arguments that allow you to customize the compile
|
||||
function created:
|
||||
@ -404,35 +410,41 @@ class ParallelCompile(object):
|
||||
|
||||
__slots__ = ("envvar", "default", "max", "_old", "needs_recompile")
|
||||
|
||||
def __init__(self, envvar=None, default=0, max=0, needs_recompile=no_recompile):
|
||||
def __init__(
|
||||
self,
|
||||
envvar: Optional[str] = None,
|
||||
default: int = 0,
|
||||
max: int = 0, # pylint: disable=redefined-builtin
|
||||
needs_recompile: Callable[[str, str], bool] = no_recompile,
|
||||
) -> None:
|
||||
self.envvar = envvar
|
||||
self.default = default
|
||||
self.max = max
|
||||
self.needs_recompile = needs_recompile
|
||||
self._old = []
|
||||
self._old: List[CCompilerMethod] = []
|
||||
|
||||
def function(self):
|
||||
def function(self) -> CCompilerMethod:
|
||||
"""
|
||||
Builds a function object usable as distutils.ccompiler.CCompiler.compile.
|
||||
"""
|
||||
|
||||
def compile_function(
|
||||
compiler,
|
||||
sources,
|
||||
output_dir=None,
|
||||
macros=None,
|
||||
include_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None,
|
||||
depends=None,
|
||||
):
|
||||
compiler: distutils.ccompiler.CCompiler,
|
||||
sources: List[str],
|
||||
output_dir: Optional[str] = None,
|
||||
macros: Optional[Union[Tuple[str], Tuple[str, Optional[str]]]] = None,
|
||||
include_dirs: Optional[List[str]] = None,
|
||||
debug: bool = False,
|
||||
extra_preargs: Optional[List[str]] = None,
|
||||
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(
|
||||
macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined]
|
||||
output_dir, macros, include_dirs, sources, depends, extra_postargs
|
||||
)
|
||||
cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs)
|
||||
cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs) # type: ignore[attr-defined]
|
||||
|
||||
# The number of threads; start with default.
|
||||
threads = self.default
|
||||
@ -441,14 +453,14 @@ class ParallelCompile(object):
|
||||
if self.envvar is not None:
|
||||
threads = int(os.environ.get(self.envvar, self.default))
|
||||
|
||||
def _single_compile(obj):
|
||||
def _single_compile(obj: Any) -> None:
|
||||
try:
|
||||
src, ext = build[obj]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if not os.path.exists(obj) or self.needs_recompile(obj, src):
|
||||
compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
|
||||
compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) # type: ignore[attr-defined]
|
||||
|
||||
try:
|
||||
# Importing .synchronize checks for platforms that have some multiprocessing
|
||||
@ -466,14 +478,9 @@ class ParallelCompile(object):
|
||||
threads = 1
|
||||
|
||||
if threads > 1:
|
||||
pool = ThreadPool(threads)
|
||||
# In Python 2, ThreadPool can't be used as a context manager.
|
||||
# Once we are no longer supporting it, this can be 'with pool:'
|
||||
try:
|
||||
with ThreadPool(threads) as pool:
|
||||
for _ in pool.imap_unordered(_single_compile, objects):
|
||||
pass
|
||||
finally:
|
||||
pool.terminate()
|
||||
else:
|
||||
for ob in objects:
|
||||
_single_compile(ob)
|
||||
@ -482,13 +489,16 @@ class ParallelCompile(object):
|
||||
|
||||
return compile_function
|
||||
|
||||
def install(self):
|
||||
distutils.ccompiler.CCompiler.compile = self.function()
|
||||
def install(self: S) -> S:
|
||||
"""
|
||||
Installs the compile function into distutils.ccompiler.CCompiler.compile.
|
||||
"""
|
||||
distutils.ccompiler.CCompiler.compile = self.function() # type: ignore[assignment]
|
||||
return self
|
||||
|
||||
def __enter__(self):
|
||||
def __enter__(self: S) -> S:
|
||||
self._old.append(distutils.ccompiler.CCompiler.compile)
|
||||
return self.install()
|
||||
|
||||
def __exit__(self, *args):
|
||||
distutils.ccompiler.CCompiler.compile = self._old.pop()
|
||||
def __exit__(self, *args: Any) -> None:
|
||||
distutils.ccompiler.CCompiler.compile = self._old.pop() # type: ignore[assignment]
|
||||
|
@ -1,63 +0,0 @@
|
||||
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
|
||||
# pre-commit).
|
||||
|
||||
import contextlib
|
||||
import distutils.ccompiler
|
||||
from distutils.command.build_ext import build_ext as _build_ext # type: ignore
|
||||
from distutils.extension import Extension as _Extension
|
||||
from types import TracebackType
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
|
||||
|
||||
WIN: bool
|
||||
PY2: bool
|
||||
MACOS: bool
|
||||
STD_TMPL: str
|
||||
|
||||
class Pybind11Extension(_Extension):
|
||||
def _add_cflags(self, *flags: str) -> None: ...
|
||||
def _add_lflags(self, *flags: str) -> None: ...
|
||||
def __init__(
|
||||
self, *args: Any, cxx_std: int = 0, language: str = "c++", **kwargs: Any
|
||||
) -> None: ...
|
||||
@property
|
||||
def cxx_std(self) -> int: ...
|
||||
@cxx_std.setter
|
||||
def cxx_std(self, level: int) -> None: ...
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tmp_chdir() -> Iterator[str]: ...
|
||||
def has_flag(compiler: distutils.ccompiler.CCompiler, flag: str) -> bool: ...
|
||||
def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]: ...
|
||||
|
||||
class build_ext(_build_ext): # type: ignore
|
||||
def build_extensions(self) -> None: ...
|
||||
|
||||
def intree_extensions(
|
||||
paths: Iterator[str], package_dir: Optional[Dict[str, str]] = None
|
||||
) -> List[Pybind11Extension]: ...
|
||||
def no_recompile(obj: str, src: str) -> bool: ...
|
||||
def naive_recompile(obj: str, src: str) -> bool: ...
|
||||
|
||||
T = TypeVar("T", bound="ParallelCompile")
|
||||
|
||||
class ParallelCompile:
|
||||
envvar: Optional[str]
|
||||
default: int
|
||||
max: int
|
||||
needs_recompile: Callable[[str, str], bool]
|
||||
def __init__(
|
||||
self,
|
||||
envvar: Optional[str] = None,
|
||||
default: int = 0,
|
||||
max: int = 0,
|
||||
needs_recompile: Callable[[str, str], bool] = no_recompile,
|
||||
) -> None: ...
|
||||
def function(self) -> Any: ...
|
||||
def install(self: T) -> T: ...
|
||||
def __enter__(self: T) -> T: ...
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> None: ...
|
@ -1,5 +1,5 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"]
|
||||
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.check-manifest]
|
||||
@ -22,20 +22,40 @@ known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
|
||||
profile = "black"
|
||||
|
||||
[tool.mypy]
|
||||
files = "pybind11"
|
||||
python_version = "2.7"
|
||||
warn_unused_configs = true
|
||||
files = ["pybind11"]
|
||||
python_version = "3.6"
|
||||
strict = true
|
||||
show_error_codes = true
|
||||
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
||||
warn_unreachable = true
|
||||
|
||||
disallow_any_generics = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
no_implicit_optional = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_return_any = true
|
||||
no_implicit_reexport = true
|
||||
strict_equality = true
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["ghapi.*", "setuptools.*"]
|
||||
ignore_missing_imports = true
|
||||
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "6.0"
|
||||
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
|
||||
xfail_strict = true
|
||||
filterwarnings = ["error"]
|
||||
log_cli_level = "info"
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
timeout=300
|
||||
|
||||
|
||||
[tool.pylint]
|
||||
master.py-version = "3.6"
|
||||
reports.output-format = "colorized"
|
||||
messages_control.disable = [
|
||||
"design",
|
||||
"fixme",
|
||||
"imports",
|
||||
"line-too-long",
|
||||
"imports",
|
||||
"invalid-name",
|
||||
"protected-access",
|
||||
"missing-module-docstring",
|
||||
]
|
||||
|
25
setup.cfg
25
setup.cfg
@ -13,14 +13,13 @@ classifiers =
|
||||
Topic :: Software Development :: Libraries :: Python Modules
|
||||
Topic :: Utilities
|
||||
Programming Language :: C++
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
License :: OSI Approved :: BSD License
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
@ -39,25 +38,13 @@ project_urls =
|
||||
Chat = https://gitter.im/pybind/Lobby
|
||||
|
||||
[options]
|
||||
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
|
||||
python_requires = >=3.6
|
||||
zip_safe = False
|
||||
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
|
||||
[flake8]
|
||||
max-line-length = 99
|
||||
max-line-length = 120
|
||||
show_source = True
|
||||
exclude = .git, __pycache__, build, dist, docs, tools, venv
|
||||
ignore =
|
||||
# required for pretty matrix formatting: multiple spaces after `,` and `[`
|
||||
E201, E241, W504,
|
||||
# camelcase 'cPickle' imported as lowercase 'pickle'
|
||||
N813
|
||||
# Black conflict
|
||||
W503, E203
|
||||
|
||||
|
||||
[tool:pytest]
|
||||
timeout = 300
|
||||
extend-ignore = E203, E722, B950
|
||||
extend-select = B9
|
||||
|
136
setup.py
136
setup.py
@ -1,56 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Setup script for PyPI; use CMakeFile.txt to build extension modules
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Dict, Iterator, List, Union
|
||||
|
||||
import setuptools.command.sdist
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
DIR = Path(__file__).parent.absolute()
|
||||
VERSION_REGEX = re.compile(
|
||||
r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
|
||||
)
|
||||
VERSION_FILE = Path("pybind11/_version.py")
|
||||
COMMON_FILE = Path("include/pybind11/detail/common.h")
|
||||
|
||||
|
||||
def build_expected_version_hex(matches):
|
||||
def build_expected_version_hex(matches: Dict[str, str]) -> str:
|
||||
patch_level_serial = matches["PATCH"]
|
||||
serial = None
|
||||
try:
|
||||
major = int(matches["MAJOR"])
|
||||
minor = int(matches["MINOR"])
|
||||
flds = patch_level_serial.split(".")
|
||||
if flds:
|
||||
patch = int(flds[0])
|
||||
level = None
|
||||
if len(flds) == 1:
|
||||
level = "0"
|
||||
serial = 0
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
major = int(matches["MAJOR"])
|
||||
minor = int(matches["MINOR"])
|
||||
flds = patch_level_serial.split(".")
|
||||
if flds:
|
||||
patch = int(flds[0])
|
||||
if len(flds) == 1:
|
||||
level = "0"
|
||||
serial = 0
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
if serial is None:
|
||||
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
|
||||
msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
|
||||
raise RuntimeError(msg)
|
||||
return (
|
||||
"0x"
|
||||
+ "{:02x}{:02x}{:02x}{}{:x}".format(
|
||||
major, minor, patch, level[:1], serial
|
||||
).upper()
|
||||
)
|
||||
version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
|
||||
return f"0x{version_hex_str.upper()}"
|
||||
|
||||
|
||||
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
|
||||
@ -58,82 +52,67 @@ def build_expected_version_hex(matches):
|
||||
|
||||
global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False)
|
||||
|
||||
setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
|
||||
setup_py = Path(
|
||||
"tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
|
||||
)
|
||||
extra_cmd = 'cmdclass["sdist"] = SDist\n'
|
||||
|
||||
to_src = (
|
||||
("pyproject.toml", "tools/pyproject.toml"),
|
||||
("setup.py", setup_py),
|
||||
(Path("pyproject.toml"), Path("tools/pyproject.toml")),
|
||||
(Path("setup.py"), setup_py),
|
||||
)
|
||||
|
||||
|
||||
# Read the listed version
|
||||
with open("pybind11/_version.py") as f:
|
||||
code = compile(f.read(), "pybind11/_version.py", "exec")
|
||||
loc = {}
|
||||
loc: Dict[str, str] = {}
|
||||
code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
|
||||
exec(code, loc)
|
||||
version = loc["__version__"]
|
||||
|
||||
# Verify that the version matches the one in C++
|
||||
with io.open("include/pybind11/detail/common.h", encoding="utf8") as f:
|
||||
matches = dict(VERSION_REGEX.findall(f.read()))
|
||||
matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
|
||||
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
|
||||
if version != cpp_version:
|
||||
msg = "Python version {} does not match C++ version {}!".format(
|
||||
version, cpp_version
|
||||
)
|
||||
msg = f"Python version {version} does not match C++ version {cpp_version}!"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
version_hex = matches.get("HEX", "MISSING")
|
||||
expected_version_hex = build_expected_version_hex(matches)
|
||||
if version_hex != expected_version_hex:
|
||||
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
|
||||
version_hex,
|
||||
expected_version_hex,
|
||||
)
|
||||
exp_version_hex = build_expected_version_hex(matches)
|
||||
if version_hex != exp_version_hex:
|
||||
msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
def get_and_replace(filename, binary=False, **opts):
|
||||
with open(filename, "rb" if binary else "r") as f:
|
||||
contents = f.read()
|
||||
# Replacement has to be done on text in Python 3 (both work in Python 2)
|
||||
# TODO: use literals & overload (typing extensions or Python 3.8)
|
||||
def get_and_replace(
|
||||
filename: Path, binary: bool = False, **opts: str
|
||||
) -> Union[bytes, str]:
|
||||
if binary:
|
||||
contents = filename.read_bytes()
|
||||
return string.Template(contents.decode()).substitute(opts).encode()
|
||||
else:
|
||||
return string.Template(contents).substitute(opts)
|
||||
|
||||
return string.Template(filename.read_text()).substitute(opts)
|
||||
|
||||
|
||||
# Use our input files instead when making the SDist (and anything that depends
|
||||
# on it, like a wheel)
|
||||
class SDist(setuptools.command.sdist.sdist):
|
||||
def make_release_tree(self, base_dir, files):
|
||||
setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files)
|
||||
class SDist(setuptools.command.sdist.sdist): # type: ignore[misc]
|
||||
def make_release_tree(self, base_dir: str, files: List[str]) -> None:
|
||||
super().make_release_tree(base_dir, files)
|
||||
|
||||
for to, src in to_src:
|
||||
txt = get_and_replace(src, binary=True, version=version, extra_cmd="")
|
||||
|
||||
dest = os.path.join(base_dir, to)
|
||||
dest = Path(base_dir) / to
|
||||
|
||||
# This is normally linked, so unlink before writing!
|
||||
os.unlink(dest)
|
||||
with open(dest, "wb") as f:
|
||||
f.write(txt)
|
||||
|
||||
|
||||
# Backport from Python 3
|
||||
@contextlib.contextmanager
|
||||
def TemporaryDirectory(): # noqa: N802
|
||||
"Prepare a temporary directory, cleanup when done"
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
yield tmpdir
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
dest.unlink()
|
||||
dest.write_bytes(txt) # type: ignore[arg-type]
|
||||
|
||||
|
||||
# Remove the CMake install directory when done
|
||||
@contextlib.contextmanager
|
||||
def remove_output(*sources):
|
||||
def remove_output(*sources: str) -> Iterator[None]:
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
@ -156,9 +135,14 @@ with remove_output("pybind11/include", "pybind11/share"):
|
||||
if "DCMAKE_INSTALL_PREFIX" not in c
|
||||
]
|
||||
cmd += fcommand
|
||||
cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
|
||||
subprocess.check_call(cmd, **cmake_opts)
|
||||
subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)
|
||||
subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
|
||||
subprocess.run(
|
||||
["cmake", "--install", tmpdir],
|
||||
check=True,
|
||||
cwd=DIR,
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
|
||||
txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd)
|
||||
code = compile(txt, setup_py, "exec")
|
||||
|
@ -179,11 +179,6 @@ if(PYBIND11_TEST_FILTER)
|
||||
pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER})
|
||||
endif()
|
||||
|
||||
if(PYTHON_VERSION VERSION_LESS 3.5)
|
||||
pybind11_filter_tests(PYBIND11_TEST_FILES test_async.cpp MESSAGE
|
||||
"Skipping test_async on Python 2")
|
||||
endif()
|
||||
|
||||
# Skip tests for CUDA check:
|
||||
# /pybind11/tests/test_constants_and_functions.cpp(125):
|
||||
# error: incompatible exception specifications
|
||||
@ -220,6 +215,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_
|
||||
"pybind11_cross_module_tests")
|
||||
|
||||
# And add additional targets for other tests.
|
||||
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
|
||||
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
|
||||
|
||||
set(PYBIND11_EIGEN_REPO
|
||||
@ -356,7 +352,7 @@ endif()
|
||||
# Compile with compiler warnings turned on
|
||||
function(pybind11_enable_warnings target_name)
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name} PRIVATE /W4)
|
||||
target_compile_options(${target_name} PRIVATE /W4 /wd4189)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS)
|
||||
target_compile_options(
|
||||
${target_name}
|
||||
@ -388,17 +384,6 @@ function(pybind11_enable_warnings target_name)
|
||||
-diag-disable 11074,11076)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Needs to be re-added since the ordering requires these to be after the ones above
|
||||
if(CMAKE_CXX_STANDARD
|
||||
AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
|
||||
AND PYTHON_VERSION VERSION_LESS 3.0)
|
||||
if(CMAKE_CXX_STANDARD LESS 17)
|
||||
target_compile_options(${target_name} PUBLIC -Wno-deprecated-register)
|
||||
else()
|
||||
target_compile_options(${target_name} PUBLIC -Wno-register)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
set(test_targets pybind11_tests)
|
||||
|
@ -1,8 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""pytest configuration
|
||||
|
||||
Extends output capture as needed by pybind11: ignore constructors, optional unordered lines.
|
||||
Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences.
|
||||
Adds docstring and exceptions message sanitizers.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
@ -13,19 +12,14 @@ import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
|
||||
# Early diagnostic for failed imports
|
||||
import pybind11_tests # noqa: F401
|
||||
import pybind11_tests
|
||||
|
||||
_unicode_marker = re.compile(r"u(\'[^\']*\')")
|
||||
_long_marker = re.compile(r"([0-9])L")
|
||||
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
|
||||
|
||||
# Avoid collecting Python3 only files
|
||||
collect_ignore = []
|
||||
if env.PY2:
|
||||
collect_ignore.append("test_async.py")
|
||||
|
||||
|
||||
def _strip_and_dedent(s):
|
||||
@ -45,7 +39,7 @@ def _make_explanation(a, b):
|
||||
]
|
||||
|
||||
|
||||
class Output(object):
|
||||
class Output:
|
||||
"""Basic output post-processing and comparison"""
|
||||
|
||||
def __init__(self, string):
|
||||
@ -83,7 +77,7 @@ class Unordered(Output):
|
||||
return False
|
||||
|
||||
|
||||
class Capture(object):
|
||||
class Capture:
|
||||
def __init__(self, capfd):
|
||||
self.capfd = capfd
|
||||
self.out = ""
|
||||
@ -126,7 +120,7 @@ def capture(capsys):
|
||||
return Capture(capsys)
|
||||
|
||||
|
||||
class SanitizedString(object):
|
||||
class SanitizedString:
|
||||
def __init__(self, sanitizer):
|
||||
self.sanitizer = sanitizer
|
||||
self.string = ""
|
||||
@ -149,9 +143,7 @@ class SanitizedString(object):
|
||||
def _sanitize_general(s):
|
||||
s = s.strip()
|
||||
s = s.replace("pybind11_tests.", "m.")
|
||||
s = s.replace("unicode", "str")
|
||||
s = _long_marker.sub(r"\1", s)
|
||||
s = _unicode_marker.sub(r"\1", s)
|
||||
return s
|
||||
|
||||
|
||||
@ -206,3 +198,16 @@ def gc_collect():
|
||||
def pytest_configure():
|
||||
pytest.suppress = suppress
|
||||
pytest.gc_collect = gc_collect
|
||||
|
||||
|
||||
def pytest_report_header(config):
|
||||
del config # Unused.
|
||||
assert (
|
||||
pybind11_tests.compiler_info is not None
|
||||
), "Please update pybind11_tests.cpp if this assert fails."
|
||||
return (
|
||||
"C++ Info:"
|
||||
f" {pybind11_tests.compiler_info}"
|
||||
f" {pybind11_tests.cpp_std}"
|
||||
f" {pybind11_tests.PYBIND11_INTERNALS_ID}"
|
||||
)
|
||||
|
@ -25,40 +25,21 @@ void gil_acquire() { py::gil_scoped_acquire gil; }
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
struct PyModuleDef moduledef
|
||||
= {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
|
||||
#else
|
||||
PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
|
||||
#endif
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *
|
||||
PyInit_cross_module_gil_utils()
|
||||
#else
|
||||
void
|
||||
initcross_module_gil_utils()
|
||||
#endif
|
||||
{
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
|
||||
|
||||
PyObject *m =
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyModule_Create(&moduledef);
|
||||
#else
|
||||
Py_InitModule(kModuleName, module_methods);
|
||||
#endif
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
|
||||
if (m != NULL) {
|
||||
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)));
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
51
tests/cross_module_interleaved_error_already_set.cpp
Normal file
51
tests/cross_module_interleaved_error_already_set.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) 2022 Google LLC
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE,
|
||||
// so that the first call of cross_module_error_already_set() triggers the first call of
|
||||
// pybind11::detail::get_internals().
|
||||
|
||||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
void interleaved_error_already_set() {
|
||||
PyErr_SetString(PyExc_RuntimeError, "1st error.");
|
||||
try {
|
||||
throw py::error_already_set();
|
||||
} catch (const py::error_already_set &) {
|
||||
// The 2nd error could be conditional in a real application.
|
||||
PyErr_SetString(PyExc_RuntimeError, "2nd error.");
|
||||
} // Here the 1st error is destroyed before the 2nd error is fetched.
|
||||
// The error_already_set dtor triggers a pybind11::detail::get_internals()
|
||||
// call via pybind11::gil_scoped_acquire.
|
||||
if (PyErr_Occurred()) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_interleaved_error_already_set";
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() {
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
if (m != nullptr) {
|
||||
static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
|
||||
"Function pointer must have the same size as void *");
|
||||
PyModule_AddObject(
|
||||
m,
|
||||
"funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void *>(&interleaved_error_already_set)));
|
||||
}
|
||||
return m;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
import sys
|
||||
|
||||
@ -11,10 +10,6 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
|
||||
CPYTHON = platform.python_implementation() == "CPython"
|
||||
PYPY = platform.python_implementation() == "PyPy"
|
||||
|
||||
PY2 = sys.version_info.major == 2
|
||||
|
||||
PY = sys.version_info
|
||||
|
||||
|
||||
def deprecated_call():
|
||||
"""
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import contextlib
|
||||
import os
|
||||
import string
|
||||
@ -64,11 +63,9 @@ py_files = {
|
||||
"__init__.py",
|
||||
"__main__.py",
|
||||
"_version.py",
|
||||
"_version.pyi",
|
||||
"commands.py",
|
||||
"py.typed",
|
||||
"setup_helpers.py",
|
||||
"setup_helpers.pyi",
|
||||
}
|
||||
|
||||
headers = main_headers | detail_headers | stl_headers
|
||||
@ -111,19 +108,19 @@ def test_build_sdist(monkeypatch, tmpdir):
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"setup.py",
|
||||
"sdist",
|
||||
"--formats=tar",
|
||||
"--dist-dir",
|
||||
"-m",
|
||||
"build",
|
||||
"--sdist",
|
||||
"--outdir",
|
||||
str(tmpdir),
|
||||
]
|
||||
)
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist,) = tmpdir.visit("*.tar")
|
||||
(sdist,) = tmpdir.visit("*.tar.gz")
|
||||
|
||||
with tarfile.open(str(sdist)) as tar:
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
version = start[9:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
@ -148,9 +145,9 @@ def test_build_sdist(monkeypatch, tmpdir):
|
||||
contents = f.read().decode("utf8")
|
||||
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
|
||||
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= sdist_files
|
||||
files |= {"pybind11{}".format(n) for n in local_sdist_files}
|
||||
files |= {f"pybind11{n}" for n in local_sdist_files}
|
||||
files.add("pybind11.egg-info/entry_points.txt")
|
||||
files.add("pybind11.egg-info/requires.txt")
|
||||
assert simpler == files
|
||||
@ -172,23 +169,23 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
||||
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
|
||||
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"setup.py",
|
||||
"sdist",
|
||||
"--formats=tar",
|
||||
"--dist-dir",
|
||||
"-m",
|
||||
"build",
|
||||
"--sdist",
|
||||
"--outdir",
|
||||
str(tmpdir),
|
||||
]
|
||||
)
|
||||
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist,) = tmpdir.visit("*.tar")
|
||||
(sdist,) = tmpdir.visit("*.tar.gz")
|
||||
|
||||
with tarfile.open(str(sdist)) as tar:
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
version = start[16:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
@ -203,9 +200,9 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
||||
) as f:
|
||||
pyproject_toml = f.read()
|
||||
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= sdist_files
|
||||
files |= {"pybind11_global{}".format(n) for n in local_sdist_files}
|
||||
files |= {f"pybind11_global{n}" for n in local_sdist_files}
|
||||
assert simpler == files
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
|
||||
@ -230,7 +227,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
@ -244,9 +241,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
|
||||
names = z.namelist()
|
||||
|
||||
trimmed = {n for n in names if "dist-info" not in n}
|
||||
trimmed |= {
|
||||
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
|
||||
}
|
||||
trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n}
|
||||
assert files == trimmed
|
||||
|
||||
|
||||
@ -260,8 +255,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
||||
files = {"data/data/{}".format(n) for n in src_files}
|
||||
files |= {"data/headers/{}".format(n[8:]) for n in headers}
|
||||
files = {f"data/data/{n}" for n in src_files}
|
||||
files |= {f"data/headers/{n[8:]}" for n in headers}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
@ -19,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
||||
|
||||
(tmpdir / "setup.py").write_text(
|
||||
dedent(
|
||||
u"""\
|
||||
f"""\
|
||||
import sys
|
||||
sys.path.append({MAIN_DIR!r})
|
||||
|
||||
@ -52,13 +51,13 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
||||
ext_modules=ext_modules,
|
||||
)
|
||||
"""
|
||||
).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel),
|
||||
),
|
||||
encoding="ascii",
|
||||
)
|
||||
|
||||
(tmpdir / "main.cpp").write_text(
|
||||
dedent(
|
||||
u"""\
|
||||
"""\
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
int f(int x) {
|
||||
@ -96,7 +95,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
||||
|
||||
(tmpdir / "test.py").write_text(
|
||||
dedent(
|
||||
u"""\
|
||||
"""\
|
||||
import simple_setup
|
||||
assert simple_setup.f(3) == 9
|
||||
"""
|
||||
@ -121,10 +120,11 @@ def test_intree_extensions(monkeypatch, tmpdir):
|
||||
subdir.ensure_dir()
|
||||
src = subdir / "ext.cpp"
|
||||
src.ensure()
|
||||
(ext,) = intree_extensions([src.relto(tmpdir)])
|
||||
relpath = src.relto(tmpdir)
|
||||
(ext,) = intree_extensions([relpath])
|
||||
assert ext.name == "ext"
|
||||
subdir.ensure("__init__.py")
|
||||
(ext,) = intree_extensions([src.relto(tmpdir)])
|
||||
(ext,) = intree_extensions([relpath])
|
||||
assert ext.name == "dir.ext"
|
||||
|
||||
|
||||
|
@ -62,15 +62,40 @@ void bind_ConstructorStats(py::module_ &m) {
|
||||
});
|
||||
}
|
||||
|
||||
const char *cpp_std() {
|
||||
return
|
||||
#if defined(PYBIND11_CPP20)
|
||||
"C++20";
|
||||
#elif defined(PYBIND11_CPP17)
|
||||
"C++17";
|
||||
#elif defined(PYBIND11_CPP14)
|
||||
"C++14";
|
||||
#else
|
||||
"C++11";
|
||||
#endif
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(pybind11_tests, m) {
|
||||
m.doc() = "pybind11 test module";
|
||||
|
||||
// Intentionally kept minimal to not create a maintenance chore
|
||||
// ("just enough" to be conclusive).
|
||||
#if defined(_MSC_FULL_VER)
|
||||
m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER);
|
||||
#elif defined(__VERSION__)
|
||||
m.attr("compiler_info") = __VERSION__;
|
||||
#else
|
||||
m.attr("compiler_info") = py::none();
|
||||
#endif
|
||||
m.attr("cpp_std") = cpp_std();
|
||||
m.attr("PYBIND11_INTERNALS_ID") = PYBIND11_INTERNALS_ID;
|
||||
|
||||
bind_ConstructorStats(m);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
m.attr("debug_enabled") = true;
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
m.attr("detailed_error_messages_enabled") = true;
|
||||
#else
|
||||
m.attr("debug_enabled") = false;
|
||||
m.attr("detailed_error_messages_enabled") = false;
|
||||
#endif
|
||||
|
||||
py::class_<UserType>(m, "UserType", "A `py::class_` type for testing")
|
||||
|
@ -3,12 +3,6 @@
|
||||
#include <pybind11/eval.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
// We get some really long type names here which causes MSVC 2015 to emit warnings
|
||||
# pragma warning( \
|
||||
disable : 4503) // NOLINT: warning C4503: decorated name length exceeded, name was truncated
|
||||
#endif
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11::literals;
|
||||
|
||||
|
@ -1,12 +1,15 @@
|
||||
[pytest]
|
||||
minversion = 3.1
|
||||
minversion = 3.10
|
||||
norecursedirs = test_* extra_*
|
||||
xfail_strict = True
|
||||
addopts =
|
||||
# show summary of skipped tests
|
||||
-rs
|
||||
# show summary of tests
|
||||
-ra
|
||||
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
|
||||
--capture=sys
|
||||
# Show local info when a failure occurs
|
||||
--showlocals
|
||||
log_cli_level = info
|
||||
filterwarnings =
|
||||
# make warnings into errors but ignore certain third-party extension issues
|
||||
error
|
||||
|
@ -1,12 +1,9 @@
|
||||
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy"
|
||||
numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
|
||||
numpy==1.20.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||
build==0.8.0
|
||||
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
|
||||
numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11"
|
||||
py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
|
||||
pytest==4.6.9; python_version<"3.5"
|
||||
pytest==6.1.2; python_version=="3.5"
|
||||
pytest==6.2.4; python_version>="3.6"
|
||||
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
|
||||
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
|
||||
pytest==7.0.0
|
||||
pytest-timeout
|
||||
scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6"
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"
|
||||
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"
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
asyncio = pytest.importorskip("asyncio")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import ctypes
|
||||
import io
|
||||
import struct
|
||||
@ -93,16 +92,16 @@ def test_pointer_to_member_fn():
|
||||
def test_readonly_buffer():
|
||||
buf = m.BufferReadOnly(0x64)
|
||||
view = memoryview(buf)
|
||||
assert view[0] == b"d" if env.PY2 else 0x64
|
||||
assert view[0] == 0x64
|
||||
assert view.readonly
|
||||
with pytest.raises(TypeError):
|
||||
view[0] = b"\0" if env.PY2 else 0
|
||||
view[0] = 0
|
||||
|
||||
|
||||
def test_selective_readonly_buffer():
|
||||
buf = m.BufferReadOnlySelect()
|
||||
|
||||
memoryview(buf)[0] = b"d" if env.PY2 else 0x64
|
||||
memoryview(buf)[0] = 0x64
|
||||
assert buf.value == 0x64
|
||||
|
||||
io.BytesIO(b"A").readinto(buf)
|
||||
@ -110,7 +109,7 @@ def test_selective_readonly_buffer():
|
||||
|
||||
buf.readonly = True
|
||||
with pytest.raises(TypeError):
|
||||
memoryview(buf)[0] = b"\0" if env.PY2 else 0
|
||||
memoryview(buf)[0] = 0
|
||||
with pytest.raises(TypeError):
|
||||
io.BytesIO(b"1").readinto(buf)
|
||||
|
||||
@ -145,9 +144,6 @@ def test_ctypes_array_2d():
|
||||
assert not info.readonly
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
"env.PYPY and env.PY2", reason="PyPy2 bytes buffer not reported as readonly"
|
||||
)
|
||||
def test_ctypes_from_buffer():
|
||||
test_pystr = b"0123456789"
|
||||
for pyarray in (test_pystr, bytearray(test_pystr)):
|
||||
|
@ -110,8 +110,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
"def");
|
||||
});
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger
|
||||
// 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)) {
|
||||
@ -119,7 +118,6 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
|
||||
});
|
||||
}
|
||||
#endif
|
||||
m.def("u8_Z", []() -> char { return 'Z'; });
|
||||
m.def("u8_eacute", []() -> char { return '\xe9'; });
|
||||
m.def("u16_ibang", [=]() -> char16_t { return ib16; });
|
||||
@ -198,12 +196,10 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
[]() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
|
||||
m.def("string_view_from_bytes",
|
||||
[](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
|
||||
# if PY_MAJOR_VERSION >= 3
|
||||
m.def("string_view_memoryview", []() {
|
||||
static constexpr auto val = "Have some \360\237\216\202"sv;
|
||||
return py::memoryview::from_memory(val);
|
||||
});
|
||||
# endif
|
||||
|
||||
# ifdef PYBIND11_HAS_U8STRING
|
||||
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
|
||||
@ -271,7 +267,8 @@ 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; });
|
||||
m.def(
|
||||
"int_string_pair", []() { return &int_string_pair; }, py::return_value_policy::reference);
|
||||
|
||||
// test_builtins_cast_return_none
|
||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
@ -12,12 +13,12 @@ def test_simple_string():
|
||||
|
||||
def test_unicode_conversion():
|
||||
"""Tests unicode conversion and error reporting."""
|
||||
assert m.good_utf8_string() == u"Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf16_string() == u"b‽🎂𝐀z"
|
||||
assert m.good_utf32_string() == u"a𝐀🎂‽z"
|
||||
assert m.good_wchar_string() == u"a⸘𝐀z"
|
||||
assert m.good_utf8_string() == "Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf16_string() == "b‽🎂𝐀z"
|
||||
assert m.good_utf32_string() == "a𝐀🎂‽z"
|
||||
assert m.good_wchar_string() == "a⸘𝐀z"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.good_utf8_u8string() == u"Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf8_u8string() == "Say utf8‽ 🎂 𝐀"
|
||||
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf8_string()
|
||||
@ -25,7 +26,7 @@ def test_unicode_conversion():
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf16_string()
|
||||
|
||||
# These are provided only if they actually fail (they don't when 32-bit and under Python 2.7)
|
||||
# These are provided only if they actually fail (they don't when 32-bit)
|
||||
if hasattr(m, "bad_utf32_string"):
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf32_string()
|
||||
@ -37,10 +38,10 @@ def test_unicode_conversion():
|
||||
m.bad_utf8_u8string()
|
||||
|
||||
assert m.u8_Z() == "Z"
|
||||
assert m.u8_eacute() == u"é"
|
||||
assert m.u16_ibang() == u"‽"
|
||||
assert m.u32_mathbfA() == u"𝐀"
|
||||
assert m.wchar_heart() == u"♥"
|
||||
assert m.u8_eacute() == "é"
|
||||
assert m.u16_ibang() == "‽"
|
||||
assert m.u32_mathbfA() == "𝐀"
|
||||
assert m.wchar_heart() == "♥"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.u8_char8_Z() == "Z"
|
||||
|
||||
@ -49,72 +50,72 @@ def test_single_char_arguments():
|
||||
"""Tests failures for passing invalid inputs to char-accepting functions"""
|
||||
|
||||
def toobig_message(r):
|
||||
return "Character code point not in range({:#x})".format(r)
|
||||
return f"Character code point not in range({r:#x})"
|
||||
|
||||
toolong_message = "Expected a character, but multi-character string found"
|
||||
|
||||
assert m.ord_char(u"a") == 0x61 # simple ASCII
|
||||
assert m.ord_char_lv(u"b") == 0x62
|
||||
assert m.ord_char("a") == 0x61 # simple ASCII
|
||||
assert m.ord_char_lv("b") == 0x62
|
||||
assert (
|
||||
m.ord_char(u"é") == 0xE9
|
||||
m.ord_char("é") == 0xE9
|
||||
) # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char(u"Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert m.ord_char("Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert str(excinfo.value) == toobig_message(0x100)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char(u"ab")
|
||||
assert m.ord_char("ab")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_char16(u"a") == 0x61
|
||||
assert m.ord_char16(u"é") == 0xE9
|
||||
assert m.ord_char16_lv(u"ê") == 0xEA
|
||||
assert m.ord_char16(u"Ā") == 0x100
|
||||
assert m.ord_char16(u"‽") == 0x203D
|
||||
assert m.ord_char16(u"♥") == 0x2665
|
||||
assert m.ord_char16_lv(u"♡") == 0x2661
|
||||
assert m.ord_char16("a") == 0x61
|
||||
assert m.ord_char16("é") == 0xE9
|
||||
assert m.ord_char16_lv("ê") == 0xEA
|
||||
assert m.ord_char16("Ā") == 0x100
|
||||
assert m.ord_char16("‽") == 0x203D
|
||||
assert m.ord_char16("♥") == 0x2665
|
||||
assert m.ord_char16_lv("♡") == 0x2661
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16(u"🎂") == 0x1F382 # requires surrogate pair
|
||||
assert m.ord_char16("🎂") == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16(u"aa")
|
||||
assert m.ord_char16("aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_char32(u"a") == 0x61
|
||||
assert m.ord_char32(u"é") == 0xE9
|
||||
assert m.ord_char32(u"Ā") == 0x100
|
||||
assert m.ord_char32(u"‽") == 0x203D
|
||||
assert m.ord_char32(u"♥") == 0x2665
|
||||
assert m.ord_char32(u"🎂") == 0x1F382
|
||||
assert m.ord_char32("a") == 0x61
|
||||
assert m.ord_char32("é") == 0xE9
|
||||
assert m.ord_char32("Ā") == 0x100
|
||||
assert m.ord_char32("‽") == 0x203D
|
||||
assert m.ord_char32("♥") == 0x2665
|
||||
assert m.ord_char32("🎂") == 0x1F382
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char32(u"aa")
|
||||
assert m.ord_char32("aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_wchar(u"a") == 0x61
|
||||
assert m.ord_wchar(u"é") == 0xE9
|
||||
assert m.ord_wchar(u"Ā") == 0x100
|
||||
assert m.ord_wchar(u"‽") == 0x203D
|
||||
assert m.ord_wchar(u"♥") == 0x2665
|
||||
assert m.ord_wchar("a") == 0x61
|
||||
assert m.ord_wchar("é") == 0xE9
|
||||
assert m.ord_wchar("Ā") == 0x100
|
||||
assert m.ord_wchar("‽") == 0x203D
|
||||
assert m.ord_wchar("♥") == 0x2665
|
||||
if m.wchar_size == 2:
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_wchar(u"🎂") == 0x1F382 # requires surrogate pair
|
||||
assert m.ord_wchar("🎂") == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
else:
|
||||
assert m.ord_wchar(u"🎂") == 0x1F382
|
||||
assert m.ord_wchar("🎂") == 0x1F382
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_wchar(u"aa")
|
||||
assert m.ord_wchar("aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.ord_char8(u"a") == 0x61 # simple ASCII
|
||||
assert m.ord_char8_lv(u"b") == 0x62
|
||||
assert m.ord_char8("a") == 0x61 # simple ASCII
|
||||
assert m.ord_char8_lv("b") == 0x62
|
||||
assert (
|
||||
m.ord_char8(u"é") == 0xE9
|
||||
m.ord_char8("é") == 0xE9
|
||||
) # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char8(u"Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert m.ord_char8("Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert str(excinfo.value) == toobig_message(0x100)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char8(u"ab")
|
||||
assert m.ord_char8("ab")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
|
||||
@ -123,18 +124,22 @@ def test_bytes_to_string():
|
||||
one-way: the only way to return bytes to Python is via the pybind11::bytes class."""
|
||||
# Issue #816
|
||||
|
||||
def to_bytes(s):
|
||||
b = s if env.PY2 else s.encode("utf8")
|
||||
assert isinstance(b, bytes)
|
||||
return b
|
||||
|
||||
assert m.strlen(to_bytes("hi")) == 2
|
||||
assert m.string_length(to_bytes("world")) == 5
|
||||
assert m.string_length(to_bytes("a\x00b")) == 3
|
||||
assert m.strlen(to_bytes("a\x00b")) == 1 # C-string limitation
|
||||
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
|
||||
|
||||
# passing in a utf8 encoded string should work
|
||||
assert m.string_length(u"💩".encode("utf8")) == 4
|
||||
assert m.string_length("💩".encode()) == 4
|
||||
|
||||
|
||||
def test_bytearray_to_string():
|
||||
"""Tests the ability to pass bytearray to C++ string-accepting functions"""
|
||||
assert m.string_length(bytearray(b"Hi")) == 2
|
||||
assert m.strlen(bytearray(b"bytearray")) == 9
|
||||
assert m.string_length(bytearray()) == 0
|
||||
assert m.string_length(bytearray("🦜", "utf-8", "strict")) == 4
|
||||
assert m.string_length(bytearray(b"\x80")) == 1
|
||||
|
||||
|
||||
@pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no <string_view>")
|
||||
@ -142,26 +147,26 @@ def test_string_view(capture):
|
||||
"""Tests support for C++17 string_view arguments and return values"""
|
||||
assert m.string_view_chars("Hi") == [72, 105]
|
||||
assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82]
|
||||
assert m.string_view16_chars(u"Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82]
|
||||
assert m.string_view32_chars(u"Hi 🎂") == [72, 105, 32, 127874]
|
||||
assert m.string_view16_chars("Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82]
|
||||
assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874]
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_chars("Hi") == [72, 105]
|
||||
assert m.string_view8_chars(u"Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82]
|
||||
assert m.string_view8_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82]
|
||||
|
||||
assert m.string_view_return() == u"utf8 secret 🎂"
|
||||
assert m.string_view16_return() == u"utf16 secret 🎂"
|
||||
assert m.string_view32_return() == u"utf32 secret 🎂"
|
||||
assert m.string_view_return() == "utf8 secret 🎂"
|
||||
assert m.string_view16_return() == "utf16 secret 🎂"
|
||||
assert m.string_view32_return() == "utf32 secret 🎂"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_return() == u"utf8 secret 🎂"
|
||||
assert m.string_view8_return() == "utf8 secret 🎂"
|
||||
|
||||
with capture:
|
||||
m.string_view_print("Hi")
|
||||
m.string_view_print("utf8 🎂")
|
||||
m.string_view16_print(u"utf16 🎂")
|
||||
m.string_view32_print(u"utf32 🎂")
|
||||
m.string_view16_print("utf16 🎂")
|
||||
m.string_view32_print("utf32 🎂")
|
||||
assert (
|
||||
capture
|
||||
== u"""
|
||||
== """
|
||||
Hi 2
|
||||
utf8 🎂 9
|
||||
utf16 🎂 8
|
||||
@ -171,10 +176,10 @@ def test_string_view(capture):
|
||||
if hasattr(m, "has_u8string"):
|
||||
with capture:
|
||||
m.string_view8_print("Hi")
|
||||
m.string_view8_print(u"utf8 🎂")
|
||||
m.string_view8_print("utf8 🎂")
|
||||
assert (
|
||||
capture
|
||||
== u"""
|
||||
== """
|
||||
Hi 2
|
||||
utf8 🎂 9
|
||||
"""
|
||||
@ -183,11 +188,11 @@ def test_string_view(capture):
|
||||
with capture:
|
||||
m.string_view_print("Hi, ascii")
|
||||
m.string_view_print("Hi, utf8 🎂")
|
||||
m.string_view16_print(u"Hi, utf16 🎂")
|
||||
m.string_view32_print(u"Hi, utf32 🎂")
|
||||
m.string_view16_print("Hi, utf16 🎂")
|
||||
m.string_view32_print("Hi, utf32 🎂")
|
||||
assert (
|
||||
capture
|
||||
== u"""
|
||||
== """
|
||||
Hi, ascii 9
|
||||
Hi, utf8 🎂 13
|
||||
Hi, utf16 🎂 12
|
||||
@ -197,22 +202,21 @@ def test_string_view(capture):
|
||||
if hasattr(m, "has_u8string"):
|
||||
with capture:
|
||||
m.string_view8_print("Hi, ascii")
|
||||
m.string_view8_print(u"Hi, utf8 🎂")
|
||||
m.string_view8_print("Hi, utf8 🎂")
|
||||
assert (
|
||||
capture
|
||||
== u"""
|
||||
== """
|
||||
Hi, ascii 9
|
||||
Hi, utf8 🎂 13
|
||||
"""
|
||||
)
|
||||
|
||||
assert m.string_view_bytes() == b"abc \x80\x80 def"
|
||||
assert m.string_view_str() == u"abc ‽ def"
|
||||
assert m.string_view_from_bytes(u"abc ‽ def".encode("utf-8")) == u"abc ‽ def"
|
||||
assert m.string_view_str() == "abc ‽ def"
|
||||
assert m.string_view_from_bytes("abc ‽ def".encode()) == "abc ‽ def"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_str() == u"abc ‽ def"
|
||||
if not env.PY2:
|
||||
assert m.string_view_memoryview() == "Have some 🎂".encode()
|
||||
assert m.string_view8_str() == "abc ‽ def"
|
||||
assert m.string_view_memoryview() == "Have some 🎂".encode()
|
||||
|
||||
assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success"
|
||||
assert m.str_from_type_with_both_operator_string_and_string_view() == "success"
|
||||
@ -224,20 +228,8 @@ def test_integer_casting():
|
||||
assert m.i64_str(-1) == "-1"
|
||||
assert m.i32_str(2000000000) == "2000000000"
|
||||
assert m.u32_str(2000000000) == "2000000000"
|
||||
if env.PY2:
|
||||
assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long'
|
||||
assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long'
|
||||
assert (
|
||||
m.i64_str(long(-999999999999)) # noqa: F821 undefined name 'long'
|
||||
== "-999999999999"
|
||||
)
|
||||
assert (
|
||||
m.u64_str(long(999999999999)) # noqa: F821 undefined name 'long'
|
||||
== "999999999999"
|
||||
)
|
||||
else:
|
||||
assert m.i64_str(-999999999999) == "-999999999999"
|
||||
assert m.u64_str(999999999999) == "999999999999"
|
||||
assert m.i64_str(-999999999999) == "-999999999999"
|
||||
assert m.u64_str(999999999999) == "999999999999"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u32_str(-1)
|
||||
@ -252,46 +244,38 @@ def test_integer_casting():
|
||||
m.i32_str(3000000000)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
if env.PY2:
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u32_str(long(-1)) # noqa: F821 undefined name 'long'
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u64_str(long(-1)) # noqa: F821 undefined name 'long'
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_int_convert():
|
||||
class Int(object):
|
||||
class Int:
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
class NotInt(object):
|
||||
class NotInt:
|
||||
pass
|
||||
|
||||
class Float(object):
|
||||
class Float:
|
||||
def __float__(self):
|
||||
return 41.99999
|
||||
|
||||
class Index(object):
|
||||
class Index:
|
||||
def __index__(self):
|
||||
return 42
|
||||
|
||||
class IntAndIndex(object):
|
||||
class IntAndIndex:
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
def __index__(self):
|
||||
return 0
|
||||
|
||||
class RaisingTypeErrorOnIndex(object):
|
||||
class RaisingTypeErrorOnIndex:
|
||||
def __index__(self):
|
||||
raise TypeError
|
||||
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
class RaisingValueErrorOnIndex(object):
|
||||
class RaisingValueErrorOnIndex:
|
||||
def __index__(self):
|
||||
raise ValueError
|
||||
|
||||
@ -311,7 +295,7 @@ def test_int_convert():
|
||||
cant_convert(3.14159)
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
|
||||
if (3, 8) <= env.PY < (3, 10) and env.CPYTHON:
|
||||
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
|
||||
with env.deprecated_call():
|
||||
assert convert(Int()) == 42
|
||||
else:
|
||||
@ -348,7 +332,7 @@ def test_numpy_int_convert():
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
|
||||
# https://github.com/pybind/pybind11/issues/3408
|
||||
if (3, 8) <= env.PY < (3, 10) and env.CPYTHON:
|
||||
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
|
||||
with env.deprecated_call():
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
else:
|
||||
@ -475,7 +459,7 @@ def test_bool_caster():
|
||||
require_implicit(None)
|
||||
assert convert(None) is False
|
||||
|
||||
class A(object):
|
||||
class A:
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
@ -485,7 +469,7 @@ def test_bool_caster():
|
||||
def __bool__(self):
|
||||
return self.x
|
||||
|
||||
class B(object):
|
||||
class B:
|
||||
pass
|
||||
|
||||
# Arbitrary objects are not accepted
|
||||
@ -515,17 +499,9 @@ def test_numpy_bool():
|
||||
|
||||
|
||||
def test_int_long():
|
||||
"""In Python 2, a C++ int should return a Python int rather than long
|
||||
if possible: longs are not always accepted where ints are used (such
|
||||
as the argument to sys.exit()). A C++ long long is always a Python
|
||||
long."""
|
||||
|
||||
import sys
|
||||
|
||||
must_be_long = type(getattr(sys, "maxint", 1) + 1)
|
||||
assert isinstance(m.int_cast(), int)
|
||||
assert isinstance(m.long_cast(), int)
|
||||
assert isinstance(m.longlong_cast(), must_be_long)
|
||||
assert isinstance(m.longlong_cast(), int)
|
||||
|
||||
|
||||
def test_void_caster_2():
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
@ -18,7 +17,7 @@ def test_callbacks():
|
||||
return "func2", a, b, c, d
|
||||
|
||||
def func3(a):
|
||||
return "func3({})".format(a)
|
||||
return f"func3({a})"
|
||||
|
||||
assert m.test_callback1(func1) == "func1"
|
||||
assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
|
||||
@ -189,14 +188,8 @@ def test_callback_num_times():
|
||||
if not rep:
|
||||
print()
|
||||
print(
|
||||
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format(
|
||||
num_millions, td, rate
|
||||
)
|
||||
f"callback_num_times: {num_millions:d} million / {td:.3f} seconds = {rate:.3f} million / second"
|
||||
)
|
||||
if len(rates) > 1:
|
||||
print("Min Mean Max")
|
||||
print(
|
||||
"{:6.3f} {:6.3f} {:6.3f}".format(
|
||||
min(rates), sum(rates) / len(rates), max(rates)
|
||||
)
|
||||
)
|
||||
print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
@ -101,7 +100,7 @@ SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif(
|
||||
)
|
||||
def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
|
||||
if tz is not None:
|
||||
monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz))
|
||||
monkeypatch.setenv("TZ", f"/usr/share/zoneinfo/{tz}")
|
||||
|
||||
# Roundtrip the time
|
||||
datetime2 = m.test_chrono2(time1)
|
||||
|
@ -354,13 +354,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
using ProtectedA::foo;
|
||||
};
|
||||
|
||||
py::class_<ProtectedA>(m, "ProtectedA")
|
||||
.def(py::init<>())
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
||||
.def("foo", &PublicistA::foo);
|
||||
#else
|
||||
.def("foo", static_cast<int (ProtectedA::*)() const>(&PublicistA::foo));
|
||||
#endif
|
||||
py::class_<ProtectedA>(m, "ProtectedA").def(py::init<>()).def("foo", &PublicistA::foo);
|
||||
|
||||
class ProtectedB {
|
||||
public:
|
||||
@ -391,11 +385,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
|
||||
py::class_<ProtectedB, TrampolineB>(m, "ProtectedB")
|
||||
.def(py::init<>())
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
||||
.def("foo", &PublicistB::foo);
|
||||
#else
|
||||
.def("foo", static_cast<int (ProtectedB::*)() const>(&PublicistB::foo));
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct BraceInitialization {
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
@ -7,7 +6,6 @@ from pybind11_tests import class_ as m
|
||||
|
||||
|
||||
def test_repr():
|
||||
# In Python 3.3+, repr() accesses __qualname__
|
||||
assert "pybind11_type" in repr(type(UserType))
|
||||
assert "UserType" in repr(UserType)
|
||||
|
||||
@ -103,8 +101,8 @@ def test_docstrings(doc):
|
||||
|
||||
|
||||
def test_qualname(doc):
|
||||
"""Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we
|
||||
backport the attribute) and that generated docstrings properly use it and the module name"""
|
||||
"""Tests that a properly qualified name is set in __qualname__ and that
|
||||
generated docstrings properly use it and the module name"""
|
||||
assert m.NestBase.__qualname__ == "NestBase"
|
||||
assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
|
||||
|
||||
@ -130,13 +128,13 @@ def test_qualname(doc):
|
||||
doc(m.NestBase.Nested.fn)
|
||||
== """
|
||||
fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
doc(m.NestBase.Nested.fa)
|
||||
== """
|
||||
fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
assert m.NestBase.__module__ == "pybind11_tests.class_"
|
||||
assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"
|
||||
|
@ -1,10 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import test_cmake_build
|
||||
|
||||
if str is not bytes: # If not Python2
|
||||
assert isinstance(__file__, str) # Test this is properly set
|
||||
assert isinstance(__file__, str) # Test this is properly set
|
||||
|
||||
assert test_cmake_build.add(1, 2) == 3
|
||||
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
|
||||
print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3")
|
||||
|
@ -4,65 +4,50 @@
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
|
||||
// MSVC 2015 fails in bizarre ways.
|
||||
# define PYBIND11_SKIP_TEST_CONST_NAME
|
||||
|
||||
#else // Only test with MSVC 2017 or newer.
|
||||
|
||||
// IUT = Implementation Under Test
|
||||
# define CONST_NAME_TESTS(TEST_FUNC, IUT) \
|
||||
std::string TEST_FUNC(int selector) { \
|
||||
switch (selector) { \
|
||||
case 0: \
|
||||
return IUT("").text; \
|
||||
case 1: \
|
||||
return IUT("A").text; \
|
||||
case 2: \
|
||||
return IUT("Bd").text; \
|
||||
case 3: \
|
||||
return IUT("Cef").text; \
|
||||
case 4: \
|
||||
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 5: \
|
||||
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 6: \
|
||||
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 7: \
|
||||
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 8: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<true>(IUT("D1"), IUT("D2")).text; \
|
||||
case 9: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<false>(IUT("E1"), IUT("E2")).text; \
|
||||
case 10: \
|
||||
return IUT("KeepAtEnd").text; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
throw std::runtime_error("Invalid selector value."); \
|
||||
}
|
||||
#define CONST_NAME_TESTS(TEST_FUNC, IUT) \
|
||||
std::string TEST_FUNC(int selector) { \
|
||||
switch (selector) { \
|
||||
case 0: \
|
||||
return IUT("").text; \
|
||||
case 1: \
|
||||
return IUT("A").text; \
|
||||
case 2: \
|
||||
return IUT("Bd").text; \
|
||||
case 3: \
|
||||
return IUT("Cef").text; \
|
||||
case 4: \
|
||||
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 5: \
|
||||
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 6: \
|
||||
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 7: \
|
||||
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 8: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<true>(IUT("D1"), IUT("D2")).text; \
|
||||
case 9: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<false>(IUT("E1"), IUT("E2")).text; \
|
||||
case 10: \
|
||||
return IUT("KeepAtEnd").text; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
throw std::runtime_error("Invalid selector value."); \
|
||||
}
|
||||
|
||||
CONST_NAME_TESTS(const_name_tests, py::detail::const_name)
|
||||
|
||||
# ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
CONST_NAME_TESTS(underscore_tests, py::detail::_)
|
||||
# endif
|
||||
|
||||
#endif // MSVC >= 2017
|
||||
|
||||
TEST_SUBMODULE(const_name, m) {
|
||||
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
|
||||
m.attr("const_name_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
|
||||
#else
|
||||
m.def("const_name_tests", const_name_tests);
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
|
||||
m.attr("underscore_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
|
||||
#elif defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
|
||||
TEST_SUBMODULE(const_name, m) {
|
||||
m.def("const_name_tests", const_name_tests);
|
||||
|
||||
#if defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
|
||||
m.def("underscore_tests", underscore_tests);
|
||||
#else
|
||||
m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.";
|
||||
|
@ -1,7 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import const_name as m
|
||||
|
||||
|
||||
@ -25,7 +23,7 @@ from pybind11_tests import const_name as m
|
||||
),
|
||||
)
|
||||
def test_const_name(func, selector, expected):
|
||||
if isinstance(func, type(u"") if env.PY2 else str):
|
||||
if isinstance(func, str):
|
||||
pytest.skip(func)
|
||||
text = func(selector)
|
||||
assert text == expected
|
||||
|
@ -31,8 +31,8 @@ py::bytes return_bytes() {
|
||||
std::string print_bytes(const py::bytes &bytes) {
|
||||
std::string ret = "bytes[";
|
||||
const auto value = static_cast<std::string>(bytes);
|
||||
for (size_t i = 0; i < value.length(); ++i) {
|
||||
ret += std::to_string(static_cast<int>(value[i])) + " ";
|
||||
for (char c : value) {
|
||||
ret += std::to_string(static_cast<int>(c)) + ' ';
|
||||
}
|
||||
ret.back() = ']';
|
||||
return ret;
|
||||
@ -137,18 +137,15 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
||||
m.def("f4", f4);
|
||||
|
||||
// test_function_record_leaks
|
||||
struct LargeCapture {
|
||||
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
|
||||
// This should always be enough to trigger the alternative branch
|
||||
// where `sizeof(capture) > sizeof(rec->data)`
|
||||
uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
};
|
||||
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
|
||||
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
|
||||
uint64_t capture[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 // CentOS7
|
||||
py::detail::silence_unused_warnings(capture);
|
||||
#endif
|
||||
m.def(
|
||||
"should_raise",
|
||||
[capture](int) { return capture.zeros[9] + 33; },
|
||||
py::kw_only(),
|
||||
py::arg());
|
||||
"should_raise", [capture](int) { return capture[9] + 33; }, py::kw_only(), py::arg());
|
||||
});
|
||||
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
|
||||
m.def(
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
m = pytest.importorskip("pybind11_tests.constants_and_functions")
|
||||
|
@ -289,4 +289,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
py::return_value_policy::move);
|
||||
m.def(
|
||||
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
|
||||
|
||||
// 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_>(); });
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import copy_move_policies as m
|
||||
@ -124,3 +123,10 @@ def test_move_fallback():
|
||||
assert m1.value == 1
|
||||
m2 = m.get_moveissue2(2)
|
||||
assert m2.value == 2
|
||||
|
||||
|
||||
def test_pytype_rvalue_cast():
|
||||
"""Make sure that cast from pytype rvalue to other pytype works"""
|
||||
|
||||
value = m.get_pytype_rvalue_castissue(1.0)
|
||||
assert value == 1
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import custom_type_casters as m
|
||||
@ -19,7 +18,7 @@ def test_noconvert_args(msg):
|
||||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||
13
|
||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
msg(a.g("this is a", "this is b", 42))
|
||||
@ -28,7 +27,7 @@ def test_noconvert_args(msg):
|
||||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||
42
|
||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
msg(a.g("this is a", "this is b", 42, "this is d"))
|
||||
@ -76,7 +75,7 @@ def test_noconvert_args(msg):
|
||||
1. (i: int) -> int
|
||||
|
||||
Invoked with: 4.0
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
|
||||
assert m.ints_only(4) == 2
|
||||
|
@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gc
|
||||
import weakref
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pybind11_tests import docstring_options as m
|
||||
|
||||
|
||||
|
@ -14,9 +14,6 @@
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# if _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable : 4127) // C4127: conditional expression is constant
|
||||
# endif
|
||||
# pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated
|
||||
#endif
|
||||
|
||||
@ -348,10 +345,13 @@ TEST_SUBMODULE(eigen, m) {
|
||||
},
|
||||
py::arg{}.noconvert());
|
||||
|
||||
// test_issue738
|
||||
// Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an
|
||||
// test_issue738, test_zero_length
|
||||
// Issue #738: 1×N or N×1 2D matrices were neither accepted nor properly copied with an
|
||||
// incompatible stride value on the length-1 dimension--but that should be allowed (without
|
||||
// requiring a copy!) because the stride value can be safely ignored on a size-1 dimension.
|
||||
// Similarly, 0×N or N×0 matrices were not accepted--again, these should be allowed since
|
||||
// they contain no data. This particularly affects numpy ≥ 1.23, which sets the strides to
|
||||
// 0 if any dimension size is 0.
|
||||
m.def("iss738_f1",
|
||||
&adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>,
|
||||
py::arg{}.noconvert());
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import ConstructorStats
|
||||
@ -201,7 +200,7 @@ def test_negative_stride_from_python(msg):
|
||||
double_threer(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None
|
||||
|
||||
Invoked with: """ # noqa: E501 line too long
|
||||
Invoked with: """
|
||||
+ repr(np.array([5.0, 4.0, 3.0], dtype="float32"))
|
||||
)
|
||||
|
||||
@ -213,7 +212,7 @@ def test_negative_stride_from_python(msg):
|
||||
double_threec(): incompatible function arguments. The following argument types are supported:
|
||||
1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None
|
||||
|
||||
Invoked with: """ # noqa: E501 line too long
|
||||
Invoked with: """
|
||||
+ repr(np.array([7.0, 4.0, 1.0], dtype="float32"))
|
||||
)
|
||||
|
||||
@ -222,9 +221,7 @@ def test_nonunit_stride_to_python():
|
||||
assert np.all(m.diagonal(ref) == ref.diagonal())
|
||||
assert np.all(m.diagonal_1(ref) == ref.diagonal(1))
|
||||
for i in range(-5, 7):
|
||||
assert np.all(
|
||||
m.diagonal_n(ref, i) == ref.diagonal(i)
|
||||
), "m.diagonal_n({})".format(i)
|
||||
assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), f"m.diagonal_n({i})"
|
||||
|
||||
assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4])
|
||||
assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:])
|
||||
@ -237,7 +234,7 @@ def test_eigen_ref_to_python():
|
||||
mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]]))
|
||||
assert np.all(
|
||||
mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]])
|
||||
), "cholesky{}".format(i)
|
||||
), f"cholesky{i}"
|
||||
|
||||
|
||||
def assign_both(a1, a2, r, c, v):
|
||||
@ -724,13 +721,13 @@ def test_sparse_signature(doc):
|
||||
doc(m.sparse_copy_r)
|
||||
== """
|
||||
sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32]
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
doc(m.sparse_copy_c)
|
||||
== """
|
||||
sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32]
|
||||
""" # noqa: E501 line too long
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@ -747,6 +744,13 @@ def test_issue738():
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", [m.iss738_f1, m.iss738_f2])
|
||||
@pytest.mark.parametrize("sizes", [(0, 2), (2, 0)])
|
||||
def test_zero_length(func, sizes):
|
||||
"""Ignore strides on a length-0 dimension (even if they would be incompatible length > 1)"""
|
||||
assert np.all(func(np.zeros(sizes)) == np.zeros(sizes))
|
||||
|
||||
|
||||
def test_issue1105():
|
||||
"""Issue 1105: 1xN or Nx1 input arrays weren't accepted for eigen
|
||||
compile-time row vectors or column vector"""
|
||||
|
@ -7,7 +7,7 @@ if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STR
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Catch 2.13.5)
|
||||
find_package(Catch 2.13.9)
|
||||
|
||||
if(CATCH_FOUND)
|
||||
message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user