Merge branch 'master' into stable

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
This commit is contained in:
Henry Schreiner 2022-07-15 20:47:12 -05:00
commit 0ba639d617
No known key found for this signature in database
GPG Key ID: B9D0E45146A241E8
152 changed files with 2843 additions and 2418 deletions

View File

@ -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

View File

@ -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
View File

@ -0,0 +1 @@
docs/*.svg binary

View File

@ -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 dont 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

View File

@ -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
View 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"
}
]
}

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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]

View File

@ -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

View File

@ -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 isnt 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.
- Its 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 Xcodes 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)

View File

@ -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
View File

@ -0,0 +1,3 @@
.highlight .go {
color: #707070;
}

View File

@ -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;
}

View File

@ -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``.

View File

@ -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:

View File

@ -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.

View File

@ -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
=====================

View File

@ -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

View File

@ -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
=========================

View File

@ -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.

View File

@ -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:

View File

@ -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()

View File

@ -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 CMakes 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

View File

@ -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);

View File

@ -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:

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -524,7 +524,7 @@ include a declaration of the form:
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
Continuing to do so wont 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.

View File

@ -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);

View File

@ -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.:

View File

@ -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)

View File

@ -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));

View File

@ -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
objects 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 objects 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 propertys 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)

View File

@ -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)

View File

@ -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());

View File

@ -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;

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View File

@ -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();

View File

@ -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>();
}
};

View File

@ -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!");
}

View File

@ -100,7 +100,7 @@ private:
if (size > remainder) {
str line(pbase(), size - remainder);
pywrite(line);
pywrite(std::move(line));
pyflush();
}

View File

@ -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;
};

View File

@ -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
}
};

View File

@ -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();
}

View File

@ -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};
}

View File

@ -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)

View File

@ -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)

View File

@ -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)) {

View File

@ -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")

View File

@ -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

View File

@ -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(

View File

@ -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("."))

View File

@ -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], ...]

View File

@ -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)

View File

@ -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]

View File

@ -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: ...

View File

@ -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",
]

View File

@ -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
View File

@ -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")

View File

@ -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)

View File

@ -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}"
)

View File

@ -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
}

View 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;
}

View File

@ -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():
"""

View File

@ -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",

View File

@ -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"

View File

@ -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")

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest
asyncio = pytest.importorskip("asyncio")

View File

@ -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)):

View File

@ -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; });

View File

@ -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():

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest
import env # noqa: F401

View File

@ -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}")

View File

@ -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)

View File

@ -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 {

View File

@ -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_"

View File

@ -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")

View File

@ -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.";

View File

@ -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

View File

@ -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(

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest
m = pytest.importorskip("pybind11_tests.constants_and_functions")

View File

@ -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_>(); });
}

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
import gc
import weakref

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from pybind11_tests import docstring_options as m

View File

@ -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());

View File

@ -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"""

View File

@ -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