Commit 03764178 by Henry Schreiner

Merge branch 'master' into v2.10

parents 80dc998e 0694ec6a
...@@ -6,7 +6,8 @@ body: ...@@ -6,7 +6,8 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Maintainers will only make a best effort to triage PRs. Please do your best to make the issue as easy to act on as possible, and only open if clearly a problem with pybind11 (ask first if unsure). Please do your best to make the issue as easy to act on as possible, and only submit here if there is clearly a problem with pybind11 (ask first if unsure). **Note that a reproducer in a PR is much more likely to get immediate attention.**
- type: checkboxes - type: checkboxes
id: steps id: steps
attributes: attributes:
...@@ -20,6 +21,13 @@ body: ...@@ -20,6 +21,13 @@ body:
- label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new). - label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new).
required: false required: false
- type: input
id: version
attributes:
label: What version (or hash if on master) of pybind11 are you using?
validations:
required: true
- type: textarea - type: textarea
id: description id: description
attributes: attributes:
...@@ -40,6 +48,14 @@ body: ...@@ -40,6 +48,14 @@ body:
The code should be minimal, have no external dependencies, isolate the The code should be minimal, have no external dependencies, isolate the
function(s) that cause breakage. Submit matched and complete C++ and function(s) that cause breakage. Submit matched and complete C++ and
Python snippets that can be easily compiled and run to diagnose the Python snippets that can be easily compiled and run to diagnose the
issue. If possible, make a PR with a new, failing test to give us a issue. — Note that a reproducer in a PR is much more likely to get
starting point to work on! immediate attention: failing tests in the pybind11 CI are the best
starting point for working out fixes.
render: text render: text
- type: input
id: regression
attributes:
label: Is this a regression? Put the last known working version here if it is.
description: Put the last known working version here if this is a regression.
value: Not a regression
...@@ -17,6 +17,8 @@ env: ...@@ -17,6 +17,8 @@ env:
PIP_ONLY_BINARY: numpy PIP_ONLY_BINARY: numpy
FORCE_COLOR: 3 FORCE_COLOR: 3
PYTEST_TIMEOUT: 300 PYTEST_TIMEOUT: 300
# For cmake:
VERBOSE: 1
jobs: jobs:
# This is the "main" test suite, which tests a large number of different # This is the "main" test suite, which tests a large number of different
...@@ -25,7 +27,7 @@ jobs: ...@@ -25,7 +27,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
runs-on: [ubuntu-latest, windows-2022, macos-latest] runs-on: [ubuntu-20.04, windows-2022, macos-latest]
python: python:
- '3.6' - '3.6'
- '3.9' - '3.9'
...@@ -42,12 +44,12 @@ jobs: ...@@ -42,12 +44,12 @@ jobs:
# We support an optional key: args, for cmake args # We support an optional key: args, for cmake args
include: include:
# Just add a key # Just add a key
- runs-on: ubuntu-latest - runs-on: ubuntu-20.04
python: '3.6' python: '3.6'
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
-DCMAKE_CXX_FLAGS="-D_=1" -DCMAKE_CXX_FLAGS="-D_=1"
- runs-on: ubuntu-latest - runs-on: ubuntu-20.04
python: 'pypy-3.8' python: 'pypy-3.8'
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
...@@ -194,13 +196,13 @@ jobs: ...@@ -194,13 +196,13 @@ jobs:
python-debug: false python-debug: false
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' Valgrind' || '' }} x64" name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' Valgrind' || '' }} x64"
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Python ${{ matrix.python-version }} (deadsnakes) - name: Setup Python ${{ matrix.python-version }} (deadsnakes)
uses: deadsnakes/action@v2.1.1 uses: deadsnakes/action@v3.0.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
debug: ${{ matrix.python-debug }} debug: ${{ matrix.python-debug }}
...@@ -918,7 +920,7 @@ jobs: ...@@ -918,7 +920,7 @@ jobs:
- name: Configure C++11 - name: Configure C++11
# LTO leads to many undefined reference like # LTO leads to many undefined reference like
# `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&) # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&)
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DCMAKE_VERBOSE_MAKEFILE=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build
- name: Build C++11 - name: Build C++11
run: cmake --build build -j 2 run: cmake --build build -j 2
...@@ -936,7 +938,7 @@ jobs: ...@@ -936,7 +938,7 @@ jobs:
run: git clean -fdx run: git clean -fdx
- name: Configure C++14 - name: Configure C++14
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DCMAKE_VERBOSE_MAKEFILE=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build2 run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build2
- name: Build C++14 - name: Build C++14
run: cmake --build build2 -j 2 run: cmake --build build2 -j 2
...@@ -954,7 +956,7 @@ jobs: ...@@ -954,7 +956,7 @@ jobs:
run: git clean -fdx run: git clean -fdx
- name: Configure C++17 - name: Configure C++17
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DCMAKE_VERBOSE_MAKEFILE=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build3 run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build3
- name: Build C++17 - name: Build C++17
run: cmake --build build3 -j 2 run: cmake --build build3 -j 2
...@@ -967,3 +969,138 @@ jobs: ...@@ -967,3 +969,138 @@ jobs:
- name: Interface test C++17 - name: Interface test C++17
run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build
windows_clang:
strategy:
matrix:
os: [windows-latest]
python: ['3.10']
runs-on: "${{ matrix.os }}"
name: "🐍 ${{ matrix.python }} ${{ matrix.os }} clang-latest"
steps:
- name: Show env
run: env
- name: Checkout
uses: actions/checkout@v3
- name: Set up Clang
uses: egor-tensin/setup-clang@v1
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.13
- name: Install ninja-build tool
uses: seanmiddleditch/gha-setup-ninja@v3
- name: Run pip installs
run: |
python -m pip install --upgrade pip
python -m pip install -r tests/requirements.txt
- name: Show Clang++ version
run: clang++ --version
- name: Show CMake version
run: cmake --version
# TODO: WERROR=ON
- name: Configure Clang
run: >
cmake -G Ninja -S . -B .
-DPYBIND11_WERROR=OFF
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_STANDARD=17
- name: Build
run: cmake --build . -j 2
- name: Python tests
run: cmake --build . --target pytest -j 2
- name: C++ tests
run: cmake --build . --target cpptest -j 2
- name: Interface test
run: cmake --build . --target test_cmake_build -j 2
- name: Clean directory
run: git clean -fdx
macos_brew_install_llvm:
name: "macos-latest brew install llvm"
runs-on: macos-latest
env:
# https://apple.stackexchange.com/questions/227026/how-to-install-recent-clang-with-homebrew
LDFLAGS: '-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib'
steps:
- name: Update PATH
run: echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
- name: Show env
run: env
- name: Checkout
uses: actions/checkout@v3
- name: Show Clang++ version before brew install llvm
run: clang++ --version
- name: brew install llvm
run: brew install llvm
- name: Show Clang++ version after brew install llvm
run: clang++ --version
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.13
- name: Run pip installs
run: |
python3 -m pip install --upgrade pip
python3 -m pip install -r tests/requirements.txt
python3 -m pip install numpy
python3 -m pip install scipy
- name: Show CMake version
run: cmake --version
- name: CMake Configure
run: >
cmake -S . -B .
-DPYBIND11_WERROR=ON
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_STANDARD=17
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
- name: Build
run: cmake --build . -j 2
- name: Python tests
run: cmake --build . --target pytest -j 2
- name: C++ tests
run: cmake --build . --target cpptest -j 2
- name: Interface test
run: cmake --build . --target test_cmake_build -j 2
- name: Clean directory
run: git clean -fdx
...@@ -9,6 +9,10 @@ on: ...@@ -9,6 +9,10 @@ on:
- stable - stable
- v* - v*
env:
# For cmake:
VERBOSE: 1
jobs: jobs:
# This tests various versions of CMake in various combinations, to make sure # This tests various versions of CMake in various combinations, to make sure
# the configure step passes. # the configure step passes.
...@@ -16,12 +20,12 @@ jobs: ...@@ -16,12 +20,12 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
runs-on: [ubuntu-latest, macos-latest, windows-latest] runs-on: [ubuntu-20.04, macos-latest, windows-latest]
arch: [x64] arch: [x64]
cmake: ["3.23"] cmake: ["3.23"]
include: include:
- runs-on: ubuntu-latest - runs-on: ubuntu-20.04
arch: x64 arch: x64
cmake: 3.4 cmake: 3.4
......
...@@ -14,6 +14,8 @@ on: ...@@ -14,6 +14,8 @@ on:
env: env:
FORCE_COLOR: 3 FORCE_COLOR: 3
# For cmake:
VERBOSE: 1
jobs: jobs:
pre-commit: pre-commit:
......
...@@ -10,7 +10,11 @@ jobs: ...@@ -10,7 +10,11 @@ jobs:
steps: steps:
- uses: actions/labeler@main - uses: actions/labeler@main
if: github.event.pull_request.merged == true if: >
github.event.pull_request.merged == true &&
!startsWith(github.event.pull_request.title, 'chore(deps):') &&
!startsWith(github.event.pull_request.title, 'ci(fix):') &&
!startsWith(github.event.pull_request.title, 'docs(changelog):')
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler_merged.yml configuration-path: .github/labeler_merged.yml
...@@ -98,13 +98,13 @@ jobs: ...@@ -98,13 +98,13 @@ jobs:
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v3
- name: Publish standard package - name: Publish standard package
uses: pypa/gh-action-pypi-publish@v1.5.1 uses: pypa/gh-action-pypi-publish@v1.6.4
with: with:
password: ${{ secrets.pypi_password }} password: ${{ secrets.pypi_password }}
packages_dir: standard/ packages_dir: standard/
- name: Publish global package - name: Publish global package
uses: pypa/gh-action-pypi-publish@v1.5.1 uses: pypa/gh-action-pypi-publish@v1.6.4
with: with:
password: ${{ secrets.pypi_password_global }} password: ${{ secrets.pypi_password_global }}
packages_dir: global/ packages_dir: global/
...@@ -11,6 +11,8 @@ concurrency: ...@@ -11,6 +11,8 @@ concurrency:
env: env:
PIP_ONLY_BINARY: numpy PIP_ONLY_BINARY: numpy
# For cmake:
VERBOSE: 1
jobs: jobs:
standard: standard:
......
...@@ -43,3 +43,4 @@ pybind11Targets.cmake ...@@ -43,3 +43,4 @@ pybind11Targets.cmake
/pybind11/share/* /pybind11/share/*
/docs/_build/* /docs/_build/*
.ipynb_checkpoints/ .ipynb_checkpoints/
tests/main.cpp
...@@ -24,7 +24,7 @@ exclude: ^tools/JoinPaths.cmake$ ...@@ -24,7 +24,7 @@ exclude: ^tools/JoinPaths.cmake$
repos: repos:
# Standard hooks # Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v4.3.0" rev: "v4.4.0"
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
...@@ -41,7 +41,7 @@ repos: ...@@ -41,7 +41,7 @@ repos:
# Upgrade old Python syntax # Upgrade old Python syntax
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: "v2.38.2" rev: "v3.3.0"
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py36-plus] args: [--py36-plus]
...@@ -54,7 +54,7 @@ repos: ...@@ -54,7 +54,7 @@ repos:
# Black, the code formatter, natively supports pre-commit # Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: "22.8.0" # Keep in sync with blacken-docs rev: "22.10.0" # Keep in sync with blacken-docs
hooks: hooks:
- id: black - id: black
...@@ -64,7 +64,7 @@ repos: ...@@ -64,7 +64,7 @@ repos:
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: additional_dependencies:
- black==22.8.0 # keep in sync with black hook - black==22.10.0 # keep in sync with black hook
# Changes tabs to spaces # Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
...@@ -80,7 +80,7 @@ repos: ...@@ -80,7 +80,7 @@ repos:
# Autoremoves unused imports # Autoremoves unused imports
- repo: https://github.com/hadialqattan/pycln - repo: https://github.com/hadialqattan/pycln
rev: "v2.1.1" rev: "v2.1.2"
hooks: hooks:
- id: pycln - id: pycln
stages: [manual] stages: [manual]
...@@ -108,7 +108,7 @@ repos: ...@@ -108,7 +108,7 @@ repos:
# Flake8 also supports pre-commit natively (same author) # Flake8 also supports pre-commit natively (same author)
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: "5.0.4" rev: "6.0.0"
hooks: hooks:
- id: flake8 - id: flake8
exclude: ^(docs/.*|tools/.*)$ exclude: ^(docs/.*|tools/.*)$
...@@ -116,7 +116,7 @@ repos: ...@@ -116,7 +116,7 @@ repos:
# PyLint has native support - not always usable, but works for us # PyLint has native support - not always usable, but works for us
- repo: https://github.com/PyCQA/pylint - repo: https://github.com/PyCQA/pylint
rev: "v2.15.3" rev: "v2.15.8"
hooks: hooks:
- id: pylint - id: pylint
files: ^pybind11 files: ^pybind11
...@@ -132,7 +132,7 @@ repos: ...@@ -132,7 +132,7 @@ repos:
# Check static types with mypy # Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: "v0.981" rev: "v0.991"
hooks: hooks:
- id: mypy - id: mypy
args: [] args: []
...@@ -141,7 +141,7 @@ repos: ...@@ -141,7 +141,7 @@ repos:
# Checks the manifest for missing files (native support) # Checks the manifest for missing files (native support)
- repo: https://github.com/mgedmin/check-manifest - repo: https://github.com/mgedmin/check-manifest
rev: "0.48" rev: "0.49"
hooks: hooks:
- id: check-manifest - id: check-manifest
# This is a slow hook, so only run this if --hook-stage manual is passed # This is a slow hook, so only run this if --hook-stage manual is passed
...@@ -152,7 +152,7 @@ repos: ...@@ -152,7 +152,7 @@ repos:
# Use tools/codespell_ignore_lines_from_errors.py # Use tools/codespell_ignore_lines_from_errors.py
# to rebuild .codespell-ignore-lines # to rebuild .codespell-ignore-lines
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: "v2.2.1" rev: "v2.2.2"
hooks: hooks:
- id: codespell - id: codespell
exclude: ".supp$" exclude: ".supp$"
...@@ -175,7 +175,7 @@ repos: ...@@ -175,7 +175,7 @@ repos:
# Clang format the codebase automatically # Clang format the codebase automatically
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v14.0.6" rev: "v15.0.4"
hooks: hooks:
- id: clang-format - id: clang-format
types_or: [c++, c, cuda] types_or: [c++, c, cuda]
...@@ -126,6 +126,8 @@ set(PYBIND11_HEADERS ...@@ -126,6 +126,8 @@ set(PYBIND11_HEADERS
include/pybind11/complex.h include/pybind11/complex.h
include/pybind11/options.h include/pybind11/options.h
include/pybind11/eigen.h include/pybind11/eigen.h
include/pybind11/eigen/matrix.h
include/pybind11/eigen/tensor.h
include/pybind11/embed.h include/pybind11/embed.h
include/pybind11/eval.h include/pybind11/eval.h
include/pybind11/gil.h include/pybind11/gil.h
......
...@@ -177,9 +177,12 @@ section. ...@@ -177,9 +177,12 @@ section.
may be explicitly (re-)thrown to delegate it to the other, may be explicitly (re-)thrown to delegate it to the other,
previously-declared existing exception translators. previously-declared existing exception translators.
Note that ``libc++`` and ``libstdc++`` `behave differently <https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_ Note that ``libc++`` and ``libstdc++`` `behave differently under macOS
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``. <https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_. with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI
boundaries need to be explicitly exported, as exercised in
``tests/test_exceptions.h``. See also:
"Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
Local vs Global Exception Translators Local vs Global Exception Translators
......
...@@ -324,6 +324,15 @@ The class ``options`` allows you to selectively suppress auto-generated signatur ...@@ -324,6 +324,15 @@ The class ``options`` allows you to selectively suppress auto-generated signatur
m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers"); m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers");
} }
pybind11 also appends all members of an enum to the resulting enum docstring.
This default behavior can be disabled by using the ``disable_enum_members_docstring()``
function of the ``options`` class.
With ``disable_user_defined_docstrings()`` all user defined docstrings of
``module_::def()``, ``class_::def()`` and ``enum_()`` are disabled, but the
function signatures and enum members are included in the docstring, unless they
are disabled separately.
Note that changes to the settings affect only function bindings created during the Note that changes to the settings affect only function bindings created during the
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
the default settings are restored to prevent unwanted side effects. the default settings are restored to prevent unwanted side effects.
......
...@@ -15,6 +15,60 @@ IN DEVELOPMENT ...@@ -15,6 +15,60 @@ IN DEVELOPMENT
Changes will be summarized here periodically. Changes will be summarized here periodically.
Version 2.10.2 (Dec 20, 2022)
-----------------------------
Changes:
* ``scoped_interpreter`` constructor taking ``PyConfig``.
`#4330 <https://github.com/pybind/pybind11/pull/4330>`_
* ``pybind11/eigen/tensor.h`` adds converters to and from ``Eigen::Tensor`` and
``Eigen::TensorMap``.
`#4201 <https://github.com/pybind/pybind11/pull/4201>`_
* ``PyGILState_Check()``'s were integrated to ``pybind11::handle``
``inc_ref()`` & ``dec_ref()``. The added GIL checks are guarded by
``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, which is the default only if
``NDEBUG`` is not defined.
`#4246 <https://github.com/pybind/pybind11/pull/4246>`_
* Add option for enable/disable enum members in docstring.
`#2768 <https://github.com/pybind/pybind11/pull/2768>`_
* Fixed typing of ``KeysView``, ``ValuesView`` and ``ItemsView`` in ``bind_map``.
`#4353 <https://github.com/pybind/pybind11/pull/4353>`_
Bug fixes:
* Bug fix affecting only Python 3.6 under very specific, uncommon conditions:
move ``PyEval_InitThreads()`` call to the correct location.
`#4350 <https://github.com/pybind/pybind11/pull/4350>`_
* Fix segfault bug when passing foreign native functions to functional.h.
`#4254 <https://github.com/pybind/pybind11/pull/4254>`_
Build system improvements:
* Support setting PYTHON_LIBRARIES manually for Windows ARM cross-compilation
(classic mode).
`#4406 <https://github.com/pybind/pybind11/pull/4406>`_
* Extend IPO/LTO detection for ICX (a.k.a IntelLLVM) compiler.
`#4402 <https://github.com/pybind/pybind11/pull/4402>`_
* Allow calling ``find_package(pybind11 CONFIG)`` multiple times from separate
directories in the same CMake project and properly link Python (new mode).
`#4401 <https://github.com/pybind/pybind11/pull/4401>`_
* ``multiprocessing_set_spawn`` in pytest fixture for added safety.
`#4377 <https://github.com/pybind/pybind11/pull/4377>`_
* Fixed a bug in two pybind11/tools cmake scripts causing "Unknown arguments specified" errors.
`#4327 <https://github.com/pybind/pybind11/pull/4327>`_
Version 2.10.1 (Oct 31, 2022) Version 2.10.1 (Oct 31, 2022)
----------------------------- -----------------------------
...@@ -95,7 +149,6 @@ Bug fixes: ...@@ -95,7 +149,6 @@ Bug fixes:
finalization. finalization.
`#4192 <https://github.com/pybind/pybind11/pull/4192>`_ `#4192 <https://github.com/pybind/pybind11/pull/4192>`_
Performance and style: Performance and style:
* Reserve space in set and STL map casters if possible. This will prevent * Reserve space in set and STL map casters if possible. This will prevent
......
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
#include <vector> #include <vector>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename type, typename SFINAE = void> template <typename type, typename SFINAE = void>
...@@ -88,7 +91,8 @@ public: ...@@ -88,7 +91,8 @@ public:
template <typename T_, \ template <typename T_, \
::pybind11::detail::enable_if_t< \ ::pybind11::detail::enable_if_t< \
std::is_same<type, ::pybind11::detail::remove_cv_t<T_>>::value, \ std::is_same<type, ::pybind11::detail::remove_cv_t<T_>>::value, \
int> = 0> \ int> \
= 0> \
static ::pybind11::handle cast( \ static ::pybind11::handle cast( \
T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \
if (!src) \ if (!src) \
...@@ -389,7 +393,7 @@ struct string_caster { ...@@ -389,7 +393,7 @@ struct string_caster {
// For UTF-8 we avoid the need for a temporary `bytes` object by using // For UTF-8 we avoid the need for a temporary `bytes` object by using
// `PyUnicode_AsUTF8AndSize`. // `PyUnicode_AsUTF8AndSize`.
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) { if (UTF_N == 8) {
Py_ssize_t size = -1; Py_ssize_t size = -1;
const auto *buffer const auto *buffer
= reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); = reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size));
...@@ -416,7 +420,7 @@ struct string_caster { ...@@ -416,7 +420,7 @@ struct string_caster {
= reinterpret_cast<const CharT *>(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); = reinterpret_cast<const CharT *>(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr()));
size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);
// Skip BOM for UTF-16/32 // Skip BOM for UTF-16/32
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) { if (UTF_N > 8) {
buffer++; buffer++;
length--; length--;
} }
...@@ -572,7 +576,7 @@ public: ...@@ -572,7 +576,7 @@ public:
// figure out how long the first encoded character is in bytes to distinguish between these // figure out how long the first encoded character is in bytes to distinguish between these
// two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as // two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as
// those can fit into a single char value. // those can fit into a single char value.
if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 8) && str_len > 1 && str_len <= 4) { if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) {
auto v0 = static_cast<unsigned char>(value[0]); auto v0 = static_cast<unsigned char>(value[0]);
// low bits only: 0-127 // low bits only: 0-127
// 0b110xxxxx - start of 2-byte sequence // 0b110xxxxx - start of 2-byte sequence
...@@ -598,7 +602,7 @@ public: ...@@ -598,7 +602,7 @@ public:
// UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a
// surrogate pair with total length 2 instantly indicates a range error (but not a "your // surrogate pair with total length 2 instantly indicates a range error (but not a "your
// string was too long" error). // string was too long" error).
else if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 16) && str_len == 2) { else if (StringCaster::UTF_N == 16 && str_len == 2) {
one_char = static_cast<CharT>(value[0]); one_char = static_cast<CharT>(value[0]);
if (one_char >= 0xD800 && one_char < 0xE000) { if (one_char >= 0xD800 && one_char < 0xE000) {
throw value_error("Character code point not in range(0x10000)"); throw value_error("Character code point not in range(0x10000)");
......
...@@ -11,14 +11,75 @@ ...@@ -11,14 +11,75 @@
#define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 10 #define PYBIND11_VERSION_MINOR 10
#define PYBIND11_VERSION_PATCH 1 #define PYBIND11_VERSION_PATCH 2
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
// Additional convention: 0xD = dev // Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x020A0100 #define PYBIND11_VERSION_HEX 0x020A0200
// Define some generic pybind11 helper macros for warning management.
//
// Note that compiler-specific push/pop pairs are baked into the
// PYBIND11_NAMESPACE_BEGIN/PYBIND11_NAMESPACE_END pair of macros. Therefore manual
// PYBIND11_WARNING_PUSH/PYBIND11_WARNING_POP are usually only needed in `#include` sections.
//
// If you find you need to suppress a warning, please try to make the suppression as local as
// possible using these macros. Please also be sure to push/pop with the pybind11 macros. Please
// only use compiler specifics if you need to check specific versions, e.g. Apple Clang vs. vanilla
// Clang.
#if defined(_MSC_VER)
# define PYBIND11_COMPILER_MSVC
# define PYBIND11_PRAGMA(...) __pragma(__VA_ARGS__)
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(warning(push))
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(warning(pop))
#elif defined(__INTEL_COMPILER)
# define PYBIND11_COMPILER_INTEL
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(warning push)
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(warning pop)
#elif defined(__clang__)
# define PYBIND11_COMPILER_CLANG
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(clang diagnostic push)
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(clang diagnostic push)
#elif defined(__GNUC__)
# define PYBIND11_COMPILER_GCC
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(GCC diagnostic push)
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(GCC diagnostic pop)
#endif
#ifdef PYBIND11_COMPILER_MSVC
# define PYBIND11_WARNING_DISABLE_MSVC(name) PYBIND11_PRAGMA(warning(disable : name))
#else
# define PYBIND11_WARNING_DISABLE_MSVC(name)
#endif
#ifdef PYBIND11_COMPILER_CLANG
# define PYBIND11_WARNING_DISABLE_CLANG(name) PYBIND11_PRAGMA(clang diagnostic ignored name)
#else
# define PYBIND11_WARNING_DISABLE_CLANG(name)
#endif
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name { #ifdef PYBIND11_COMPILER_GCC
#define PYBIND11_NAMESPACE_END(name) } # define PYBIND11_WARNING_DISABLE_GCC(name) PYBIND11_PRAGMA(GCC diagnostic ignored name)
#else
# define PYBIND11_WARNING_DISABLE_GCC(name)
#endif
#ifdef PYBIND11_COMPILER_INTEL
# define PYBIND11_WARNING_DISABLE_INTEL(name) PYBIND11_PRAGMA(warning disable name)
#else
# define PYBIND11_WARNING_DISABLE_INTEL(name)
#endif
#define PYBIND11_NAMESPACE_BEGIN(name) \
namespace name { \
PYBIND11_WARNING_PUSH
#define PYBIND11_NAMESPACE_END(name) \
PYBIND11_WARNING_POP \
}
// Robust support for some features and loading modules compiled against different pybind versions // Robust support for some features and loading modules compiled against different pybind versions
// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute // requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute
...@@ -96,13 +157,10 @@ ...@@ -96,13 +157,10 @@
#endif #endif
#if !defined(PYBIND11_EXPORT_EXCEPTION) #if !defined(PYBIND11_EXPORT_EXCEPTION)
# ifdef __MINGW32__ # if defined(__apple_build_version__)
// workaround for:
// error: 'dllexport' implies default visibility, but xxx has already been declared with a
// different visibility
# define PYBIND11_EXPORT_EXCEPTION
# else
# define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT # define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT
# else
# define PYBIND11_EXPORT_EXCEPTION
# endif # endif
#endif #endif
...@@ -154,9 +212,9 @@ ...@@ -154,9 +212,9 @@
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(push) PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_MSVC(4505)
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only) // C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
# pragma warning(disable : 4505)
# if defined(_DEBUG) && !defined(Py_DEBUG) # if defined(_DEBUG) && !defined(Py_DEBUG)
// Workaround for a VS 2022 issue. // Workaround for a VS 2022 issue.
// NOTE: This workaround knowingly violates the Python.h include order requirement: // NOTE: This workaround knowingly violates the Python.h include order requirement:
...@@ -239,7 +297,7 @@ ...@@ -239,7 +297,7 @@
# define _DEBUG # define _DEBUG
# undef PYBIND11_DEBUG_MARKER # undef PYBIND11_DEBUG_MARKER
# endif # endif
# pragma warning(pop) PYBIND11_WARNING_POP
#endif #endif
#include <cstddef> #include <cstddef>
...@@ -265,6 +323,15 @@ ...@@ -265,6 +323,15 @@
# define PYBIND11_HAS_U8STRING # define PYBIND11_HAS_U8STRING
#endif #endif
// See description of PR #4246:
#if !defined(NDEBUG) && !defined(PY_ASSERT_GIL_HELD_INCREF_DECREF) \
&& !(defined(PYPY_VERSION) \
&& defined(_MSC_VER)) /* PyPy Windows: pytest hangs indefinitely at the end of the \
process (see PR #4268) */ \
&& !defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
# define PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
#endif
// #define PYBIND11_STR_LEGACY_PERMISSIVE // #define PYBIND11_STR_LEGACY_PERMISSIVE
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject // If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
// (probably surprising and never documented, but this was the // (probably surprising and never documented, but this was the
...@@ -904,12 +971,6 @@ using expand_side_effects = bool[]; ...@@ -904,12 +971,6 @@ using expand_side_effects = bool[];
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4275)
// warning C4275: An exported class was derived from a class that wasn't exported.
// Can be ignored when derived from a STL class.
#endif
/// C++ bindings of builtin Python exceptions /// C++ bindings of builtin Python exceptions
class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error { class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error {
public: public:
...@@ -917,9 +978,6 @@ public: ...@@ -917,9 +978,6 @@ public:
/// Set the error using the Python C API /// Set the error using the Python C API
virtual void set_error() const = 0; virtual void set_error() const = 0;
}; };
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ #define PYBIND11_RUNTIME_EXCEPTION(name, type) \
class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { \ class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { \
...@@ -1148,17 +1206,6 @@ constexpr ...@@ -1148,17 +1206,6 @@ constexpr
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...) # define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...)
#endif #endif
#if defined(_MSC_VER) // All versions (as of July 2021).
// warning C4127: Conditional expression is constant
constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
# define PYBIND11_SILENCE_MSVC_C4127(...) ::pybind11::detail::silence_msvc_c4127(__VA_ARGS__)
#else
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__
#endif
#if defined(__clang__) \ #if defined(__clang__) \
&& (defined(__apple_build_version__) /* AppleClang 13.0.0.13000029 was the only data point \ && (defined(__apple_build_version__) /* AppleClang 13.0.0.13000029 was the only data point \
available. */ \ available. */ \
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include "class.h" #include "class.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <> template <>
...@@ -115,7 +118,7 @@ template <typename Class> ...@@ -115,7 +118,7 @@ template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) { void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
no_nullptr(ptr); no_nullptr(ptr);
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) { if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
// We're going to try to construct an alias by moving the cpp type. Whether or not // We're going to try to construct an alias by moving the cpp type. Whether or not
// that succeeds, we still need to destroy the original cpp pointer (either the // that succeeds, we still need to destroy the original cpp pointer (either the
// moved away leftover, if the alias construction works, or the value itself if we // moved away leftover, if the alias construction works, or the value itself if we
...@@ -156,7 +159,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { ...@@ -156,7 +159,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
auto *ptr = holder_helper<Holder<Class>>::get(holder); auto *ptr = holder_helper<Holder<Class>>::get(holder);
no_nullptr(ptr); no_nullptr(ptr);
// If we need an alias, check that the held pointer is actually an alias instance // If we need an alias, check that the held pointer is actually an alias instance
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) { if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
"is not an alias instance"); "is not an alias instance");
} }
...@@ -174,7 +177,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { ...@@ -174,7 +177,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
static_assert(std::is_move_constructible<Cpp<Class>>::value, static_assert(std::is_move_constructible<Cpp<Class>>::value,
"pybind11::init() return-by-value factory function requires a movable class"); "pybind11::init() return-by-value factory function requires a movable class");
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) { if (Class::has_alias && need_alias) {
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
} else { } else {
v_h.value_ptr() = new Cpp<Class>(std::move(result)); v_h.value_ptr() = new Cpp<Class>(std::move(result));
...@@ -206,10 +209,11 @@ struct constructor { ...@@ -206,10 +209,11 @@ struct constructor {
extra...); extra...);
} }
template <typename Class, template <
typename Class,
typename... Extra, typename... Extra,
enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, int>
int> = 0> = 0>
static void execute(Class &cl, const Extra &...extra) { static void execute(Class &cl, const Extra &...extra) {
cl.def( cl.def(
"__init__", "__init__",
...@@ -226,10 +230,11 @@ struct constructor { ...@@ -226,10 +230,11 @@ struct constructor {
extra...); extra...);
} }
template <typename Class, template <
typename Class,
typename... Extra, typename... Extra,
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, int>
int> = 0> = 0>
static void execute(Class &cl, const Extra &...extra) { static void execute(Class &cl, const Extra &...extra) {
cl.def( cl.def(
"__init__", "__init__",
...@@ -245,10 +250,11 @@ struct constructor { ...@@ -245,10 +250,11 @@ struct constructor {
// Implementing class for py::init_alias<...>() // Implementing class for py::init_alias<...>()
template <typename... Args> template <typename... Args>
struct alias_constructor { struct alias_constructor {
template <typename Class, template <
typename Class,
typename... Extra, typename... Extra,
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int>
int> = 0> = 0>
static void execute(Class &cl, const Extra &...extra) { static void execute(Class &cl, const Extra &...extra) {
cl.def( cl.def(
"__init__", "__init__",
......
...@@ -43,6 +43,8 @@ using ExceptionTranslator = void (*)(std::exception_ptr); ...@@ -43,6 +43,8 @@ using ExceptionTranslator = void (*)(std::exception_ptr);
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
constexpr const char *internals_function_record_capsule_name = "pybind11_function_record_capsule";
// Forward declarations // Forward declarations
inline PyTypeObject *make_static_property_type(); inline PyTypeObject *make_static_property_type();
inline PyTypeObject *make_default_metaclass(); inline PyTypeObject *make_default_metaclass();
...@@ -182,6 +184,16 @@ struct internals { ...@@ -182,6 +184,16 @@ struct internals {
# endif // PYBIND11_INTERNALS_VERSION > 4 # endif // PYBIND11_INTERNALS_VERSION > 4
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
PyInterpreterState *istate = nullptr; PyInterpreterState *istate = nullptr;
# if PYBIND11_INTERNALS_VERSION > 4
// Note that we have to use a std::string to allocate memory to ensure a unique address
// We want unique addresses since we use pointer equality to compare function records
std::string function_record_capsule_name = internals_function_record_capsule_name;
# endif
internals() = default;
internals(const internals &other) = delete;
internals &operator=(const internals &other) = delete;
~internals() { ~internals() {
# if PYBIND11_INTERNALS_VERSION > 4 # if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_FREE(loader_life_support_tls_key); PYBIND11_TLS_FREE(loader_life_support_tls_key);
...@@ -456,9 +468,6 @@ PYBIND11_NOINLINE internals &get_internals() { ...@@ -456,9 +468,6 @@ PYBIND11_NOINLINE internals &get_internals() {
internals_ptr = new internals(); internals_ptr = new internals();
#if defined(WITH_THREAD) #if defined(WITH_THREAD)
# if PY_VERSION_HEX < 0x03090000
PyEval_InitThreads();
# endif
PyThreadState *tstate = PyThreadState_Get(); PyThreadState *tstate = PyThreadState_Get();
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) { if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!"); pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
...@@ -548,6 +557,25 @@ const char *c_str(Args &&...args) { ...@@ -548,6 +557,25 @@ const char *c_str(Args &&...args) {
return strings.front().c_str(); return strings.front().c_str();
} }
inline const char *get_function_record_capsule_name() {
#if PYBIND11_INTERNALS_VERSION > 4
return get_internals().function_record_capsule_name.c_str();
#else
return nullptr;
#endif
}
// Determine whether or not the following capsule contains a pybind11 function record.
// Note that we use `internals` to make sure that only ABI compatible records are touched.
//
// This check is currently used in two places:
// - An important optimization in functional.h to avoid overhead in C++ -> Python -> C++
// - The sibling feature of cpp_function to allow overloads
inline bool is_function_record_capsule(const capsule &cap) {
// Pointer equality as we rely on internals() to ensure unique pointers
return cap.name() == get_function_record_capsule_name();
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/// Returns a named pointer that is shared among all extension modules (using the same /// Returns a named pointer that is shared among all extension modules (using the same
......
...@@ -1006,5 +1006,14 @@ protected: ...@@ -1006,5 +1006,14 @@ protected:
static Constructor make_move_constructor(...) { return nullptr; } static Constructor make_move_constructor(...) { return nullptr; }
}; };
PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) {
if (auto *type_data = get_type_info(ti)) {
handle th((PyObject *) type_data->type);
return th.attr("__module__").cast<std::string>() + '.'
+ th.attr("__qualname__").cast<std::string>();
}
return clean_type_id(ti.name());
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
...@@ -86,38 +86,26 @@ inline wchar_t *widen_chars(const char *safe_arg) { ...@@ -86,38 +86,26 @@ inline wchar_t *widen_chars(const char *safe_arg) {
return widened_arg; return widened_arg;
} }
PYBIND11_NAMESPACE_END(detail) inline void precheck_interpreter() {
/** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
optional `init_signal_handlers` parameter can be used to skip the registration of
signal handlers (see the `Python documentation`_ for details). Calling this function
again after the interpreter has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.)
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
used to populate ``sys.argv`` and ``sys.path``.
See the |PySys_SetArgvEx documentation|_ for details.
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
\endrst */
inline void initialize_interpreter(bool init_signal_handlers = true,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
if (Py_IsInitialized() != 0) { if (Py_IsInitialized() != 0) {
pybind11_fail("The interpreter is already running"); pybind11_fail("The interpreter is already running");
} }
}
#if PY_VERSION_HEX < 0x030B0000 #if !defined(PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX)
# define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000)
#endif
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
int argc,
const char *const *argv,
bool add_program_dir_to_path) {
detail::precheck_interpreter();
Py_InitializeEx(init_signal_handlers ? 1 : 0); Py_InitializeEx(init_signal_handlers ? 1 : 0);
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
PyEval_InitThreads();
# endif
// Before it was special-cased in python 3.8, passing an empty or null argv // 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. // caused a segfault, so we have to reimplement the special case ourselves.
...@@ -147,25 +135,29 @@ inline void initialize_interpreter(bool init_signal_handlers = true, ...@@ -147,25 +135,29 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
auto *pysys_argv = widened_argv.get(); auto *pysys_argv = widened_argv.get();
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path)); PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
#else }
PyConfig config; #endif
PyConfig_InitIsolatedConfig(&config);
config.isolated = 0;
config.use_environment = 1;
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast<char *const *>(argv)); PYBIND11_NAMESPACE_END(detail)
if (PyStatus_Exception(status)) {
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
inline void initialize_interpreter(PyConfig *config,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
detail::precheck_interpreter();
PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast<char *const *>(argv));
if (PyStatus_Exception(status) != 0) {
// A failure here indicates a character-encoding failure or the python // A failure here indicates a character-encoding failure or the python
// interpreter out of memory. Give up. // interpreter out of memory. Give up.
PyConfig_Clear(&config); PyConfig_Clear(config);
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
: "Failed to prepare CPython"); : "Failed to prepare CPython");
} }
status = Py_InitializeFromConfig(&config); status = Py_InitializeFromConfig(config);
PyConfig_Clear(&config); if (PyStatus_Exception(status) != 0) {
if (PyStatus_Exception(status)) { PyConfig_Clear(config);
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
: "Failed to init CPython"); : "Failed to init CPython");
} }
if (add_program_dir_to_path) { if (add_program_dir_to_path) {
...@@ -174,6 +166,43 @@ inline void initialize_interpreter(bool init_signal_handlers = true, ...@@ -174,6 +166,43 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
"os.path.abspath(os.path.dirname(sys.argv[0])) " "os.path.abspath(os.path.dirname(sys.argv[0])) "
"if sys.argv and os.path.exists(sys.argv[0]) else '')"); "if sys.argv and os.path.exists(sys.argv[0]) else '')");
} }
PyConfig_Clear(config);
}
#endif
/** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
optional `init_signal_handlers` parameter can be used to skip the registration of
signal handlers (see the `Python documentation`_ for details). Calling this function
again after the interpreter has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.)
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
used to populate ``sys.argv`` and ``sys.path``.
See the |PySys_SetArgvEx documentation|_ for details.
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
\endrst */
inline void initialize_interpreter(bool init_signal_handlers = true,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
detail::initialize_interpreter_pre_pyconfig(
init_signal_handlers, argc, argv, add_program_dir_to_path);
#else
PyConfig config;
PyConfig_InitIsolatedConfig(&config);
config.isolated = 0;
config.use_environment = 1;
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
#endif #endif
} }
...@@ -261,6 +290,15 @@ public: ...@@ -261,6 +290,15 @@ public:
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path); initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
} }
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
explicit scoped_interpreter(PyConfig *config,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
initialize_interpreter(config, argc, argv, add_program_dir_to_path);
}
#endif
scoped_interpreter(const scoped_interpreter &) = delete; scoped_interpreter(const scoped_interpreter &) = delete;
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
scoped_interpreter &operator=(const scoped_interpreter &) = delete; scoped_interpreter &operator=(const scoped_interpreter &) = delete;
......
...@@ -48,9 +48,16 @@ public: ...@@ -48,9 +48,16 @@ public:
*/ */
if (auto cfunc = func.cpp_function()) { if (auto cfunc = func.cpp_function()) {
auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr()); auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr());
if (isinstance<capsule>(cfunc_self)) { if (cfunc_self == nullptr) {
PyErr_Clear();
} else if (isinstance<capsule>(cfunc_self)) {
auto c = reinterpret_borrow<capsule>(cfunc_self); auto c = reinterpret_borrow<capsule>(cfunc_self);
auto *rec = (function_record *) c;
function_record *rec = nullptr;
// Check that we can safely reinterpret the capsule into a function_record
if (detail::is_function_record_capsule(c)) {
rec = c.get_pointer<function_record>();
}
while (rec != nullptr) { while (rec != nullptr) {
if (rec->is_stateless if (rec->is_stateless
......
...@@ -36,6 +36,8 @@ static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed"); ...@@ -36,6 +36,8 @@ static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
class array; // Forward declaration class array; // Forward declaration
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -875,7 +877,7 @@ public: ...@@ -875,7 +877,7 @@ public:
*/ */
template <typename T, ssize_t Dims = -1> template <typename T, ssize_t Dims = -1>
detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & { detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims) { if (Dims >= 0 && ndim() != Dims) {
throw std::domain_error("array has incorrect number of dimensions: " throw std::domain_error("array has incorrect number of dimensions: "
+ std::to_string(ndim()) + "; expected " + std::to_string(ndim()) + "; expected "
+ std::to_string(Dims)); + std::to_string(Dims));
...@@ -893,7 +895,7 @@ public: ...@@ -893,7 +895,7 @@ public:
*/ */
template <typename T, ssize_t Dims = -1> template <typename T, ssize_t Dims = -1>
detail::unchecked_reference<T, Dims> unchecked() const & { detail::unchecked_reference<T, Dims> unchecked() const & {
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims) { if (Dims >= 0 && ndim() != Dims) {
throw std::domain_error("array has incorrect number of dimensions: " throw std::domain_error("array has incorrect number of dimensions: "
+ std::to_string(ndim()) + "; expected " + std::to_string(ndim()) + "; expected "
+ std::to_string(Dims)); + std::to_string(Dims));
...@@ -1469,7 +1471,7 @@ private: ...@@ -1469,7 +1471,7 @@ private:
} }
// Extract name, offset and format descriptor for a struct field // Extract name, offset and format descriptor for a struct field
# define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, # Field) # define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field)
// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro // The main idea of this macro is borrowed from https://github.com/swansontec/map-macro
// (C) William Swanson, Paul Fultz // (C) William Swanson, Paul Fultz
...@@ -1865,9 +1867,10 @@ private: ...@@ -1865,9 +1867,10 @@ private:
} }
auto result = returned_array::create(trivial, shape); auto result = returned_array::create(trivial, shape);
PYBIND11_WARNING_PUSH
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING #ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
# pragma clang diagnostic push PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
# pragma clang diagnostic ignored "-Wreturn-std-move"
#endif #endif
if (size == 0) { if (size == 0) {
...@@ -1883,9 +1886,7 @@ private: ...@@ -1883,9 +1886,7 @@ private:
} }
return result; return result;
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING PYBIND11_WARNING_POP
# pragma clang diagnostic pop
#endif
} }
template <size_t... Index, size_t... VIndex, size_t... BIndex> template <size_t... Index, size_t... VIndex, size_t... BIndex>
......
...@@ -47,6 +47,16 @@ public: ...@@ -47,6 +47,16 @@ public:
return *this; return *this;
} }
options &disable_enum_members_docstring() & {
global_state().show_enum_members_docstring = false;
return *this;
}
options &enable_enum_members_docstring() & {
global_state().show_enum_members_docstring = true;
return *this;
}
// Getter methods (return the global state): // Getter methods (return the global state):
static bool show_user_defined_docstrings() { static bool show_user_defined_docstrings() {
...@@ -55,6 +65,10 @@ public: ...@@ -55,6 +65,10 @@ public:
static bool show_function_signatures() { return global_state().show_function_signatures; } static bool show_function_signatures() { return global_state().show_function_signatures; }
static bool show_enum_members_docstring() {
return global_state().show_enum_members_docstring;
}
// This type is not meant to be allocated on the heap. // This type is not meant to be allocated on the heap.
void *operator new(size_t) = delete; void *operator new(size_t) = delete;
...@@ -63,6 +77,8 @@ private: ...@@ -63,6 +77,8 @@ private:
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
bool show_function_signatures = true; //< Include auto-generated function signatures bool show_function_signatures = true; //< Include auto-generated function signatures
// in docstrings. // in docstrings.
bool show_enum_members_docstring = true; //< Include auto-generated member list in enum
// docstrings.
}; };
static state &global_state() { static state &global_state() {
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
# include <cxxabi.h> # include <cxxabi.h>
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/* https://stackoverflow.com/questions/46798456/handling-gccs-noexcept-type-warning /* https://stackoverflow.com/questions/46798456/handling-gccs-noexcept-type-warning
This warning is about ABI compatibility, not code health. This warning is about ABI compatibility, not code health.
It is only actually needed in a couple places, but apparently GCC 7 "generates this warning if It is only actually needed in a couple places, but apparently GCC 7 "generates this warning if
...@@ -43,11 +45,10 @@ ...@@ -43,11 +45,10 @@
No other GCC version generates this warning. No other GCC version generates this warning.
*/ */
#if defined(__GNUC__) && __GNUC__ == 7 #if defined(__GNUC__) && __GNUC__ == 7
# pragma GCC diagnostic push PYBIND11_WARNING_DISABLE_GCC("-Wnoexcept-type")
# pragma GCC diagnostic ignored "-Wnoexcept-type"
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -177,22 +178,22 @@ protected: ...@@ -177,22 +178,22 @@ protected:
auto *rec = unique_rec.get(); auto *rec = unique_rec.get();
/* Store the capture object directly in the function record if there is enough space */ /* Store the capture object directly in the function record if there is enough space */
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(capture) <= sizeof(rec->data))) { if (sizeof(capture) <= sizeof(rec->data)) {
/* Without these pragmas, GCC warns that there might not be /* Without these pragmas, GCC warns that there might not be
enough space to use the placement new operator. However, the enough space to use the placement new operator. However, the
'if' statement above ensures that this is the case. */ 'if' statement above ensures that this is the case. */
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER) PYBIND11_WARNING_PUSH
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wplacement-new" #if defined(__GNUG__) && __GNUC__ >= 6
PYBIND11_WARNING_DISABLE_GCC("-Wplacement-new")
#endif #endif
new ((capture *) &rec->data) capture{std::forward<Func>(f)}; new ((capture *) &rec->data) capture{std::forward<Func>(f)};
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop #if !PYBIND11_HAS_STD_LAUNDER
#endif PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif #endif
// UB without std::launder, but without breaking ABI and/or // UB without std::launder, but without breaking ABI and/or
// a significant refactoring it's "impossible" to solve. // a significant refactoring it's "impossible" to solve.
if (!std::is_trivially_destructible<capture>::value) { if (!std::is_trivially_destructible<capture>::value) {
...@@ -202,9 +203,7 @@ protected: ...@@ -202,9 +203,7 @@ protected:
data->~capture(); data->~capture();
}; };
} }
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER) PYBIND11_WARNING_POP
# pragma GCC diagnostic pop
#endif
} else { } else {
rec->data[0] = new capture{std::forward<Func>(f)}; rec->data[0] = new capture{std::forward<Func>(f)};
rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); };
...@@ -468,14 +467,21 @@ protected: ...@@ -468,14 +467,21 @@ protected:
if (rec->sibling) { if (rec->sibling) {
if (PyCFunction_Check(rec->sibling.ptr())) { if (PyCFunction_Check(rec->sibling.ptr())) {
auto *self = PyCFunction_GET_SELF(rec->sibling.ptr()); auto *self = PyCFunction_GET_SELF(rec->sibling.ptr());
capsule rec_capsule = isinstance<capsule>(self) ? reinterpret_borrow<capsule>(self) if (!isinstance<capsule>(self)) {
: capsule(self); chain = nullptr;
chain = (detail::function_record *) rec_capsule; } else {
auto rec_capsule = reinterpret_borrow<capsule>(self);
if (detail::is_function_record_capsule(rec_capsule)) {
chain = rec_capsule.get_pointer<detail::function_record>();
/* Never append a method to an overload chain of a parent class; /* Never append a method to an overload chain of a parent class;
instead, hide the parent's overloads in this case */ instead, hide the parent's overloads in this case */
if (!chain->scope.is(rec->scope)) { if (!chain->scope.is(rec->scope)) {
chain = nullptr; chain = nullptr;
} }
} else {
chain = nullptr;
}
}
} }
// Don't trigger for things like the default __init__, which are wrapper_descriptors // Don't trigger for things like the default __init__, which are wrapper_descriptors
// that we are intentionally replacing // that we are intentionally replacing
...@@ -496,6 +502,7 @@ protected: ...@@ -496,6 +502,7 @@ protected:
capsule rec_capsule(unique_rec.release(), capsule rec_capsule(unique_rec.release(),
[](void *ptr) { destruct((detail::function_record *) ptr); }); [](void *ptr) { destruct((detail::function_record *) ptr); });
rec_capsule.set_name(detail::get_function_record_capsule_name());
guarded_strdup.release(); guarded_strdup.release();
object scope_module; object scope_module;
...@@ -661,10 +668,13 @@ protected: ...@@ -661,10 +668,13 @@ protected:
/// Main dispatch logic for calls to functions bound using pybind11 /// Main dispatch logic for calls to functions bound using pybind11
static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) {
using namespace detail; using namespace detail;
assert(isinstance<capsule>(self));
/* Iterator over the list of potentially admissible overloads */ /* Iterator over the list of potentially admissible overloads */
const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), const function_record *overloads = reinterpret_cast<function_record *>(
PyCapsule_GetPointer(self, get_function_record_capsule_name())),
*it = overloads; *it = overloads;
assert(overloads != nullptr);
/* Need to know how many arguments + keyword arguments there are to pick the right /* Need to know how many arguments + keyword arguments there are to pick the right
overload */ overload */
...@@ -1416,9 +1426,9 @@ template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0> ...@@ -1416,9 +1426,9 @@ template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
void call_operator_delete(T *p, size_t, size_t) { void call_operator_delete(T *p, size_t, size_t) {
T::operator delete(p); T::operator delete(p);
} }
template < template <typename T,
typename T, enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int>
enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0> = 0>
void call_operator_delete(T *p, size_t s, size_t) { void call_operator_delete(T *p, size_t s, size_t) {
T::operator delete(p, s); T::operator delete(p, s);
} }
...@@ -1830,8 +1840,7 @@ private: ...@@ -1830,8 +1840,7 @@ private:
if (holder_ptr) { if (holder_ptr) {
init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible<holder_type>()); init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible<holder_type>());
v_h.set_holder_constructed(); v_h.set_holder_constructed();
} else if (PYBIND11_SILENCE_MSVC_C4127(detail::always_construct_holder<holder_type>::value) } else if (detail::always_construct_holder<holder_type>::value || inst->owned) {
|| inst->owned) {
new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>()); new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());
v_h.set_holder_constructed(); v_h.set_holder_constructed();
} }
...@@ -1871,9 +1880,22 @@ private: ...@@ -1871,9 +1880,22 @@ private:
static detail::function_record *get_function_record(handle h) { static detail::function_record *get_function_record(handle h) {
h = detail::get_function(h); h = detail::get_function(h);
return h ? (detail::function_record *) reinterpret_borrow<capsule>( if (!h) {
PyCFunction_GET_SELF(h.ptr())) return nullptr;
: nullptr; }
handle func_self = PyCFunction_GET_SELF(h.ptr());
if (!func_self) {
throw error_already_set();
}
if (!isinstance<capsule>(func_self)) {
return nullptr;
}
auto cap = reinterpret_borrow<capsule>(func_self);
if (!detail::is_function_record_capsule(cap)) {
return nullptr;
}
return cap.get_pointer<detail::function_record>();
} }
}; };
...@@ -1950,21 +1972,26 @@ struct enum_base { ...@@ -1950,21 +1972,26 @@ struct enum_base {
name("name"), name("name"),
is_method(m_base)); is_method(m_base));
if (options::show_enum_members_docstring()) {
m_base.attr("__doc__") = static_property( m_base.attr("__doc__") = static_property(
cpp_function( cpp_function(
[](handle arg) -> std::string { [](handle arg) -> std::string {
std::string docstring; std::string docstring;
dict entries = arg.attr("__entries"); dict entries = arg.attr("__entries");
if (((PyTypeObject *) arg.ptr())->tp_doc) { if (((PyTypeObject *) arg.ptr())->tp_doc) {
docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; docstring += std::string(
reinterpret_cast<PyTypeObject *>(arg.ptr())->tp_doc);
docstring += "\n\n";
} }
docstring += "Members:"; docstring += "Members:";
for (auto kv : entries) { for (auto kv : entries) {
auto key = std::string(pybind11::str(kv.first)); auto key = std::string(pybind11::str(kv.first));
auto comment = kv.second[int_(1)]; auto comment = kv.second[int_(1)];
docstring += "\n\n " + key; docstring += "\n\n ";
docstring += key;
if (!comment.is_none()) { if (!comment.is_none()) {
docstring += " : " + (std::string) pybind11::str(comment); docstring += " : ";
docstring += pybind11::str(comment).cast<std::string>();
} }
} }
return docstring; return docstring;
...@@ -1973,6 +2000,7 @@ struct enum_base { ...@@ -1973,6 +2000,7 @@ struct enum_base {
none(), none(),
none(), none(),
""); "");
}
m_base.attr("__members__") = static_property(cpp_function( m_base.attr("__members__") = static_property(cpp_function(
[](handle arg) -> dict { [](handle arg) -> dict {
...@@ -2852,7 +2880,3 @@ inline function get_overload(const T *this_ptr, const char *name) { ...@@ -2852,7 +2880,3 @@ inline function get_overload(const T *this_ptr, const char *name) {
PYBIND11_OVERRIDE_PURE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__); PYBIND11_OVERRIDE_PURE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__);
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUC__) && __GNUC__ == 7
# pragma GCC diagnostic pop // -Wnoexcept-type
#endif
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
/* A few forward declarations */ /* A few forward declarations */
class handle; class handle;
class object; class object;
...@@ -230,7 +232,8 @@ public: ...@@ -230,7 +232,8 @@ public:
detail::enable_if_t<detail::all_of<detail::none_of<std::is_base_of<handle, 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>>, detail::is_pyobj_ptr_or_nullptr_t<T>>,
std::is_convertible<T, PyObject *>>::value, std::is_convertible<T, PyObject *>>::value,
int> = 0> int>
= 0>
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
handle(T &obj) : m_ptr(obj) {} handle(T &obj) : m_ptr(obj) {}
...@@ -247,6 +250,11 @@ public: ...@@ -247,6 +250,11 @@ public:
#ifdef PYBIND11_HANDLE_REF_DEBUG #ifdef PYBIND11_HANDLE_REF_DEBUG
inc_ref_counter(1); inc_ref_counter(1);
#endif #endif
#if defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
if (m_ptr != nullptr && !PyGILState_Check()) {
throw std::runtime_error("pybind11::handle::inc_ref() PyGILState_Check() failure.");
}
#endif
Py_XINCREF(m_ptr); Py_XINCREF(m_ptr);
return *this; return *this;
} }
...@@ -257,6 +265,11 @@ public: ...@@ -257,6 +265,11 @@ public:
this function automatically. Returns a reference to itself. this function automatically. Returns a reference to itself.
\endrst */ \endrst */
const handle &dec_ref() const & { const handle &dec_ref() const & {
#if defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
if (m_ptr != nullptr && !PyGILState_Check()) {
throw std::runtime_error("pybind11::handle::dec_ref() PyGILState_Check() failure.");
}
#endif
Py_XDECREF(m_ptr); Py_XDECREF(m_ptr);
return *this; return *this;
} }
...@@ -623,12 +636,6 @@ inline std::string error_string() { ...@@ -623,12 +636,6 @@ inline std::string error_string() {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4275 4251)
// warning C4275: An exported class was derived from a class that wasn't exported.
// Can be ignored when derived from a STL class.
#endif
/// Fetch and hold an error which was already set in Python. An instance of this is typically /// Fetch and hold an error which was already set in Python. An instance of this is typically
/// thrown to propagate python-side errors back through C++ which can either be caught manually or /// 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 /// else falls back to the function dispatcher (which then raises the captured error back to
...@@ -688,9 +695,6 @@ private: ...@@ -688,9 +695,6 @@ private:
/// crashes (undefined behavior) if the Python interpreter is finalizing. /// crashes (undefined behavior) if the Python interpreter is finalizing.
static void m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr); static void m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr);
}; };
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
/// Replaces the current Python error indicator with the chosen error, performing a /// 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. /// 'raise from' to indicate that the chosen error was caused by the original error.
...@@ -893,10 +897,8 @@ object object_or_cast(T &&o); ...@@ -893,10 +897,8 @@ object object_or_cast(T &&o);
// Match a PyObject*, which we want to convert directly to handle via its converting constructor // Match a PyObject*, which we want to convert directly to handle via its converting constructor
inline handle object_or_cast(PyObject *ptr) { return ptr; } inline handle object_or_cast(PyObject *ptr) { return ptr; }
#if defined(_MSC_VER) && _MSC_VER < 1920 PYBIND11_WARNING_PUSH
# pragma warning(push) PYBIND11_WARNING_DISABLE_MSVC(4522) // warning C4522: multiple assignment operators specified
# pragma warning(disable : 4522) // warning C4522: multiple assignment operators specified
#endif
template <typename Policy> template <typename Policy>
class accessor : public object_api<accessor<Policy>> { class accessor : public object_api<accessor<Policy>> {
using key_type = typename Policy::key_type; using key_type = typename Policy::key_type;
...@@ -960,9 +962,7 @@ private: ...@@ -960,9 +962,7 @@ private:
key_type key; key_type key;
mutable object cache; mutable object cache;
}; };
#if defined(_MSC_VER) && _MSC_VER < 1920 PYBIND11_WARNING_POP
# pragma warning(pop)
#endif
PYBIND11_NAMESPACE_BEGIN(accessor_policies) PYBIND11_NAMESPACE_BEGIN(accessor_policies)
struct obj_attr { struct obj_attr {
...@@ -1702,7 +1702,7 @@ PYBIND11_NAMESPACE_BEGIN(detail) ...@@ -1702,7 +1702,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). // unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
template <typename Unsigned> template <typename Unsigned>
Unsigned as_unsigned(PyObject *o) { Unsigned as_unsigned(PyObject *o) {
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))) { if (sizeof(Unsigned) <= sizeof(unsigned long)) {
unsigned long v = PyLong_AsUnsignedLong(o); unsigned long v = PyLong_AsUnsignedLong(o);
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
} }
...@@ -1719,7 +1719,7 @@ public: ...@@ -1719,7 +1719,7 @@ public:
template <typename T, detail::enable_if_t<std::is_integral<T>::value, int> = 0> template <typename T, detail::enable_if_t<std::is_integral<T>::value, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
int_(T value) { int_(T value) {
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(T) <= sizeof(long))) { if (sizeof(T) <= sizeof(long)) {
if (std::is_signed<T>::value) { if (std::is_signed<T>::value) {
m_ptr = PyLong_FromLong((long) value); m_ptr = PyLong_FromLong((long) value);
} else { } else {
......
...@@ -10,10 +10,13 @@ ...@@ -10,10 +10,13 @@
#pragma once #pragma once
#include "detail/common.h" #include "detail/common.h"
#include "detail/type_caster_base.h"
#include "cast.h"
#include "operators.h" #include "operators.h"
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <type_traits>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -636,18 +639,52 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name) ...@@ -636,18 +639,52 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name)
"Return the canonical string representation of this map."); "Return the canonical string representation of this map.");
} }
template <typename Map> template <typename KeyType>
struct keys_view { struct keys_view {
Map &map; virtual size_t len() = 0;
virtual iterator iter() = 0;
virtual bool contains(const KeyType &k) = 0;
virtual bool contains(const object &k) = 0;
virtual ~keys_view() = default;
}; };
template <typename Map> template <typename MappedType>
struct values_view { struct values_view {
Map &map; virtual size_t len() = 0;
virtual iterator iter() = 0;
virtual ~values_view() = default;
}; };
template <typename Map> template <typename KeyType, typename MappedType>
struct items_view { struct items_view {
virtual size_t len() = 0;
virtual iterator iter() = 0;
virtual ~items_view() = default;
};
template <typename Map, typename KeysView>
struct KeysViewImpl : public KeysView {
explicit KeysViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); }
iterator iter() override { return make_key_iterator(map.begin(), map.end()); }
bool contains(const typename Map::key_type &k) override { return map.find(k) != map.end(); }
bool contains(const object &) override { return false; }
Map &map;
};
template <typename Map, typename ValuesView>
struct ValuesViewImpl : public ValuesView {
explicit ValuesViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); }
iterator iter() override { return make_value_iterator(map.begin(), map.end()); }
Map &map;
};
template <typename Map, typename ItemsView>
struct ItemsViewImpl : public ItemsView {
explicit ItemsViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); }
iterator iter() override { return make_iterator(map.begin(), map.end()); }
Map &map; Map &map;
}; };
...@@ -657,9 +694,11 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... ...@@ -657,9 +694,11 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) { class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
using KeyType = typename Map::key_type; using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
using KeysView = detail::keys_view<Map>; using StrippedKeyType = detail::remove_cvref_t<KeyType>;
using ValuesView = detail::values_view<Map>; using StrippedMappedType = detail::remove_cvref_t<MappedType>;
using ItemsView = detail::items_view<Map>; using KeysView = detail::keys_view<StrippedKeyType>;
using ValuesView = detail::values_view<StrippedMappedType>;
using ItemsView = detail::items_view<StrippedKeyType, StrippedMappedType>;
using Class_ = class_<Map, holder_type>; using Class_ = class_<Map, holder_type>;
// If either type is a non-module-local bound type then make the map binding non-local as well; // If either type is a non-module-local bound type then make the map binding non-local as well;
...@@ -673,12 +712,57 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args && ...@@ -673,12 +712,57 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
} }
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
static constexpr auto key_type_descr = detail::make_caster<KeyType>::name;
static constexpr auto mapped_type_descr = detail::make_caster<MappedType>::name;
std::string key_type_name(key_type_descr.text), mapped_type_name(mapped_type_descr.text);
// If key type isn't properly wrapped, fall back to C++ names
if (key_type_name == "%") {
key_type_name = detail::type_info_description(typeid(KeyType));
}
// Similarly for value type:
if (mapped_type_name == "%") {
mapped_type_name = detail::type_info_description(typeid(MappedType));
}
// Wrap KeysView[KeyType] if it wasn't already wrapped
if (!detail::get_type_info(typeid(KeysView))) {
class_<KeysView> keys_view( class_<KeysView> keys_view(
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local)); scope, ("KeysView[" + key_type_name + "]").c_str(), pybind11::module_local(local));
class_<ValuesView> values_view( keys_view.def("__len__", &KeysView::len);
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local)); keys_view.def("__iter__",
&KeysView::iter,
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
keys_view.def("__contains__",
static_cast<bool (KeysView::*)(const KeyType &)>(&KeysView::contains));
// Fallback for when the object is not of the key type
keys_view.def("__contains__",
static_cast<bool (KeysView::*)(const object &)>(&KeysView::contains));
}
// Similarly for ValuesView:
if (!detail::get_type_info(typeid(ValuesView))) {
class_<ValuesView> values_view(scope,
("ValuesView[" + mapped_type_name + "]").c_str(),
pybind11::module_local(local));
values_view.def("__len__", &ValuesView::len);
values_view.def("__iter__",
&ValuesView::iter,
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
}
// Similarly for ItemsView:
if (!detail::get_type_info(typeid(ItemsView))) {
class_<ItemsView> items_view( class_<ItemsView> items_view(
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local)); scope,
("ItemsView[" + key_type_name + ", ").append(mapped_type_name + "]").c_str(),
pybind11::module_local(local));
items_view.def("__len__", &ItemsView::len);
items_view.def("__iter__",
&ItemsView::iter,
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
}
cl.def(init<>()); cl.def(init<>());
...@@ -698,19 +782,25 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args && ...@@ -698,19 +782,25 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
cl.def( cl.def(
"keys", "keys",
[](Map &m) { return KeysView{m}; }, [](Map &m) {
return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map, KeysView>(m));
},
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def( cl.def(
"values", "values",
[](Map &m) { return ValuesView{m}; }, [](Map &m) {
return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map, ValuesView>(m));
},
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def( cl.def(
"items", "items",
[](Map &m) { return ItemsView{m}; }, [](Map &m) {
return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map, ItemsView>(m));
},
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
...@@ -749,36 +839,6 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args && ...@@ -749,36 +839,6 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
cl.def("__len__", &Map::size); cl.def("__len__", &Map::size);
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
keys_view.def(
"__iter__",
[](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
auto it = view.map.find(k);
if (it == view.map.end()) {
return false;
}
return true;
});
// Fallback for when the object is not of the key type
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
values_view.def(
"__iter__",
[](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
items_view.def(
"__iter__",
[](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
return cl; return cl;
} }
......
...@@ -8,5 +8,5 @@ def _to_int(s: str) -> Union[int, str]: ...@@ -8,5 +8,5 @@ def _to_int(s: str) -> Union[int, str]:
return s return s
__version__ = "2.10.1" __version__ = "2.10.2"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))
...@@ -46,5 +46,5 @@ zip_safe = False ...@@ -46,5 +46,5 @@ zip_safe = False
max-line-length = 120 max-line-length = 120
show_source = True show_source = True
exclude = .git, __pycache__, build, dist, docs, tools, venv exclude = .git, __pycache__, build, dist, docs, tools, venv
extend-ignore = E203, E722, B903, B950 extend-ignore = E203, E722
extend-select = B9 extend-select = B902, B904
...@@ -128,7 +128,8 @@ set(PYBIND11_TEST_FILES ...@@ -128,7 +128,8 @@ set(PYBIND11_TEST_FILES
test_custom_type_casters test_custom_type_casters
test_custom_type_setup test_custom_type_setup
test_docstring_options test_docstring_options
test_eigen test_eigen_matrix
test_eigen_tensor
test_enum test_enum
test_eval test_eval
test_exceptions test_exceptions
...@@ -233,7 +234,10 @@ list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH) ...@@ -233,7 +234,10 @@ list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH)
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but # Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" # keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
# skip message). # skip message).
list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I) list(FIND PYBIND11_TEST_FILES test_eigen_matrix.cpp PYBIND11_TEST_FILES_EIGEN_I)
if(PYBIND11_TEST_FILES_EIGEN_I EQUAL -1)
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
endif()
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
...@@ -288,13 +292,34 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) ...@@ -288,13 +292,34 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING}) set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING})
endif() endif()
message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}")
if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
tests_extra_targets("test_eigen_tensor.py" "eigen_tensor_avoid_stl_array")
endif()
else() else()
list(FIND PYBIND11_TEST_FILES test_eigen_matrix.cpp PYBIND11_TEST_FILES_EIGEN_I)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
endif()
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
endif()
message( message(
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download") STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
endif() endif()
endif() endif()
# Some code doesn't support gcc 4
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
endif()
endif()
# Optional dependency for some tests (boost::variant is only supported with version >= 1.56) # Optional dependency for some tests (boost::variant is only supported with version >= 1.56)
find_package(Boost 1.56) find_package(Boost 1.56)
......
...@@ -7,6 +7,8 @@ Adds docstring and exceptions message sanitizers. ...@@ -7,6 +7,8 @@ Adds docstring and exceptions message sanitizers.
import contextlib import contextlib
import difflib import difflib
import gc import gc
import multiprocessing
import os
import re import re
import textwrap import textwrap
...@@ -15,6 +17,22 @@ import pytest ...@@ -15,6 +17,22 @@ import pytest
# Early diagnostic for failed imports # Early diagnostic for failed imports
import pybind11_tests import pybind11_tests
@pytest.fixture(scope="session", autouse=True)
def always_forkserver_on_unix():
if os.name == "nt":
return
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
# In a nutshell: fork() after starting threads == flakiness in the form of deadlocks.
# It is actually a well-known pitfall, unfortunately without guard rails.
# "forkserver" is more performant than "spawn" (~9s vs ~13s for tests/test_gil_scoped.py,
# visit the issuecomment link above for details).
# Windows does not have fork() and the associated pitfall, therefore it is best left
# running with defaults.
multiprocessing.set_start_method("forkserver")
_long_marker = re.compile(r"([0-9])L") _long_marker = re.compile(r"([0-9])L")
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+") _hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
......
/*
tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#ifndef EIGEN_AVOID_STL_ARRAY
# define EIGEN_AVOID_STL_ARRAY
#endif
#include "test_eigen_tensor.inl"
PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m) { eigen_tensor_test::test_module(m); }
...@@ -55,6 +55,11 @@ detail_headers = { ...@@ -55,6 +55,11 @@ detail_headers = {
"include/pybind11/detail/typeid.h", "include/pybind11/detail/typeid.h",
} }
eigen_headers = {
"include/pybind11/eigen/matrix.h",
"include/pybind11/eigen/tensor.h",
}
stl_headers = { stl_headers = {
"include/pybind11/stl/filesystem.h", "include/pybind11/stl/filesystem.h",
} }
...@@ -82,7 +87,7 @@ py_files = { ...@@ -82,7 +87,7 @@ py_files = {
"setup_helpers.py", "setup_helpers.py",
} }
headers = main_headers | detail_headers | stl_headers headers = main_headers | detail_headers | eigen_headers | stl_headers
src_files = headers | cmake_files | pkgconfig_files src_files = headers | cmake_files | pkgconfig_files
all_files = src_files | py_files all_files = src_files | py_files
...@@ -92,6 +97,7 @@ sdist_files = { ...@@ -92,6 +97,7 @@ sdist_files = {
"pybind11/include", "pybind11/include",
"pybind11/include/pybind11", "pybind11/include/pybind11",
"pybind11/include/pybind11/detail", "pybind11/include/pybind11/detail",
"pybind11/include/pybind11/eigen",
"pybind11/include/pybind11/stl", "pybind11/include/pybind11/stl",
"pybind11/share", "pybind11/share",
"pybind11/share/cmake", "pybind11/share/cmake",
......
...@@ -73,6 +73,9 @@ PYBIND11_NAMESPACE_END(detail) ...@@ -73,6 +73,9 @@ PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
TEST_SUBMODULE(builtin_casters, m) { TEST_SUBMODULE(builtin_casters, m) {
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_MSVC(4127)
// test_simple_string // test_simple_string
m.def("string_roundtrip", [](const char *s) { return s; }); m.def("string_roundtrip", [](const char *s) { return s; });
...@@ -86,7 +89,7 @@ TEST_SUBMODULE(builtin_casters, m) { ...@@ -86,7 +89,7 @@ TEST_SUBMODULE(builtin_casters, m) {
std::wstring wstr; std::wstring wstr;
wstr.push_back(0x61); // a wstr.push_back(0x61); // a
wstr.push_back(0x2e18); // ⸘ wstr.push_back(0x2e18); // ⸘
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { if (sizeof(wchar_t) == 2) {
wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_1);
wstr.push_back(mathbfA16_2); wstr.push_back(mathbfA16_2);
} // 𝐀, utf16 } // 𝐀, utf16
...@@ -113,7 +116,7 @@ TEST_SUBMODULE(builtin_casters, m) { ...@@ -113,7 +116,7 @@ TEST_SUBMODULE(builtin_casters, m) {
// Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger // Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger
// UnicodeDecodeError // UnicodeDecodeError
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); }); m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { if (sizeof(wchar_t) == 2) {
m.def("bad_wchar_string", [=]() { m.def("bad_wchar_string", [=]() {
return std::wstring({wchar_t(0x61), wchar_t(0xd800)}); return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
}); });
...@@ -384,4 +387,6 @@ TEST_SUBMODULE(builtin_casters, m) { ...@@ -384,4 +387,6 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; }); m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; });
m.def("takes_const_ref_wrap", m.def("takes_const_ref_wrap",
[](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; }); [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
PYBIND11_WARNING_POP
} }
...@@ -240,4 +240,41 @@ TEST_SUBMODULE(callbacks, m) { ...@@ -240,4 +240,41 @@ TEST_SUBMODULE(callbacks, m) {
f(); f();
} }
}); });
auto *custom_def = []() {
static PyMethodDef def;
def.ml_name = "example_name";
def.ml_doc = "Example doc";
def.ml_meth = [](PyObject *, PyObject *args) -> PyObject * {
if (PyTuple_Size(args) != 1) {
throw std::runtime_error("Invalid number of arguments for example_name");
}
PyObject *first = PyTuple_GetItem(args, 0);
if (!PyLong_Check(first)) {
throw std::runtime_error("Invalid argument to example_name");
}
auto result = py::cast(PyLong_AsLong(first) * 9);
return result.release().ptr();
};
def.ml_flags = METH_VARARGS;
return &def;
}();
// rec_capsule with name that has the same value (but not pointer) as our internal one
// This capsule should be detected by our code as foreign and not inspected as the pointers
// shouldn't match
constexpr const char *rec_capsule_name
= pybind11::detail::internals_function_record_capsule_name;
py::capsule rec_capsule(std::malloc(1), [](void *data) { std::free(data); });
rec_capsule.set_name(rec_capsule_name);
m.add_object("custom_function", PyCFunction_New(custom_def, rec_capsule.ptr()));
// This test requires a new ABI version to pass
#if PYBIND11_INTERNALS_VERSION > 4
// rec_capsule with nullptr name
py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); });
m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr()));
#else
m.add_object("custom_function2", py::none());
#endif
} }
...@@ -193,3 +193,16 @@ def test_callback_num_times(): ...@@ -193,3 +193,16 @@ def test_callback_num_times():
if len(rates) > 1: if len(rates) > 1:
print("Min Mean Max") print("Min Mean Max")
print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}") print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}")
def test_custom_func():
assert m.custom_function(4) == 36
assert m.roundtrip(m.custom_function)(4) == 36
@pytest.mark.skipif(
m.custom_function2 is None, reason="Current PYBIND11_INTERNALS_VERSION too low"
)
def test_custom_func2():
assert m.custom_function2(3) == 27
assert m.roundtrip(m.custom_function2)(3) == 27
...@@ -22,10 +22,8 @@ ...@@ -22,10 +22,8 @@
#include <utility> #include <utility>
#if defined(_MSC_VER) PYBIND11_WARNING_DISABLE_MSVC(4324)
# pragma warning(disable : 4324)
// warning C4324: structure was padded due to alignment specifier // warning C4324: structure was padded due to alignment specifier
#endif
// test_brace_initialization // test_brace_initialization
struct NoBraceInitialization { struct NoBraceInitialization {
......
...@@ -52,15 +52,12 @@ int f1(int x) noexcept { return x + 1; } ...@@ -52,15 +52,12 @@ int f1(int x) noexcept { return x + 1; }
#endif #endif
int f2(int x) noexcept(true) { return x + 2; } int f2(int x) noexcept(true) { return x + 2; }
int f3(int x) noexcept(false) { return x + 3; } int f3(int x) noexcept(false) { return x + 3; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) PYBIND11_WARNING_PUSH
# pragma GCC diagnostic push PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
# pragma GCC diagnostic ignored "-Wdeprecated" PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
#endif
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true) int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) PYBIND11_WARNING_POP
# pragma GCC diagnostic pop
#endif
struct C { struct C {
int m1(int x) noexcept { return x - 1; } int m1(int x) noexcept { return x - 1; }
int m2(int x) const noexcept { return x - 2; } int m2(int x) const noexcept { return x - 2; }
...@@ -68,17 +65,14 @@ struct C { ...@@ -68,17 +65,14 @@ struct C {
int m4(int x) const noexcept(true) { return x - 4; } int m4(int x) const noexcept(true) { return x - 4; }
int m5(int x) noexcept(false) { return x - 5; } int m5(int x) noexcept(false) { return x - 5; }
int m6(int x) const noexcept(false) { return x - 6; } int m6(int x) const noexcept(false) { return x - 6; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) PYBIND11_WARNING_PUSH
# pragma GCC diagnostic push PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
# pragma GCC diagnostic ignored "-Wdeprecated" PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
#endif
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int m7(int x) throw() { return x - 7; } int m7(int x) throw() { return x - 7; }
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int m8(int x) const throw() { return x - 8; } int m8(int x) const throw() { return x - 8; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) PYBIND11_WARNING_POP
# pragma GCC diagnostic pop
#endif
}; };
} // namespace test_exc_sp } // namespace test_exc_sp
...@@ -126,14 +120,12 @@ TEST_SUBMODULE(constants_and_functions, m) { ...@@ -126,14 +120,12 @@ TEST_SUBMODULE(constants_and_functions, m) {
.def("m8", &C::m8); .def("m8", &C::m8);
m.def("f1", f1); m.def("f1", f1);
m.def("f2", f2); m.def("f2", f2);
#if defined(__INTEL_COMPILER)
# pragma warning push PYBIND11_WARNING_PUSH
# pragma warning disable 878 // incompatible exception specifications PYBIND11_WARNING_DISABLE_INTEL(878) // incompatible exception specifications
#endif
m.def("f3", f3); m.def("f3", f3);
#if defined(__INTEL_COMPILER) PYBIND11_WARNING_POP
# pragma warning pop
#endif
m.def("f4", f4); m.def("f4", f4);
// test_function_record_leaks // test_function_record_leaks
......
...@@ -85,4 +85,57 @@ TEST_SUBMODULE(docstring_options, m) { ...@@ -85,4 +85,57 @@ TEST_SUBMODULE(docstring_options, m) {
&DocstringTestFoo::setValue, &DocstringTestFoo::setValue,
"This is a property docstring"); "This is a property docstring");
} }
{
enum class DocstringTestEnum1 { Member1, Member2 };
py::enum_<DocstringTestEnum1>(m, "DocstringTestEnum1", "Enum docstring")
.value("Member1", DocstringTestEnum1::Member1)
.value("Member2", DocstringTestEnum1::Member2);
}
{
py::options options;
options.enable_enum_members_docstring();
enum class DocstringTestEnum2 { Member1, Member2 };
py::enum_<DocstringTestEnum2>(m, "DocstringTestEnum2", "Enum docstring")
.value("Member1", DocstringTestEnum2::Member1)
.value("Member2", DocstringTestEnum2::Member2);
}
{
py::options options;
options.disable_enum_members_docstring();
enum class DocstringTestEnum3 { Member1, Member2 };
py::enum_<DocstringTestEnum3>(m, "DocstringTestEnum3", "Enum docstring")
.value("Member1", DocstringTestEnum3::Member1)
.value("Member2", DocstringTestEnum3::Member2);
}
{
py::options options;
options.disable_user_defined_docstrings();
enum class DocstringTestEnum4 { Member1, Member2 };
py::enum_<DocstringTestEnum4>(m, "DocstringTestEnum4", "Enum docstring")
.value("Member1", DocstringTestEnum4::Member1)
.value("Member2", DocstringTestEnum4::Member2);
}
{
py::options options;
options.disable_user_defined_docstrings();
options.disable_enum_members_docstring();
enum class DocstringTestEnum5 { Member1, Member2 };
py::enum_<DocstringTestEnum5>(m, "DocstringTestEnum5", "Enum docstring")
.value("Member1", DocstringTestEnum5::Member1)
.value("Member2", DocstringTestEnum5::Member2);
}
} }
...@@ -39,3 +39,26 @@ def test_docstring_options(): ...@@ -39,3 +39,26 @@ def test_docstring_options():
# Suppression of user-defined docstrings for non-function objects # Suppression of user-defined docstrings for non-function objects
assert not m.DocstringTestFoo.__doc__ assert not m.DocstringTestFoo.__doc__
assert not m.DocstringTestFoo.value_prop.__doc__ assert not m.DocstringTestFoo.value_prop.__doc__
# Check existig behaviour of enum docstings
assert (
m.DocstringTestEnum1.__doc__
== "Enum docstring\n\nMembers:\n\n Member1\n\n Member2"
)
# options.enable_enum_members_docstring()
assert (
m.DocstringTestEnum2.__doc__
== "Enum docstring\n\nMembers:\n\n Member1\n\n Member2"
)
# options.disable_enum_members_docstring()
assert m.DocstringTestEnum3.__doc__ == "Enum docstring"
# options.disable_user_defined_docstrings()
assert m.DocstringTestEnum4.__doc__ == "Members:\n\n Member1\n\n Member2"
# options.disable_user_defined_docstrings()
# options.disable_enum_members_docstring()
# When all options are disabled, no docstring (instead of an empty one) should be generated
assert m.DocstringTestEnum5.__doc__ is None
...@@ -7,15 +7,13 @@ ...@@ -7,15 +7,13 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <pybind11/eigen.h> #include <pybind11/eigen/matrix.h>
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h" #include "pybind11_tests.h"
#if defined(_MSC_VER) PYBIND11_WARNING_DISABLE_MSVC(4996)
# pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated
#endif
#include <Eigen/Cholesky> #include <Eigen/Cholesky>
...@@ -81,7 +79,7 @@ struct CustomOperatorNew { ...@@ -81,7 +79,7 @@ struct CustomOperatorNew {
EIGEN_MAKE_ALIGNED_OPERATOR_NEW; EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
}; };
TEST_SUBMODULE(eigen, m) { TEST_SUBMODULE(eigen_matrix, m) {
using FixedMatrixR = Eigen::Matrix<float, 5, 6, Eigen::RowMajor>; using FixedMatrixR = Eigen::Matrix<float, 5, 6, Eigen::RowMajor>;
using FixedMatrixC = Eigen::Matrix<float, 5, 6>; using FixedMatrixC = Eigen::Matrix<float, 5, 6>;
using DenseMatrixR = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; using DenseMatrixR = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
......
...@@ -3,7 +3,7 @@ import pytest ...@@ -3,7 +3,7 @@ import pytest
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
np = pytest.importorskip("numpy") np = pytest.importorskip("numpy")
m = pytest.importorskip("pybind11_tests.eigen") m = pytest.importorskip("pybind11_tests.eigen_matrix")
ref = np.array( ref = np.array(
......
/*
tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#define PYBIND11_TEST_EIGEN_TENSOR_NAMESPACE eigen_tensor
#ifdef EIGEN_AVOID_STL_ARRAY
# undef EIGEN_AVOID_STL_ARRAY
#endif
#include "test_eigen_tensor.inl"
#include "pybind11_tests.h"
test_initializer egien_tensor("eigen_tensor", eigen_tensor_test::test_module);
import sys
import pytest
np = pytest.importorskip("numpy")
eigen_tensor = pytest.importorskip("pybind11_tests.eigen_tensor")
submodules = [eigen_tensor.c_style, eigen_tensor.f_style]
try:
import eigen_tensor_avoid_stl_array as avoid
submodules += [avoid.c_style, avoid.f_style]
except ImportError as e:
# Ensure config, build, toolchain, etc. issues are not masked here:
raise RuntimeError(
"import eigen_tensor_avoid_stl_array FAILED, while "
"import pybind11_tests.eigen_tensor succeeded. "
"Please ensure that "
"test_eigen_tensor.cpp & "
"eigen_tensor_avoid_stl_array.cpp "
"are built together (or both are not built if Eigen is not available)."
) from e
tensor_ref = np.empty((3, 5, 2), dtype=np.int64)
for i in range(tensor_ref.shape[0]):
for j in range(tensor_ref.shape[1]):
for k in range(tensor_ref.shape[2]):
tensor_ref[i, j, k] = i * (5 * 2) + j * 2 + k
indices = (2, 3, 1)
@pytest.fixture(autouse=True)
def cleanup():
for module in submodules:
module.setup()
yield
for module in submodules:
assert module.is_ok()
def test_import_avoid_stl_array():
pytest.importorskip("eigen_tensor_avoid_stl_array")
assert len(submodules) == 4
def assert_equal_tensor_ref(mat, writeable=True, modified=None):
assert mat.flags.writeable == writeable
copy = np.array(tensor_ref)
if modified is not None:
copy[indices] = modified
np.testing.assert_array_equal(mat, copy)
@pytest.mark.parametrize("m", submodules)
@pytest.mark.parametrize("member_name", ["member", "member_view"])
def test_reference_internal(m, member_name):
if not hasattr(sys, "getrefcount"):
pytest.skip("No reference counting")
foo = m.CustomExample()
counts = sys.getrefcount(foo)
mem = getattr(foo, member_name)
assert_equal_tensor_ref(mem, writeable=False)
new_counts = sys.getrefcount(foo)
assert new_counts == counts + 1
assert_equal_tensor_ref(mem, writeable=False)
del mem
assert sys.getrefcount(foo) == counts
assert_equal_funcs = [
"copy_tensor",
"copy_fixed_tensor",
"copy_const_tensor",
"move_tensor_copy",
"move_fixed_tensor_copy",
"take_tensor",
"take_fixed_tensor",
"reference_tensor",
"reference_tensor_v2",
"reference_fixed_tensor",
"reference_view_of_tensor",
"reference_view_of_tensor_v3",
"reference_view_of_tensor_v5",
"reference_view_of_fixed_tensor",
]
assert_equal_const_funcs = [
"reference_view_of_tensor_v2",
"reference_view_of_tensor_v4",
"reference_view_of_tensor_v6",
"reference_const_tensor",
"reference_const_tensor_v2",
]
@pytest.mark.parametrize("m", submodules)
@pytest.mark.parametrize("func_name", assert_equal_funcs + assert_equal_const_funcs)
def test_convert_tensor_to_py(m, func_name):
writeable = func_name in assert_equal_funcs
assert_equal_tensor_ref(getattr(m, func_name)(), writeable=writeable)
@pytest.mark.parametrize("m", submodules)
def test_bad_cpp_to_python_casts(m):
with pytest.raises(
RuntimeError, match="Cannot use reference internal when there is no parent"
):
m.reference_tensor_internal()
with pytest.raises(RuntimeError, match="Cannot move from a constant reference"):
m.move_const_tensor()
with pytest.raises(
RuntimeError, match="Cannot take ownership of a const reference"
):
m.take_const_tensor()
with pytest.raises(
RuntimeError,
match="Invalid return_value_policy for Eigen Map type, must be either reference or reference_internal",
):
m.take_view_tensor()
@pytest.mark.parametrize("m", submodules)
def test_bad_python_to_cpp_casts(m):
with pytest.raises(
TypeError, match=r"^round_trip_tensor\(\): incompatible function arguments"
):
m.round_trip_tensor(np.zeros((2, 3)))
with pytest.raises(TypeError, match=r"^Cannot cast array data from dtype"):
m.round_trip_tensor(np.zeros(dtype=np.str_, shape=(2, 3, 1)))
with pytest.raises(
TypeError,
match=r"^round_trip_tensor_noconvert\(\): incompatible function arguments",
):
m.round_trip_tensor_noconvert(tensor_ref)
assert_equal_tensor_ref(
m.round_trip_tensor_noconvert(tensor_ref.astype(np.float64))
)
if m.needed_options == "F":
bad_options = "C"
else:
bad_options = "F"
# Shape, dtype and the order need to be correct for a TensorMap cast
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
m.round_trip_view_tensor(
np.zeros((3, 5, 2), dtype=np.float64, order=bad_options)
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
m.round_trip_view_tensor(
np.zeros((3, 5, 2), dtype=np.float32, order=m.needed_options)
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
m.round_trip_view_tensor(
np.zeros((3, 5), dtype=np.float64, order=m.needed_options)
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
m.round_trip_view_tensor(
temp[:, ::-1, :],
)
with pytest.raises(
TypeError, match=r"^round_trip_view_tensor\(\): incompatible function arguments"
):
temp = np.zeros((3, 5, 2), dtype=np.float64, order=m.needed_options)
temp.setflags(write=False)
m.round_trip_view_tensor(temp)
@pytest.mark.parametrize("m", submodules)
def test_references_actually_refer(m):
a = m.reference_tensor()
temp = a[indices]
a[indices] = 100
assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
a[indices] = temp
assert_equal_tensor_ref(m.copy_const_tensor())
a = m.reference_view_of_tensor()
a[indices] = 100
assert_equal_tensor_ref(m.copy_const_tensor(), modified=100)
a[indices] = temp
assert_equal_tensor_ref(m.copy_const_tensor())
@pytest.mark.parametrize("m", submodules)
def test_round_trip(m):
assert_equal_tensor_ref(m.round_trip_tensor(tensor_ref))
with pytest.raises(TypeError, match="^Cannot cast array data from"):
assert_equal_tensor_ref(m.round_trip_tensor2(tensor_ref))
assert_equal_tensor_ref(m.round_trip_tensor2(np.array(tensor_ref, dtype=np.int32)))
assert_equal_tensor_ref(m.round_trip_fixed_tensor(tensor_ref))
assert_equal_tensor_ref(m.round_trip_aligned_view_tensor(m.reference_tensor()))
copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
assert_equal_tensor_ref(m.round_trip_view_tensor(copy))
assert_equal_tensor_ref(m.round_trip_view_tensor_ref(copy))
assert_equal_tensor_ref(m.round_trip_view_tensor_ptr(copy))
copy.setflags(write=False)
assert_equal_tensor_ref(m.round_trip_const_view_tensor(copy))
np.testing.assert_array_equal(
tensor_ref[:, ::-1, :], m.round_trip_tensor(tensor_ref[:, ::-1, :])
)
assert m.round_trip_rank_0(np.float64(3.5)) == 3.5
assert m.round_trip_rank_0(3.5) == 3.5
with pytest.raises(
TypeError,
match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
):
m.round_trip_rank_0_noconvert(np.float64(3.5))
with pytest.raises(
TypeError,
match=r"^round_trip_rank_0_noconvert\(\): incompatible function arguments",
):
m.round_trip_rank_0_noconvert(3.5)
with pytest.raises(
TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
):
m.round_trip_rank_0_view(np.float64(3.5))
with pytest.raises(
TypeError, match=r"^round_trip_rank_0_view\(\): incompatible function arguments"
):
m.round_trip_rank_0_view(3.5)
@pytest.mark.parametrize("m", submodules)
def test_round_trip_references_actually_refer(m):
# Need to create a copy that matches the type on the C side
copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
a = m.round_trip_view_tensor(copy)
temp = a[indices]
a[indices] = 100
assert_equal_tensor_ref(copy, modified=100)
a[indices] = temp
assert_equal_tensor_ref(copy)
@pytest.mark.parametrize("m", submodules)
def test_doc_string(m, doc):
assert (
doc(m.copy_tensor) == "copy_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
)
assert (
doc(m.copy_fixed_tensor)
== "copy_fixed_tensor() -> numpy.ndarray[numpy.float64[3, 5, 2]]"
)
assert (
doc(m.reference_const_tensor)
== "reference_const_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]"
)
order_flag = f"flags.{m.needed_options.lower()}_contiguous"
assert doc(m.round_trip_view_tensor) == (
f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])"
+ f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]"
)
assert doc(m.round_trip_const_view_tensor) == (
f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])"
+ " -> numpy.ndarray[numpy.float64[?, ?, ?]]"
)
...@@ -3,11 +3,9 @@ ...@@ -3,11 +3,9 @@
#include <pybind11/embed.h> #include <pybind11/embed.h>
#ifdef _MSC_VER
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to // Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1). // catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
# pragma warning(disable : 4996) PYBIND11_WARNING_DISABLE_MSVC(4996)
#endif
// Catch uses _ internally, which breaks gettext style defines // Catch uses _ internally, which breaks gettext style defines
#ifdef _ #ifdef _
......
#include <pybind11/embed.h> #include <pybind11/embed.h>
#ifdef _MSC_VER
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to // Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1). // catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
# pragma warning(disable : 4996) PYBIND11_WARNING_DISABLE_MSVC(4996)
#endif
#include <catch.hpp> #include <catch.hpp>
#include <cstdlib> #include <cstdlib>
...@@ -16,6 +14,11 @@ ...@@ -16,6 +14,11 @@
namespace py = pybind11; namespace py = pybind11;
using namespace py::literals; using namespace py::literals;
size_t get_sys_path_size() {
auto sys_path = py::module::import("sys").attr("path");
return py::len(sys_path);
}
class Widget { class Widget {
public: public:
explicit Widget(std::string message) : message(std::move(message)) {} explicit Widget(std::string message) : message(std::move(message)) {}
...@@ -168,6 +171,70 @@ TEST_CASE("There can be only one interpreter") { ...@@ -168,6 +171,70 @@ TEST_CASE("There can be only one interpreter") {
py::initialize_interpreter(); py::initialize_interpreter();
} }
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
TEST_CASE("Custom PyConfig") {
py::finalize_interpreter();
PyConfig config;
PyConfig_InitPythonConfig(&config);
REQUIRE_NOTHROW(py::scoped_interpreter{&config});
{
py::scoped_interpreter p{&config};
REQUIRE(py::module_::import("widget_module").attr("add")(1, 41).cast<int>() == 42);
}
py::initialize_interpreter();
}
TEST_CASE("Custom PyConfig with argv") {
py::finalize_interpreter();
{
PyConfig config;
PyConfig_InitIsolatedConfig(&config);
char *argv[] = {strdup("a.out")};
py::scoped_interpreter argv_scope{&config, 1, argv};
std::free(argv[0]);
auto module = py::module::import("test_interpreter");
auto py_widget = module.attr("DerivedWidget")("The question");
const auto &cpp_widget = py_widget.cast<const Widget &>();
REQUIRE(cpp_widget.argv0() == "a.out");
}
py::initialize_interpreter();
}
#endif
TEST_CASE("Add program dir to path pre-PyConfig") {
py::finalize_interpreter();
size_t path_size_add_program_dir_to_path_false = 0;
{
py::scoped_interpreter scoped_interp{true, 0, nullptr, false};
path_size_add_program_dir_to_path_false = get_sys_path_size();
}
{
py::scoped_interpreter scoped_interp{};
REQUIRE(get_sys_path_size() == path_size_add_program_dir_to_path_false + 1);
}
py::initialize_interpreter();
}
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
TEST_CASE("Add program dir to path using PyConfig") {
py::finalize_interpreter();
size_t path_size_add_program_dir_to_path_false = 0;
{
PyConfig config;
PyConfig_InitPythonConfig(&config);
py::scoped_interpreter scoped_interp{&config, 0, nullptr, false};
path_size_add_program_dir_to_path_false = get_sys_path_size();
}
{
PyConfig config;
PyConfig_InitPythonConfig(&config);
py::scoped_interpreter scoped_interp{&config};
REQUIRE(get_sys_path_size() == path_size_add_program_dir_to_path_false + 1);
}
py::initialize_interpreter();
}
#endif
bool has_pybind11_internals_builtin() { bool has_pybind11_internals_builtin() {
auto builtins = py::handle(PyEval_GetBuiltins()); auto builtins = py::handle(PyEval_GetBuiltins());
return builtins.contains(PYBIND11_INTERNALS_ID); return builtins.contains(PYBIND11_INTERNALS_ID);
......
...@@ -4,6 +4,7 @@ import pytest ...@@ -4,6 +4,7 @@ import pytest
import env import env
import pybind11_cross_module_tests as cm import pybind11_cross_module_tests as cm
import pybind11_tests # noqa: F401
from pybind11_tests import exceptions as m from pybind11_tests import exceptions as m
...@@ -72,9 +73,9 @@ def test_cross_module_exceptions(msg): ...@@ -72,9 +73,9 @@ def test_cross_module_exceptions(msg):
# TODO: FIXME # TODO: FIXME
@pytest.mark.xfail( @pytest.mark.xfail(
"env.PYPY and env.MACOS", "env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang'))",
raises=RuntimeError, raises=RuntimeError,
reason="Expected failure with PyPy and libc++ (Issue #2847 & PR #2999)", reason="See Issue #2847, PR #2999, PR #4324",
) )
def test_cross_module_exception_translator(): def test_cross_module_exception_translator():
with pytest.raises(KeyError): with pytest.raises(KeyError):
......
...@@ -5,6 +5,7 @@ import time ...@@ -5,6 +5,7 @@ import time
import pytest import pytest
import env
from pybind11_tests import gil_scoped as m from pybind11_tests import gil_scoped as m
...@@ -144,7 +145,6 @@ def _intentional_deadlock(): ...@@ -144,7 +145,6 @@ def _intentional_deadlock():
ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK = ALL_BASIC_TESTS + (_intentional_deadlock,) ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK = ALL_BASIC_TESTS + (_intentional_deadlock,)
SKIP_IF_DEADLOCK = True # See PR #4216
def _run_in_process(target, *args, **kwargs): def _run_in_process(target, *args, **kwargs):
...@@ -181,7 +181,7 @@ def _run_in_process(target, *args, **kwargs): ...@@ -181,7 +181,7 @@ def _run_in_process(target, *args, **kwargs):
elif process.exitcode is None: elif process.exitcode is None:
assert t_delta > 0.9 * timeout assert t_delta > 0.9 * timeout
msg = "DEADLOCK, most likely, exactly what this test is meant to detect." msg = "DEADLOCK, most likely, exactly what this test is meant to detect."
if SKIP_IF_DEADLOCK: if env.PYPY and env.WIN:
pytest.skip(msg) pytest.skip(msg)
raise RuntimeError(msg) raise RuntimeError(msg)
return process.exitcode return process.exitcode
......
...@@ -44,14 +44,13 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { ...@@ -44,14 +44,13 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
// test_args_and_kwargs // test_args_and_kwargs
m.def("args_function", [](py::args args) -> py::tuple { m.def("args_function", [](py::args args) -> py::tuple {
PYBIND11_WARNING_PUSH
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING #ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
# pragma clang diagnostic push PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
# pragma clang diagnostic ignored "-Wreturn-std-move"
#endif #endif
return args; return args;
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING PYBIND11_WARNING_POP
# pragma clang diagnostic pop
#endif
}); });
m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) { m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) {
return py::make_tuple(args, kwargs); return py::make_tuple(args, kwargs);
......
...@@ -521,4 +521,6 @@ TEST_SUBMODULE(numpy_array, sm) { ...@@ -521,4 +521,6 @@ TEST_SUBMODULE(numpy_array, sm) {
sm.def("test_fmt_desc_double", [](const py::array_t<double> &) {}); sm.def("test_fmt_desc_double", [](const py::array_t<double> &) {});
sm.def("test_fmt_desc_const_float", [](const py::array_t<const float> &) {}); sm.def("test_fmt_desc_const_float", [](const py::array_t<const float> &) {});
sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {}); sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
sm.def("round_trip_float", [](double d) { return d; });
} }
...@@ -585,3 +585,9 @@ def test_dtype_refcount_leak(): ...@@ -585,3 +585,9 @@ def test_dtype_refcount_leak():
m.ndim(a) m.ndim(a)
after = getrefcount(dtype) after = getrefcount(dtype)
assert after == before assert after == before
def test_round_trip_float():
arr = np.zeros((), np.float64)
arr[()] = 37.2
assert m.round_trip_float(arr) == 37.2
...@@ -132,22 +132,18 @@ struct hash<HashMe> { ...@@ -132,22 +132,18 @@ struct hash<HashMe> {
// Not a good abs function, but easy to test. // Not a good abs function, but easy to test.
std::string abs(const Vector2 &) { return "abs(Vector2)"; } std::string abs(const Vector2 &) { return "abs(Vector2)"; }
// MSVC & Intel warns about unknown pragmas, and warnings are errors.
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to // clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `). // `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
// Here, we suppress the warning using `#pragma diagnostic`. // Here, we suppress the warning
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46 // Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`). // TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
# if defined(__APPLE__) && defined(__clang__) #if defined(__APPLE__) && defined(__clang__)
# if (__clang_major__ >= 10) # if (__clang_major__ >= 10)
# pragma GCC diagnostic ignored "-Wself-assign-overloaded" PYBIND11_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
# endif # endif
# elif defined(__clang__) #elif defined(__clang__)
# if (__clang_major__ >= 7) # if (__clang_major__ >= 7)
# pragma GCC diagnostic ignored "-Wself-assign-overloaded" PYBIND11_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
# endif
# endif # endif
#endif #endif
...@@ -283,6 +279,3 @@ TEST_SUBMODULE(operators, m) { ...@@ -283,6 +279,3 @@ TEST_SUBMODULE(operators, m) {
m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; }); m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; });
} }
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
...@@ -309,3 +309,29 @@ def test_map_delitem(): ...@@ -309,3 +309,29 @@ def test_map_delitem():
del um["ua"] del um["ua"]
assert sorted(list(um)) == ["ub"] assert sorted(list(um)) == ["ub"]
assert sorted(list(um.items())) == [("ub", 2.6)] assert sorted(list(um.items())) == [("ub", 2.6)]
def test_map_view_types():
map_string_double = m.MapStringDouble()
unordered_map_string_double = m.UnorderedMapStringDouble()
map_string_double_const = m.MapStringDoubleConst()
unordered_map_string_double_const = m.UnorderedMapStringDoubleConst()
assert map_string_double.keys().__class__.__name__ == "KeysView[str]"
assert map_string_double.values().__class__.__name__ == "ValuesView[float]"
assert map_string_double.items().__class__.__name__ == "ItemsView[str, float]"
keys_type = type(map_string_double.keys())
assert type(unordered_map_string_double.keys()) is keys_type
assert type(map_string_double_const.keys()) is keys_type
assert type(unordered_map_string_double_const.keys()) is keys_type
values_type = type(map_string_double.values())
assert type(unordered_map_string_double.values()) is values_type
assert type(map_string_double_const.values()) is values_type
assert type(unordered_map_string_double_const.values()) is values_type
items_type = type(map_string_double.items())
assert type(unordered_map_string_double.items()) is items_type
assert type(map_string_double_const.items()) is items_type
assert type(unordered_map_string_double_const.items()) is items_type
...@@ -173,7 +173,8 @@ struct AdderBase { ...@@ -173,7 +173,8 @@ struct AdderBase {
using DataVisitor = std::function<void(const Data &)>; using DataVisitor = std::function<void(const Data &)>;
virtual void virtual void
operator()(const Data &first, const Data &second, const DataVisitor &visitor) const = 0; operator()(const Data &first, const Data &second, const DataVisitor &visitor) const
= 0;
virtual ~AdderBase() = default; virtual ~AdderBase() = default;
AdderBase() = default; AdderBase() = default;
AdderBase(const AdderBase &) = delete; AdderBase(const AdderBase &) = delete;
......
...@@ -208,7 +208,9 @@ string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}") ...@@ -208,7 +208,9 @@ string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}")
string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}") string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}")
if(CMAKE_HOST_WIN32) if(DEFINED PYTHON_LIBRARY)
# Don't write to PYTHON_LIBRARY if it's already set
elseif(CMAKE_HOST_WIN32)
set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib") set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib")
# when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the
...@@ -274,7 +276,7 @@ if(NOT PYTHON_DEBUG_LIBRARY) ...@@ -274,7 +276,7 @@ if(NOT PYTHON_DEBUG_LIBRARY)
endif() endif()
set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}")
find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARY}" find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARIES}"
"${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}") "${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}")
set(PYTHONLIBS_FOUND TRUE) set(PYTHONLIBS_FOUND TRUE)
......
...@@ -311,6 +311,16 @@ function(_pybind11_generate_lto target prefer_thin_lto) ...@@ -311,6 +311,16 @@ function(_pybind11_generate_lto target prefer_thin_lto)
HAS_FLTO "-flto${cxx_append}" "-flto${linker_append}" PYBIND11_LTO_CXX_FLAGS HAS_FLTO "-flto${cxx_append}" "-flto${linker_append}" PYBIND11_LTO_CXX_FLAGS
PYBIND11_LTO_LINKER_FLAGS) PYBIND11_LTO_LINKER_FLAGS)
endif() endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
# IntelLLVM equivalent to LTO is called IPO; also IntelLLVM is WIN32/UNIX
# WARNING/HELP WANTED: This block of code is currently not covered by pybind11 GitHub Actions!
if(WIN32)
_pybind11_return_if_cxx_and_linker_flags_work(
HAS_INTEL_IPO "-Qipo" "-Qipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
else()
_pybind11_return_if_cxx_and_linker_flags_work(
HAS_INTEL_IPO "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
# Intel equivalent to LTO is called IPO # Intel equivalent to LTO is called IPO
_pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO "-ipo" "-ipo" _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO "-ipo" "-ipo"
......
...@@ -9,7 +9,7 @@ if(CMAKE_VERSION VERSION_LESS 3.12) ...@@ -9,7 +9,7 @@ if(CMAKE_VERSION VERSION_LESS 3.12)
message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12") message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12")
endif() endif()
include_guard(GLOBAL) include_guard(DIRECTORY)
get_property( get_property(
is_config is_config
...@@ -235,7 +235,7 @@ function(pybind11_add_module target_name) ...@@ -235,7 +235,7 @@ function(pybind11_add_module target_name)
# Use case-insensitive comparison to match the result of $<CONFIG:cfgs> # Use case-insensitive comparison to match the result of $<CONFIG:cfgs>
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
if(NOT MSVC AND NOT ${uppercase_CMAKE_BUILD_TYPE} MATCHES DEBUG|RELWITHDEBINFO) if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO)
# Strip unnecessary sections of the binary on Linux/macOS # Strip unnecessary sections of the binary on Linux/macOS
pybind11_strip(${target_name}) pybind11_strip(${target_name})
endif() endif()
......
...@@ -214,7 +214,7 @@ function(pybind11_add_module target_name) ...@@ -214,7 +214,7 @@ function(pybind11_add_module target_name)
# Use case-insensitive comparison to match the result of $<CONFIG:cfgs> # Use case-insensitive comparison to match the result of $<CONFIG:cfgs>
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
if(NOT MSVC AND NOT ${uppercase_CMAKE_BUILD_TYPE} MATCHES DEBUG|RELWITHDEBINFO) if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO)
pybind11_strip(${target_name}) pybind11_strip(${target_name})
endif() endif()
......
...@@ -27,10 +27,11 @@ class InstallHeadersNested(install_headers): ...@@ -27,10 +27,11 @@ class InstallHeadersNested(install_headers):
main_headers = glob.glob("pybind11/include/pybind11/*.h") main_headers = glob.glob("pybind11/include/pybind11/*.h")
detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h") detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h")
eigen_headers = glob.glob("pybind11/include/pybind11/eigen/*.h")
stl_headers = glob.glob("pybind11/include/pybind11/stl/*.h") stl_headers = glob.glob("pybind11/include/pybind11/stl/*.h")
cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake") cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake")
pkgconfig_files = glob.glob("pybind11/share/pkgconfig/*.pc") pkgconfig_files = glob.glob("pybind11/share/pkgconfig/*.pc")
headers = main_headers + detail_headers + stl_headers headers = main_headers + detail_headers + stl_headers + eigen_headers
cmdclass = {"install_headers": InstallHeadersNested} cmdclass = {"install_headers": InstallHeadersNested}
$extra_cmd $extra_cmd
...@@ -55,6 +56,7 @@ setup( ...@@ -55,6 +56,7 @@ setup(
(base + "share/pkgconfig", pkgconfig_files), (base + "share/pkgconfig", pkgconfig_files),
(base + "include/pybind11", main_headers), (base + "include/pybind11", main_headers),
(base + "include/pybind11/detail", detail_headers), (base + "include/pybind11/detail", detail_headers),
(base + "include/pybind11/eigen", eigen_headers),
(base + "include/pybind11/stl", stl_headers), (base + "include/pybind11/stl", stl_headers),
], ],
cmdclass=cmdclass, cmdclass=cmdclass,
......
...@@ -15,6 +15,7 @@ setup( ...@@ -15,6 +15,7 @@ setup(
"pybind11", "pybind11",
"pybind11.include.pybind11", "pybind11.include.pybind11",
"pybind11.include.pybind11.detail", "pybind11.include.pybind11.detail",
"pybind11.include.pybind11.eigen",
"pybind11.include.pybind11.stl", "pybind11.include.pybind11.stl",
"pybind11.share.cmake.pybind11", "pybind11.share.cmake.pybind11",
"pybind11.share.pkgconfig", "pybind11.share.pkgconfig",
...@@ -23,6 +24,7 @@ setup( ...@@ -23,6 +24,7 @@ setup(
"pybind11": ["py.typed"], "pybind11": ["py.typed"],
"pybind11.include.pybind11": ["*.h"], "pybind11.include.pybind11": ["*.h"],
"pybind11.include.pybind11.detail": ["*.h"], "pybind11.include.pybind11.detail": ["*.h"],
"pybind11.include.pybind11.eigen": ["*.h"],
"pybind11.include.pybind11.stl": ["*.h"], "pybind11.include.pybind11.stl": ["*.h"],
"pybind11.share.cmake.pybind11": ["*.cmake"], "pybind11.share.cmake.pybind11": ["*.cmake"],
"pybind11.share.pkgconfig": ["*.pc"], "pybind11.share.pkgconfig": ["*.pc"],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment