Commit e315e1fe by Henry Schreiner

Merge branch 'master' into stable

parents 71fd5241 97976c16
FormatStyle: file FormatStyle: file
Checks: ' Checks: '
*bugprone*,
cppcoreguidelines-init-variables, cppcoreguidelines-init-variables,
cppcoreguidelines-slicing,
clang-analyzer-optin.cplusplus.VirtualCall, clang-analyzer-optin.cplusplus.VirtualCall,
google-explicit-constructor,
llvm-namespace-comment, llvm-namespace-comment,
misc-misplaced-const, misc-misplaced-const,
misc-non-copyable-objects, misc-non-copyable-objects,
...@@ -28,9 +31,10 @@ modernize-use-override, ...@@ -28,9 +31,10 @@ modernize-use-override,
modernize-use-using, modernize-use-using,
*performance*, *performance*,
readability-avoid-const-params-in-decls, readability-avoid-const-params-in-decls,
readability-const-return-type,
readability-container-size-empty, readability-container-size-empty,
readability-else-after-return,
readability-delete-null-pointer, readability-delete-null-pointer,
readability-else-after-return,
readability-implicit-bool-conversion, readability-implicit-bool-conversion,
readability-make-member-function-const, readability-make-member-function-const,
readability-misplaced-array-index, readability-misplaced-array-index,
...@@ -43,6 +47,9 @@ readability-static-accessed-through-instance, ...@@ -43,6 +47,9 @@ readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace, readability-static-definition-in-anonymous-namespace,
readability-string-compare, readability-string-compare,
readability-uniqueptr-delete-release, readability-uniqueptr-delete-release,
-bugprone-exception-escape,
-bugprone-reserved-identifier,
-bugprone-unused-raii,
' '
CheckOptions: CheckOptions:
......
...@@ -68,8 +68,8 @@ nox -l ...@@ -68,8 +68,8 @@ nox -l
# Run linters # Run linters
nox -s lint nox -s lint
# Run tests # Run tests on Python 3.9
nox -s tests nox -s tests-3.9
# Build and preview docs # Build and preview docs
nox -s docs -- serve nox -s docs -- serve
......
---
name: Bug Report
about: File an issue about a bug
title: "[BUG] "
---
Make sure you've completed the following steps before submitting your issue -- thank you!
1. Make sure you've read the [documentation][]. Your issue may be addressed there.
2. Search the [issue tracker][] to verify that this hasn't already been reported. +1 or comment there if it has.
3. Consider asking first in the [Gitter chat room][].
4. Include a self-contained and minimal piece of code that reproduces the problem. If that's not possible, try to make the description as clear as possible.
a. If possible, make a PR with a new, failing test to give us a starting point to work on!
[documentation]: https://pybind11.readthedocs.io
[issue tracker]: https://github.com/pybind/pybind11/issues
[Gitter chat room]: https://gitter.im/pybind/Lobby
*After reading, remove this checklist and the template text in parentheses below.*
## Issue description
(Provide a short description, state the expected behavior and what actually happens.)
## Reproducible example code
(The code should be minimal, have no external dependencies, isolate the function(s) that cause breakage. Submit matched and complete C++ and Python snippets that can be easily compiled and run to diagnose the issue.)
name: Bug Report
description: File an issue about a bug
title: "[BUG]: "
labels: [triage]
body:
- type: markdown
attributes:
value: |
Maintainers will only make a best effort to triage PRs. Please do your best to make the issue as easy to act on as possible, and only open if clearly a problem with pybind11 (ask first if unsure).
- type: checkboxes
id: steps
attributes:
label: Required prerequisites
description: Make sure you've completed the following steps before submitting your issue -- thank you!
options:
- label: Make sure you've read the [documentation](https://pybind11.readthedocs.io). Your issue may be addressed there.
required: true
- label: Search the [issue tracker](https://github.com/pybind/pybind11/issues) and [Discussions](https:/pybind/pybind11/discussions) to verify that this hasn't already been reported. +1 or comment there if it has.
required: true
- label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new).
required: false
- type: textarea
id: description
attributes:
label: Problem description
placeholder: >-
Provide a short description, state the expected behavior and what
actually happens. Include relevant information like what version of
pybind11 you are using, what system you are on, and any useful commands
/ output.
validations:
required: true
- type: textarea
id: code
attributes:
label: Reproducible example code
placeholder: >-
The code should be minimal, have no external dependencies, isolate the
function(s) that cause breakage. Submit matched and complete C++ and
Python snippets that can be easily compiled and run to diagnose the
issue. If possible, make a PR with a new, failing test to give us a
starting point to work on!
render: text
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Ask a question
url: https://github.com/pybind/pybind11/discussions/new
about: Please ask and answer questions here, or propose new ideas.
- name: Gitter room - name: Gitter room
url: https://gitter.im/pybind/Lobby url: https://gitter.im/pybind/Lobby
about: A room for discussing pybind11 with an active community about: A room for discussing pybind11 with an active community
---
name: Feature Request
about: File an issue about adding a feature
title: "[FEAT] "
---
Make sure you've completed the following steps before submitting your issue -- thank you!
1. Check if your feature has already been mentioned / rejected / planned in other issues.
2. If those resources didn't help, consider asking in the [Gitter chat room][] to see if this is interesting / useful to a larger audience and possible to implement reasonably,
4. If you have a useful feature that passes the previous items (or not suitable for chat), please fill in the details below.
[Gitter chat room]: https://gitter.im/pybind/Lobby
*After reading, remove this checklist.*
---
name: Question
about: File an issue about unexplained behavior
title: "[QUESTION] "
---
If you have a question, please check the following first:
1. Check if your question has already been answered in the [FAQ][] section.
2. Make sure you've read the [documentation][]. Your issue may be addressed there.
3. If those resources didn't help and you only have a short question (not a bug report), consider asking in the [Gitter chat room][]
4. Search the [issue tracker][], including the closed issues, to see if your question has already been asked/answered. +1 or comment if it has been asked but has no answer.
5. If you have a more complex question which is not answered in the previous items (or not suitable for chat), please fill in the details below.
6. Include a self-contained and minimal piece of code that illustrates your question. If that's not possible, try to make the description as clear as possible.
[FAQ]: http://pybind11.readthedocs.io/en/latest/faq.html
[documentation]: https://pybind11.readthedocs.io
[issue tracker]: https://github.com/pybind/pybind11/issues
[Gitter chat room]: https://gitter.im/pybind/Lobby
*After reading, remove this checklist.*
<!--
Title (above): please place [branch_name] at the beginning if you are targeting a branch other than master. *Do not target stable*.
It is recommended to use conventional commit format, see conventionalcommits.org, but not required.
-->
## Description ## Description
<!-- Include relevant issues or PRs here, describe what changed and why --> <!-- Include relevant issues or PRs here, describe what changed and why -->
......
...@@ -75,7 +75,7 @@ jobs: ...@@ -75,7 +75,7 @@ jobs:
run: brew install boost run: brew install boost
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
- name: Cache wheels - name: Cache wheels
if: runner.os == 'macOS' if: runner.os == 'macOS'
...@@ -144,6 +144,24 @@ jobs: ...@@ -144,6 +144,24 @@ jobs:
if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))"
run: cmake --build build2 --target cpptest run: cmake --build build2 --target cpptest
# Third build - C++17 mode with unstable ABI
- name: Configure (unstable ABI)
run: >
cmake -S . -B build3
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
-DPYBIND11_INTERNALS_VERSION=10000000
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
${{ matrix.args }}
- name: Build (unstable ABI)
run: cmake --build build3 -j 2
- name: Python tests (unstable ABI)
run: cmake --build build3 --target pytest
- name: Interface test - name: Interface test
run: cmake --build build2 --target test_cmake_build run: cmake --build build2 --target test_cmake_build
...@@ -193,7 +211,7 @@ jobs: ...@@ -193,7 +211,7 @@ jobs:
debug: ${{ matrix.python-debug }} debug: ${{ matrix.python-debug }}
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
- name: Valgrind cache - name: Valgrind cache
if: matrix.valgrind if: matrix.valgrind
...@@ -445,7 +463,7 @@ jobs: ...@@ -445,7 +463,7 @@ jobs:
run: python3 -m pip install --upgrade pip run: python3 -m pip install --upgrade pip
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
- name: Configure - name: Configure
shell: bash shell: bash
...@@ -594,14 +612,15 @@ jobs: ...@@ -594,14 +612,15 @@ jobs:
- name: VAR_BUILD_TYPE 7 - name: VAR_BUILD_TYPE 7
if: matrix.centos == 7 if: matrix.centos == 7
run: echo Release > VAR_BUILD_TYPE run: echo MinSizeRel > VAR_BUILD_TYPE
# Using Debug to avoid segfault that appeared around 2021-06-04, # Using Release to avoid segfault that appeared around 2021-06-04,
# apparently when the gcc version changed from 8.3 to 8.4. # apparently when the gcc version changed from 8.3 to 8.4.
- name: VAR_BUILD_TYPE 8 - name: VAR_BUILD_TYPE 8
if: matrix.centos == 8 if: matrix.centos == 8
run: echo Debug > VAR_BUILD_TYPE run: echo Release > VAR_BUILD_TYPE
# Temporally disabling EIGEN due to SSL issue in CentOS 7
- name: Configure - name: Configure
shell: bash shell: bash
run: > run: >
...@@ -664,7 +683,7 @@ jobs: ...@@ -664,7 +683,7 @@ jobs:
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
working-directory: /build-tests working-directory: /build-tests
- name: Run tests - name: Python tests
run: make pytest -j 2 run: make pytest -j 2
working-directory: /build-tests working-directory: /build-tests
...@@ -738,7 +757,7 @@ jobs: ...@@ -738,7 +757,7 @@ jobs:
architecture: x86 architecture: x86
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.9.0 uses: ilammy/msvc-dev-cmd@v1.9.0
...@@ -760,7 +779,7 @@ jobs: ...@@ -760,7 +779,7 @@ jobs:
- name: Build C++11 - name: Build C++11
run: cmake --build build -j 2 run: cmake --build build -j 2
- name: Run tests - name: Python tests
run: cmake --build build -t pytest run: cmake --build build -t pytest
win32-msvc2015: win32-msvc2015:
...@@ -784,7 +803,7 @@ jobs: ...@@ -784,7 +803,7 @@ jobs:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.9.0 uses: ilammy/msvc-dev-cmd@v1.9.0
...@@ -838,7 +857,7 @@ jobs: ...@@ -838,7 +857,7 @@ jobs:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
- name: Prepare env - name: Prepare env
run: python -m pip install -r tests/requirements.txt --prefer-binary run: python -m pip install -r tests/requirements.txt --prefer-binary
...@@ -861,32 +880,85 @@ jobs: ...@@ -861,32 +880,85 @@ jobs:
run: cmake --build build -t check run: cmake --build build -t check
mingw: mingw:
name: "🐍 3 windows-latest ${{ matrix.sys }}"
runs-on: windows-latest runs-on: windows-latest
defaults: defaults:
run: run:
shell: msys2 {0} shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- { sys: mingw64, env: x86_64 }
- { sys: mingw32, env: i686 }
steps: steps:
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
with: with:
msystem: ${{matrix.sys}}
install: >- install: >-
mingw-w64-x86_64-gcc git
mingw-w64-x86_64-python-pip mingw-w64-${{matrix.env}}-gcc
mingw-w64-x86_64-cmake mingw-w64-${{matrix.env}}-python-pip
mingw-w64-x86_64-make mingw-w64-${{matrix.env}}-python-numpy
mingw-w64-x86_64-python-pytest mingw-w64-${{matrix.env}}-python-scipy
mingw-w64-x86_64-eigen3 mingw-w64-${{matrix.env}}-cmake
mingw-w64-x86_64-boost mingw-w64-${{matrix.env}}-make
mingw-w64-x86_64-catch mingw-w64-${{matrix.env}}-python-pytest
mingw-w64-${{matrix.env}}-eigen3
mingw-w64-${{matrix.env}}-boost
mingw-w64-${{matrix.env}}-catch
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Configure - 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" -S . -B build run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -S . -B build
- name: Build - name: Build C++11
run: cmake --build build -j 2 run: cmake --build build -j 2
- name: Python tests - name: Python tests C++11
run: cmake --build build --target pytest run: cmake --build build --target pytest -j 2
- name: C++11 tests
run: cmake --build build --target cpptest -j 2
- name: Interface test C++11
run: cmake --build build --target test_cmake_build
- name: Clean directory
run: git clean -fdx
- name: Configure C++14
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -S . -B build2
- name: Build C++14
run: cmake --build build2 -j 2
- name: Python tests C++14
run: cmake --build build2 --target pytest -j 2
- name: C++14 tests
run: cmake --build build2 --target cpptest -j 2
- name: Interface test C++14
run: cmake --build build2 --target test_cmake_build
- name: Clean directory
run: git clean -fdx
- name: Configure C++17
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -S . -B build3
- name: Build C++17
run: cmake --build build3 -j 2
- name: Python tests C++17
run: cmake --build build3 --target pytest -j 2
- name: C++17 tests
run: cmake --build build3 --target cpptest -j 2
- name: Interface test C++17
run: cmake --build build3 --target test_cmake_build
...@@ -18,7 +18,7 @@ jobs: ...@@ -18,7 +18,7 @@ jobs:
matrix: matrix:
runs-on: [ubuntu-latest, macos-latest, windows-latest] runs-on: [ubuntu-latest, macos-latest, windows-latest]
arch: [x64] arch: [x64]
cmake: [3.18] cmake: ["3.21"]
include: include:
- runs-on: ubuntu-latest - runs-on: ubuntu-latest
...@@ -55,7 +55,7 @@ jobs: ...@@ -55,7 +55,7 @@ jobs:
# An action for adding a specific version of CMake: # An action for adding a specific version of CMake:
# https://github.com/jwlawson/actions-setup-cmake # https://github.com/jwlawson/actions-setup-cmake
- name: Setup CMake ${{ matrix.cmake }} - name: Setup CMake ${{ matrix.cmake }}
uses: jwlawson/actions-setup-cmake@v1.9 uses: jwlawson/actions-setup-cmake@v1.11
with: with:
cmake-version: ${{ matrix.cmake }} cmake-version: ${{ matrix.cmake }}
......
...@@ -19,8 +19,10 @@ repos: ...@@ -19,8 +19,10 @@ repos:
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
- id: check-docstring-first
- id: check-merge-conflict - id: check-merge-conflict
- id: check-symlinks - id: check-symlinks
- id: check-toml
- id: check-yaml - id: check-yaml
- id: debug-statements - id: debug-statements
- id: end-of-file-fixer - id: end-of-file-fixer
...@@ -31,18 +33,27 @@ repos: ...@@ -31,18 +33,27 @@ repos:
exclude: ^noxfile.py$ exclude: ^noxfile.py$
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.23.1 rev: v2.29.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
- repo: https://github.com/PyCQA/isort
rev: 5.9.3
hooks:
- id: isort
# 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: 21.7b0 rev: 21.9b0 # Keep in sync with blacken-docs
hooks: hooks:
- id: black - id: black
# By default, this ignores pyi files, though black supports them
types: [text] - repo: https://github.com/asottile/blacken-docs
files: \.pyi?$ rev: v1.11.0
hooks:
- id: blacken-docs
additional_dependencies:
- black==21.9b0 # 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
...@@ -50,6 +61,12 @@ repos: ...@@ -50,6 +61,12 @@ repos:
hooks: hooks:
- id: remove-tabs - id: remove-tabs
# Autoremoves unused imports
- repo: https://github.com/hadialqattan/pycln
rev: v1.0.3
hooks:
- id: pycln
# 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: 3.9.2 rev: 3.9.2
...@@ -81,7 +98,7 @@ repos: ...@@ -81,7 +98,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.46" rev: "0.47"
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
...@@ -95,10 +112,10 @@ repos: ...@@ -95,10 +112,10 @@ repos:
exclude: ".supp$" exclude: ".supp$"
args: ["-L", "nd,ot,thist"] args: ["-L", "nd,ot,thist"]
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.7.2.1 rev: v0.7.2.1
hooks: hooks:
- id: shellcheck - id: shellcheck
# The original pybind11 checks for a few C++ style items # The original pybind11 checks for a few C++ style items
- repo: local - repo: local
......
...@@ -7,13 +7,13 @@ ...@@ -7,13 +7,13 @@
cmake_minimum_required(VERSION 3.4) cmake_minimum_required(VERSION 3.4)
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with # The `cmake_minimum_required(VERSION 3.4...3.21)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate # some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround: # the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.18) if(${CMAKE_VERSION} VERSION_LESS 3.21)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else() else()
cmake_policy(VERSION 3.18) cmake_policy(VERSION 3.21)
endif() endif()
# Extract project version from source # Extract project version from source
...@@ -89,6 +89,9 @@ endif() ...@@ -89,6 +89,9 @@ endif()
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_NOPYTHON "Disable search for Python" OFF) option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
set(PYBIND11_INTERNALS_VERSION
""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
cmake_dependent_option( cmake_dependent_option(
USE_PYTHON_INCLUDE_DIR USE_PYTHON_INCLUDE_DIR
...@@ -183,6 +186,10 @@ if(NOT TARGET pybind11_headers) ...@@ -183,6 +186,10 @@ if(NOT TARGET pybind11_headers)
target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals
cxx_right_angle_brackets) cxx_right_angle_brackets)
if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "")
target_compile_definitions(
pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}")
endif()
else() else()
# It is invalid to install a target twice, too. # It is invalid to install a target twice, too.
set(PYBIND11_INSTALL OFF) set(PYBIND11_INSTALL OFF)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
**pybind11 — Seamless operability between C++11 and Python** **pybind11 — Seamless operability between C++11 and Python**
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status| |Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| |CI| |Build status|
|Repology| |PyPI package| |Conda-forge| |Python Versions| |Repology| |PyPI package| |Conda-forge| |Python Versions|
...@@ -134,9 +134,9 @@ About ...@@ -134,9 +134,9 @@ About
This project was created by `Wenzel This project was created by `Wenzel
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
improvements to the code were contributed by Jonas Adler, Lori A. Burns, improvements to the code were contributed by Jonas Adler, Lori A. Burns,
Sylvain Corlay, Eric Cousineau, Ralf Grosse-Kunstleve, Trent Houliston, Axel Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko, Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko,
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart. Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
We thank Google for a generous financial contribution to the continuous We thank Google for a generous financial contribution to the continuous
...@@ -176,3 +176,5 @@ to the terms and conditions of this license. ...@@ -176,3 +176,5 @@ to the terms and conditions of this license.
:target: https://repology.org/project/python:pybind11/versions :target: https://repology.org/project/python:pybind11/versions
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
:target: https://pypi.org/project/pybind11/ :target: https://pypi.org/project/pybind11/
.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
:target: https://github.com/pybind/pybind11/discussions
...@@ -26,7 +26,9 @@ The following Python snippet demonstrates the intended usage from the Python sid ...@@ -26,7 +26,9 @@ The following Python snippet demonstrates the intended usage from the Python sid
def __int__(self): def __int__(self):
return 123 return 123
from example import print from example import print
print(A()) print(A())
To register the necessary conversion routines, it is necessary to add an To register the necessary conversion routines, it is necessary to add an
......
...@@ -112,7 +112,7 @@ example: ...@@ -112,7 +112,7 @@ example:
.. code-block:: python .. code-block:: python
a = MyClass() a = MyClass()
m = a.get_matrix() # flags.writeable = True, flags.owndata = False m = a.get_matrix() # flags.writeable = True, flags.owndata = False
v = a.view_matrix() # flags.writeable = False, flags.owndata = False v = a.view_matrix() # flags.writeable = False, flags.owndata = False
c = a.copy_matrix() # flags.writeable = True, flags.owndata = True c = a.copy_matrix() # flags.writeable = True, flags.owndata = True
# m[5,6] and v[5,6] refer to the same element, c[5,6] does not. # m[5,6] and v[5,6] refer to the same element, c[5,6] does not.
...@@ -203,7 +203,7 @@ adding the ``order='F'`` option when creating an array: ...@@ -203,7 +203,7 @@ adding the ``order='F'`` option when creating an array:
.. code-block:: python .. code-block:: python
myarray = np.array(source, order='F') myarray = np.array(source, order="F")
Such an object will be passable to a bound function accepting an Such an object will be passable to a bound function accepting an
``Eigen::Ref<MatrixXd>`` (or similar column-major Eigen type). ``Eigen::Ref<MatrixXd>`` (or similar column-major Eigen type).
......
...@@ -36,13 +36,13 @@ everywhere <http://utf8everywhere.org/>`_. ...@@ -36,13 +36,13 @@ everywhere <http://utf8everywhere.org/>`_.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> utf8_test('🎂') >>> utf8_test("🎂")
utf-8 is icing on the cake. utf-8 is icing on the cake.
🎂 🎂
>>> utf8_charptr('🍕') >>> utf8_charptr("🍕")
My favorite food is My favorite food is
🍕 🍕
...@@ -80,7 +80,7 @@ raise a ``UnicodeDecodeError``. ...@@ -80,7 +80,7 @@ raise a ``UnicodeDecodeError``.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> isinstance(example.std_string_return(), str) >>> isinstance(example.std_string_return(), str)
True True
...@@ -114,7 +114,7 @@ conversion has the same overhead as implicit conversion. ...@@ -114,7 +114,7 @@ conversion has the same overhead as implicit conversion.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> str_output() >>> str_output()
'Send your résumé to Alice in HR' 'Send your résumé to Alice in HR'
...@@ -143,7 +143,7 @@ returned to Python as ``bytes``, then one can return the data as a ...@@ -143,7 +143,7 @@ returned to Python as ``bytes``, then one can return the data as a
} }
); );
.. code-block:: python .. code-block:: pycon
>>> example.return_bytes() >>> example.return_bytes()
b'\xba\xd0\xba\xd0' b'\xba\xd0\xba\xd0'
...@@ -160,7 +160,7 @@ encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly. ...@@ -160,7 +160,7 @@ encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> isinstance(example.asymmetry(b"have some bytes"), str) >>> isinstance(example.asymmetry(b"have some bytes"), str)
True True
...@@ -229,16 +229,16 @@ character. ...@@ -229,16 +229,16 @@ character.
m.def("pass_char", [](char c) { return c; }); m.def("pass_char", [](char c) { return c; });
m.def("pass_wchar", [](wchar_t w) { return w; }); m.def("pass_wchar", [](wchar_t w) { return w; });
.. code-block:: python .. code-block:: pycon
>>> example.pass_char('A') >>> example.pass_char("A")
'A' 'A'
While C++ will cast integers to character types (``char c = 0x65;``), pybind11 While C++ will cast integers to character types (``char c = 0x65;``), pybind11
does not convert Python integers to characters implicitly. The Python function does not convert Python integers to characters implicitly. The Python function
``chr()`` can be used to convert integers to characters. ``chr()`` can be used to convert integers to characters.
.. code-block:: python .. code-block:: pycon
>>> example.pass_char(0x65) >>> example.pass_char(0x65)
TypeError TypeError
...@@ -259,17 +259,17 @@ a combining acute accent). The combining character will be lost if the ...@@ -259,17 +259,17 @@ a combining acute accent). The combining character will be lost if the
two-character sequence is passed as an argument, even though it renders as a two-character sequence is passed as an argument, even though it renders as a
single grapheme. single grapheme.
.. code-block:: python .. code-block:: pycon
>>> example.pass_wchar('é') >>> example.pass_wchar("é")
'é' 'é'
>>> combining_e_acute = 'e' + '\u0301' >>> combining_e_acute = "e" + "\u0301"
>>> combining_e_acute >>> combining_e_acute
'é' 'é'
>>> combining_e_acute == 'é' >>> combining_e_acute == "é"
False False
>>> example.pass_wchar(combining_e_acute) >>> example.pass_wchar(combining_e_acute)
...@@ -278,9 +278,9 @@ single grapheme. ...@@ -278,9 +278,9 @@ single grapheme.
Normalizing combining characters before passing the character literal to C++ Normalizing combining characters before passing the character literal to C++
may resolve *some* of these issues: may resolve *some* of these issues:
.. code-block:: python .. code-block:: pycon
>>> example.pass_wchar(unicodedata.normalize('NFC', combining_e_acute)) >>> example.pass_wchar(unicodedata.normalize("NFC", combining_e_acute))
'é' 'é'
In some languages (Thai for example), there are `graphemes that cannot be In some languages (Thai for example), there are `graphemes that cannot be
......
...@@ -136,7 +136,7 @@ a virtual method call. ...@@ -136,7 +136,7 @@ a virtual method call.
u'woof! woof! woof! ' u'woof! woof! woof! '
>>> class Cat(Animal): >>> class Cat(Animal):
... def go(self, n_times): ... def go(self, n_times):
... return "meow! " * n_times ... return "meow! " * n_times
... ...
>>> c = Cat() >>> c = Cat()
>>> call_go(c) >>> call_go(c)
...@@ -159,8 +159,9 @@ Here is an example: ...@@ -159,8 +159,9 @@ Here is an example:
class Dachshund(Dog): class Dachshund(Dog):
def __init__(self, name): def __init__(self, name):
Dog.__init__(self) # Without this, a TypeError is raised. Dog.__init__(self) # Without this, a TypeError is raised.
self.name = name self.name = name
def bark(self): def bark(self):
return "yap!" return "yap!"
...@@ -1153,6 +1154,7 @@ error: ...@@ -1153,6 +1154,7 @@ error:
>>> class PyFinalChild(IsFinal): >>> class PyFinalChild(IsFinal):
... pass ... pass
...
TypeError: type 'IsFinal' is not an acceptable base type TypeError: type 'IsFinal' is not an acceptable base type
.. note:: This attribute is currently ignored on PyPy .. note:: This attribute is currently ignored on PyPy
...@@ -1247,7 +1249,7 @@ Accessing the type object ...@@ -1247,7 +1249,7 @@ Accessing the type object
You can get the type object from a C++ class that has already been registered using: You can get the type object from a C++ class that has already been registered using:
.. code-block:: python .. code-block:: cpp
py::type T_py = py::type::of<T>(); py::type T_py = py::type::of<T>();
...@@ -1259,3 +1261,37 @@ object, just like ``type(ob)`` in Python. ...@@ -1259,3 +1261,37 @@ object, just like ``type(ob)`` in Python.
Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`. Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`.
.. versionadded:: 2.6 .. versionadded:: 2.6
Custom type setup
=================
For advanced use cases, such as enabling garbage collection support, you may
wish to directly manipulate the `PyHeapTypeObject` corresponding to a
``py::class_`` definition.
You can do that using ``py::custom_type_setup``:
.. code-block:: cpp
struct OwnsPythonObjects {
py::object value = py::none();
};
py::class_<OwnsPythonObjects> cls(
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
Py_VISIT(self.value.ptr());
return 0;
};
type->tp_clear = [](PyObject *self_base) {
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
self.value = py::none();
return 0;
};
}));
cls.def(py::init<>());
cls.def_readwrite("value", &OwnsPythonObjects::value);
.. versionadded:: 2.8
...@@ -122,6 +122,7 @@ embedding the interpreter. This makes it easy to import local Python files: ...@@ -122,6 +122,7 @@ embedding the interpreter. This makes it easy to import local Python files:
"""calc.py located in the working directory""" """calc.py located in the working directory"""
def add(i, j): def add(i, j):
return i + j return i + j
......
...@@ -75,9 +75,10 @@ Registering custom translators ...@@ -75,9 +75,10 @@ Registering custom translators
If the default exception conversion policy described above is insufficient, If the default exception conversion policy described above is insufficient,
pybind11 also provides support for registering custom exception translators. pybind11 also provides support for registering custom exception translators.
To register a simple exception conversion that translates a C++ exception into Similar to pybind11 classes, exception translators can be local to the module
a new Python exception using the C++ exception's ``what()`` method, a helper they are defined in or global to the entire python session. To register a simple
function is available: exception conversion that translates a C++ exception into a new Python exception
using the C++ exception's ``what()`` method, a helper function is available:
.. code-block:: cpp .. code-block:: cpp
...@@ -87,12 +88,20 @@ This call creates a Python exception class with the name ``PyExp`` in the given ...@@ -87,12 +88,20 @@ This call creates a Python exception class with the name ``PyExp`` in the given
module and automatically converts any encountered exceptions of type ``CppExp`` module and automatically converts any encountered exceptions of type ``CppExp``
into Python exceptions of type ``PyExp``. into Python exceptions of type ``PyExp``.
A matching function is available for registering a local exception translator:
.. code-block:: cpp
py::register_local_exception<CppExp>(module, "PyExp");
It is possible to specify base class for the exception using the third It is possible to specify base class for the exception using the third
parameter, a `handle`: parameter, a `handle`:
.. code-block:: cpp .. code-block:: cpp
py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError); py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
py::register_local_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
Then `PyExp` can be caught both as `PyExp` and `RuntimeError`. Then `PyExp` can be caught both as `PyExp` and `RuntimeError`.
...@@ -100,16 +109,18 @@ The class objects of the built-in Python exceptions are listed in the Python ...@@ -100,16 +109,18 @@ The class objects of the built-in Python exceptions are listed in the Python
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_. documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_.
The default base class is `PyExc_Exception`. The default base class is `PyExc_Exception`.
When more advanced exception translation is needed, the function When more advanced exception translation is needed, the functions
``py::register_exception_translator(translator)`` can be used to register ``py::register_exception_translator(translator)`` and
``py::register_local_exception_translator(translator)`` can be used to register
functions that can translate arbitrary exception types (and which may include functions that can translate arbitrary exception types (and which may include
additional logic to do so). The function takes a stateless callable (e.g. a additional logic to do so). The functions takes a stateless callable (e.g. a
function pointer or a lambda function without captured variables) with the call function pointer or a lambda function without captured variables) with the call
signature ``void(std::exception_ptr)``. signature ``void(std::exception_ptr)``.
When a C++ exception is thrown, the registered exception translators are tried When a C++ exception is thrown, the registered exception translators are tried
in reverse order of registration (i.e. the last registered translator gets the in reverse order of registration (i.e. the last registered translator gets the
first shot at handling the exception). first shot at handling the exception). All local translators will be tried
before a global translator is tried.
Inside the translator, ``std::rethrow_exception`` should be used within Inside the translator, ``std::rethrow_exception`` should be used within
a try block to re-throw the exception. One or more catch clauses to catch a try block to re-throw the exception. One or more catch clauses to catch
...@@ -168,6 +179,53 @@ section. ...@@ -168,6 +179,53 @@ section.
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``. 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>`_. See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
Local vs Global Exception Translators
=====================================
When a global exception translator is registered, it will be applied across all
modules in the reverse order of registration. This can create behavior where the
order of module import influences how exceptions are translated.
If module1 has the following translator:
.. code-block:: cpp
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
PyErr_SetString("module1 handled this")
}
}
and module2 has the following similar translator:
.. code-block:: cpp
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
PyErr_SetString("module2 handled this")
}
}
then which translator handles the invalid_argument will be determined by the
order that module1 and module2 are imported. Since exception translators are
applied in the reverse order of registration, which ever module was imported
last will "win" and that translator will be applied.
If there are multiple pybind11 modules that share exception types (either
standard built-in or custom) loaded into a single python instance and
consistent error handling behavior is needed, then local translators should be
used.
Changing the previous example to use ``register_local_exception_translator``
would mean that when invalid_argument is thrown in the module2 code, the
module2 translator will always handle it, while in module1, the module1
translator will do the same.
.. _handling_python_exceptions_cpp: .. _handling_python_exceptions_cpp:
Handling exceptions from Python in C++ Handling exceptions from Python in C++
...@@ -265,6 +323,34 @@ Alternately, to ignore the error, call `PyErr_Clear ...@@ -265,6 +323,34 @@ Alternately, to ignore the error, call `PyErr_Clear
Any Python error must be thrown or cleared, or Python/pybind11 will be left in Any Python error must be thrown or cleared, or Python/pybind11 will be left in
an invalid state. an invalid state.
Chaining exceptions ('raise from')
==================================
In Python 3.3 a mechanism for indicating that exceptions were caused by other
exceptions was introduced:
.. code-block:: py
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("could not divide by zero") from exc
To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It
sets the current python error indicator, so to continue propagating the exception
you should ``throw py::error_already_set()`` (Python 3 only).
.. code-block:: cpp
try {
py::eval("print(1 / 0"));
} catch (py::error_already_set &e) {
py::raise_from(e, PyExc_RuntimeError, "could not divide by zero");
throw py::error_already_set();
}
.. versionadded:: 2.8
.. _unraisable_exceptions: .. _unraisable_exceptions:
Handling unraisable exceptions Handling unraisable exceptions
......
...@@ -272,7 +272,7 @@ And used in Python as usual: ...@@ -272,7 +272,7 @@ And used in Python as usual:
.. code-block:: pycon .. code-block:: pycon
>>> print_dict({'foo': 123, 'bar': 'hello'}) >>> print_dict({"foo": 123, "bar": "hello"})
key=foo, value=123 key=foo, value=123
key=bar, value=hello key=bar, value=hello
...@@ -377,10 +377,11 @@ argument in a function definition: ...@@ -377,10 +377,11 @@ argument in a function definition:
def f(a, *, b): # a can be positional or via keyword; b must be via keyword def f(a, *, b): # a can be positional or via keyword; b must be via keyword
pass pass
f(a=1, b=2) # good f(a=1, b=2) # good
f(b=2, a=1) # good f(b=2, a=1) # good
f(1, b=2) # good f(1, b=2) # good
f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given
Pybind11 provides a ``py::kw_only`` object that allows you to implement Pybind11 provides a ``py::kw_only`` object that allows you to implement
the same behaviour by specifying the object between positional and keyword-only the same behaviour by specifying the object between positional and keyword-only
......
...@@ -171,6 +171,31 @@ template parameter, and it ensures that non-conforming arguments are converted ...@@ -171,6 +171,31 @@ template parameter, and it ensures that non-conforming arguments are converted
into an array satisfying the specified requirements instead of trying the next into an array satisfying the specified requirements instead of trying the next
function overload. function overload.
There are several methods on arrays; the methods listed below under references
work, as well as the following functions based on the NumPy API:
- ``.dtype()`` returns the type of the contained values.
- ``.strides()`` returns a pointer to the strides of the array (optionally pass
an integer axis to get a number).
- ``.flags()`` returns the flag settings. ``.writable()`` and ``.owndata()``
are directly available.
- ``.offset_at()`` returns the offset (optionally pass indices).
- ``.squeeze()`` returns a view with length-1 axes removed.
- ``.view(dtype)`` returns a view of the array with a different dtype.
- ``.reshape({i, j, ...})`` returns a view of the array with a different shape.
``.resize({...})`` is also available.
- ``.index_at(i, j, ...)`` gets the count from the beginning to a given index.
There are also several methods for getting references (described below).
Structured types Structured types
================ ================
...@@ -233,8 +258,8 @@ by the compiler. The result is returned as a NumPy array of type ...@@ -233,8 +258,8 @@ by the compiler. The result is returned as a NumPy array of type
.. code-block:: pycon .. code-block:: pycon
>>> x = np.array([[1, 3],[5, 7]]) >>> x = np.array([[1, 3], [5, 7]])
>>> y = np.array([[2, 4],[6, 8]]) >>> y = np.array([[2, 4], [6, 8]])
>>> z = 3 >>> z = 3
>>> result = vectorized_func(x, y, z) >>> result = vectorized_func(x, y, z)
...@@ -345,21 +370,21 @@ The returned proxy object supports some of the same methods as ``py::array`` so ...@@ -345,21 +370,21 @@ The returned proxy object supports some of the same methods as ``py::array`` so
that it can be used as a drop-in replacement for some existing, index-checked that it can be used as a drop-in replacement for some existing, index-checked
uses of ``py::array``: uses of ``py::array``:
- ``r.ndim()`` returns the number of dimensions - ``.ndim()`` returns the number of dimensions
- ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to - ``.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to
the ``const T`` or ``T`` data, respectively, at the given indices. The the ``const T`` or ``T`` data, respectively, at the given indices. The
latter is only available to proxies obtained via ``a.mutable_unchecked()``. latter is only available to proxies obtained via ``a.mutable_unchecked()``.
- ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. - ``.itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``.
- ``ndim()`` returns the number of dimensions. - ``.ndim()`` returns the number of dimensions.
- ``shape(n)`` returns the size of dimension ``n`` - ``.shape(n)`` returns the size of dimension ``n``
- ``size()`` returns the total number of elements (i.e. the product of the shapes). - ``.size()`` returns the total number of elements (i.e. the product of the shapes).
- ``nbytes()`` returns the number of bytes used by the referenced elements - ``.nbytes()`` returns the number of bytes used by the referenced elements
(i.e. ``itemsize()`` times ``size()``). (i.e. ``itemsize()`` times ``size()``).
.. seealso:: .. seealso::
...@@ -378,7 +403,7 @@ In Python 2, the syntactic sugar ``...`` is not available, but the singleton ...@@ -378,7 +403,7 @@ In Python 2, the syntactic sugar ``...`` is not available, but the singleton
.. code-block:: python .. code-block:: python
a = # a NumPy array a = ... # a NumPy array
b = a[0, ..., 0] b = a[0, ..., 0]
The function ``py::ellipsis()`` function can be used to perform the same The function ``py::ellipsis()`` function can be used to perform the same
......
...@@ -20,6 +20,47 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`, ...@@ -20,6 +20,47 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`,
Be sure to review the :ref:`pytypes_gotchas` before using this heavily in Be sure to review the :ref:`pytypes_gotchas` before using this heavily in
your C++ API. your C++ API.
.. _instantiating_compound_types:
Instantiating compound Python types from C++
============================================
Dictionaries can be initialized in the :class:`dict` constructor:
.. code-block:: cpp
using namespace pybind11::literals; // to bring in the `_a` literal
py::dict d("spam"_a=py::none(), "eggs"_a=42);
A tuple of python objects can be instantiated using :func:`py::make_tuple`:
.. code-block:: cpp
py::tuple tup = py::make_tuple(42, py::none(), "spam");
Each element is converted to a supported Python type.
A `simple namespace`_ can be instantiated using
:func:`py::make_simple_namespace`:
.. code-block:: cpp
using namespace pybind11::literals; // to bring in the `_a` literal
py::object ns = py::make_simple_namespace("spam"_a=py::none(), "eggs"_a=42);
Attributes on a namespace can be modified with the :func:`py::delattr`,
:func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can
be useful as lightweight stand-ins for class instances.
.. note::
``make_simple_namespace`` is not available in Python 2.
.. versionchanged:: 2.8
``make_simple_namespace`` added.
.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
.. _casting_back_and_forth: .. _casting_back_and_forth:
Casting back and forth Casting back and forth
...@@ -30,7 +71,7 @@ types to Python, which can be done using :func:`py::cast`: ...@@ -30,7 +71,7 @@ types to Python, which can be done using :func:`py::cast`:
.. code-block:: cpp .. code-block:: cpp
MyClass *cls = ..; MyClass *cls = ...;
py::object obj = py::cast(cls); py::object obj = py::cast(cls);
The reverse direction uses the following syntax: The reverse direction uses the following syntax:
...@@ -132,6 +173,7 @@ Keyword arguments are also supported. In Python, there is the usual call syntax: ...@@ -132,6 +173,7 @@ Keyword arguments are also supported. In Python, there is the usual call syntax:
def f(number, say, to): def f(number, say, to):
... # function code ... # function code
f(1234, say="hello", to=some_instance) # keyword call in Python f(1234, say="hello", to=some_instance) # keyword call in Python
In C++, the same call can be made using: In C++, the same call can be made using:
......
...@@ -66,7 +66,7 @@ extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identica ...@@ -66,7 +66,7 @@ extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identica
except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with
`py::call_guard`, which allows multiple items, but uses the default constructor: `py::call_guard`, which allows multiple items, but uses the default constructor:
.. code-block:: py .. code-block:: cpp
// Alternative: Call single function using call guard // Alternative: Call single function using call guard
m.def("noisy_func", &call_noisy_function, m.def("noisy_func", &call_noisy_function,
......
...@@ -77,6 +77,7 @@ segmentation fault). ...@@ -77,6 +77,7 @@ segmentation fault).
.. code-block:: python .. code-block:: python
from example import Parent from example import Parent
print(Parent().get_child()) print(Parent().get_child())
The problem is that ``Parent::get_child()`` returns a pointer to an instance of The problem is that ``Parent::get_child()`` returns a pointer to an instance of
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import random
import os
import time
import datetime as dt import datetime as dt
import os
import random
nfns = 4 # Functions per class nfns = 4 # Functions per class
nargs = 4 # Arguments per function nargs = 4 # Arguments per function
......
...@@ -6,8 +6,13 @@ Changelog ...@@ -6,8 +6,13 @@ Changelog
Starting with version 1.8.0, pybind11 releases use a `semantic versioning Starting with version 1.8.0, pybind11 releases use a `semantic versioning
<http://semver.org>`_ policy. <http://semver.org>`_ policy.
v2.8.0 (WIP) v2.8.0 (Oct 4, 2021)
------------ --------------------
New features:
* Added ``py::raise_from`` to enable chaining exceptions.
`#3215 <https://github.com/pybind/pybind11/pull/3215>`_
* Allow exception translators to be optionally registered local to a module * Allow exception translators to be optionally registered local to a module
instead of applying globally across all pybind11 modules. Use instead of applying globally across all pybind11 modules. Use
...@@ -16,6 +21,158 @@ v2.8.0 (WIP) ...@@ -16,6 +21,158 @@ v2.8.0 (WIP)
translator)`` to keep your exception remapping code local to the module. translator)`` to keep your exception remapping code local to the module.
`#2650 <https://github.com/pybind/pybind11/pull/2650>`_ `#2650 <https://github.com/pybind/pybind11/pull/2650>`_
* Add ``make_simple_namespace`` function for instantiating Python
``SimpleNamespace`` objects.
`#2840 <https://github.com/pybind/pybind11/pull/2840>`_
* ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new
arguments to allow ``sys.argv`` initialization.
`#2341 <https://github.com/pybind/pybind11/pull/2341>`_
* Allow Python builtins to be used as callbacks in CPython.
`#1413 <https://github.com/pybind/pybind11/pull/1413>`_
* Added ``view`` to view arrays with a different datatype.
`#987 <https://github.com/pybind/pybind11/pull/987>`_
* Implemented ``reshape`` on arrays.
`#984 <https://github.com/pybind/pybind11/pull/984>`_
* Enable defining custom ``__new__`` methods on classes by fixing bug
preventing overriding methods if they have non-pybind11 siblings.
`#3265 <https://github.com/pybind/pybind11/pull/3265>`_
* Add ``make_value_iterator()``, and fix ``make_key_iterator()`` to return
references instead of copies.
`#3293 <https://github.com/pybind/pybind11/pull/3293>`_
* Improve the classes generated by ``bind_map``: `#3310 <https://github.com/pybind/pybind11/pull/3310>`_
* Change ``.items`` from an iterator to a dictionary view.
* Add ``.keys`` and ``.values`` (both dictionary views).
* Allow ``__contains__`` to take any object.
* ``pybind11::custom_type_setup`` was added, for customizing the
``PyHeapTypeObject`` corresponding to a class, which may be useful for
enabling garbage collection support, among other things.
`#3287 <https://github.com/pybind/pybind11/pull/3287>`_
Changes:
* Set ``__file__`` constant when running ``eval_file`` in an embedded interpreter.
`#3233 <https://github.com/pybind/pybind11/pull/3233>`_
* Python objects and (C++17) ``std::optional`` now accepted in ``py::slice``
constructor.
`#1101 <https://github.com/pybind/pybind11/pull/1101>`_
* The pybind11 proxy types ``str``, ``bytes``, ``bytearray``, ``tuple``,
``list`` now consistently support passing ``ssize_t`` values for sizes and
indexes. Previously, only ``size_t`` was accepted in several interfaces.
`#3219 <https://github.com/pybind/pybind11/pull/3219>`_
* Avoid evaluating ``PYBIND11_TLS_REPLACE_VALUE`` arguments more than once.
`#3290 <https://github.com/pybind/pybind11/pull/3290>`_
Fixes:
* Bug fix: enum value's ``__int__`` returning non-int when underlying type is
bool or of char type.
`#1334 <https://github.com/pybind/pybind11/pull/1334>`_
* Fixes bug in setting error state in Capsule's pointer methods.
`#3261 <https://github.com/pybind/pybind11/pull/3261>`_
* A long-standing memory leak in ``py::cpp_function::initialize`` was fixed.
`#3229 <https://github.com/pybind/pybind11/pull/3229>`_
* Fixes thread safety for some ``pybind11::type_caster`` which require lifetime
extension, such as for ``std::string_view``.
`#3237 <https://github.com/pybind/pybind11/pull/3237>`_
* Restore compatibility with gcc 4.8.4 as distributed by ubuntu-trusty, linuxmint-17.
`#3270 <https://github.com/pybind/pybind11/pull/3270>`_
Build system improvements:
* Fix regression in CMake Python package config: improper use of absolute path.
`#3144 <https://github.com/pybind/pybind11/pull/3144>`_
* Cached Python version information could become stale when CMake was re-run
with a different Python version. The build system now detects this and
updates this information.
`#3299 <https://github.com/pybind/pybind11/pull/3299>`_
* Specified UTF8-encoding in setup.py calls of open().
`#3137 <https://github.com/pybind/pybind11/pull/3137>`_
* Fix a harmless warning from CMake 3.21 with the classic Python discovery.
`#3220 <https://github.com/pybind/pybind11/pull/3220>`_
* Eigen repo and version can now be specified as cmake options.
`#3324 <https://github.com/pybind/pybind11/pull/3324>`_
Backend and tidying up:
* Reduced thread-local storage required for keeping alive temporary data for
type conversion to one key per ABI version, rather than one key per extension
module. This makes the total thread-local storage required by pybind11 2
keys per ABI version.
`#3275 <https://github.com/pybind/pybind11/pull/3275>`_
* Optimize NumPy array construction with additional moves.
`#3183 <https://github.com/pybind/pybind11/pull/3183>`_
* Conversion to ``std::string`` and ``std::string_view`` now avoids making an
extra copy of the data on Python >= 3.3.
`#3257 <https://github.com/pybind/pybind11/pull/3257>`_
* Remove const modifier from certain C++ methods on Python collections
(``list``, ``set``, ``dict``) such as (``clear()``, ``append()``,
``insert()``, etc...) and annotated them with ``py-non-const``.
* Enable readability ``clang-tidy-const-return`` and remove useless consts.
`#3254 <https://github.com/pybind/pybind11/pull/3254>`_
`#3194 <https://github.com/pybind/pybind11/pull/3194>`_
* The clang-tidy ``google-explicit-constructor`` option was enabled.
`#3250 <https://github.com/pybind/pybind11/pull/3250>`_
* Mark a pytype move constructor as noexcept (perf).
`#3236 <https://github.com/pybind/pybind11/pull/3236>`_
* Enable clang-tidy check to guard against inheritance slicing.
`#3210 <https://github.com/pybind/pybind11/pull/3210>`_
* Legacy warning suppression pragma were removed from eigen.h. On Unix
platforms, please use -isystem for Eigen include directories, to suppress
compiler warnings originating from Eigen headers. Note that CMake does this
by default. No adjustments are needed for Windows.
`#3198 <https://github.com/pybind/pybind11/pull/3198>`_
* Format pybind11 with isort consistent ordering of imports
`#3195 <https://github.com/pybind/pybind11/pull/3195>`_
* The warnings-suppression "pragma clamp" at the top/bottom of pybind11 was
removed, clearing the path to refactoring and IWYU cleanup.
`#3186 <https://github.com/pybind/pybind11/pull/3186>`_
* Enable most bugprone checks in clang-tidy and fix the found potential bugs
and poor coding styles.
`#3166 <https://github.com/pybind/pybind11/pull/3166>`_
* Add ``clang-tidy-readability`` rules to make boolean casts explicit improving
code readability. Also enabled other misc and readability clang-tidy checks.
`#3148 <https://github.com/pybind/pybind11/pull/3148>`_
* Move object in ``.pop()`` for list.
`#3116 <https://github.com/pybind/pybind11/pull/3116>`_
v2.7.1 (Aug 3, 2021) v2.7.1 (Aug 3, 2021)
--------------------- ---------------------
...@@ -1028,6 +1185,7 @@ v2.2.0 (August 31, 2017) ...@@ -1028,6 +1185,7 @@ v2.2.0 (August 31, 2017)
from cpp_module import CppBase1, CppBase2 from cpp_module import CppBase1, CppBase2
class PyDerived(CppBase1, CppBase2): class PyDerived(CppBase1, CppBase2):
def __init__(self): def __init__(self):
CppBase1.__init__(self) # C++ bases must be initialized explicitly CppBase1.__init__(self) # C++ bases must be initialized explicitly
......
...@@ -44,12 +44,12 @@ interactive Python session demonstrating this example is shown below: ...@@ -44,12 +44,12 @@ interactive Python session demonstrating this example is shown below:
% python % python
>>> import example >>> import example
>>> p = example.Pet('Molly') >>> p = example.Pet("Molly")
>>> print(p) >>> print(p)
<example.Pet object at 0x10cd98060> <example.Pet object at 0x10cd98060>
>>> p.getName() >>> p.getName()
u'Molly' u'Molly'
>>> p.setName('Charly') >>> p.setName("Charly")
>>> p.getName() >>> p.getName()
u'Charly' u'Charly'
...@@ -122,10 +122,10 @@ This makes it possible to write ...@@ -122,10 +122,10 @@ This makes it possible to write
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet('Molly') >>> p = example.Pet("Molly")
>>> p.name >>> p.name
u'Molly' u'Molly'
>>> p.name = 'Charly' >>> p.name = "Charly"
>>> p.name >>> p.name
u'Charly' u'Charly'
...@@ -174,10 +174,10 @@ Native Python classes can pick up new attributes dynamically: ...@@ -174,10 +174,10 @@ Native Python classes can pick up new attributes dynamically:
.. code-block:: pycon .. code-block:: pycon
>>> class Pet: >>> class Pet:
... name = 'Molly' ... name = "Molly"
... ...
>>> p = Pet() >>> p = Pet()
>>> p.name = 'Charly' # overwrite existing >>> p.name = "Charly" # overwrite existing
>>> p.age = 2 # dynamically add a new attribute >>> p.age = 2 # dynamically add a new attribute
By default, classes exported from C++ do not support this and the only writable By default, classes exported from C++ do not support this and the only writable
...@@ -195,7 +195,7 @@ Trying to set any other attribute results in an error: ...@@ -195,7 +195,7 @@ Trying to set any other attribute results in an error:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet() >>> p = example.Pet()
>>> p.name = 'Charly' # OK, attribute defined in C++ >>> p.name = "Charly" # OK, attribute defined in C++
>>> p.age = 2 # fail >>> p.age = 2 # fail
AttributeError: 'Pet' object has no attribute 'age' AttributeError: 'Pet' object has no attribute 'age'
...@@ -213,7 +213,7 @@ Now everything works as expected: ...@@ -213,7 +213,7 @@ Now everything works as expected:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet() >>> p = example.Pet()
>>> p.name = 'Charly' # OK, overwrite value in C++ >>> p.name = "Charly" # OK, overwrite value in C++
>>> p.age = 2 # OK, dynamically add a new attribute >>> p.age = 2 # OK, dynamically add a new attribute
>>> p.__dict__ # just like a native Python class >>> p.__dict__ # just like a native Python class
{'age': 2} {'age': 2}
...@@ -280,7 +280,7 @@ expose fields and methods of both types: ...@@ -280,7 +280,7 @@ expose fields and methods of both types:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Dog('Molly') >>> p = example.Dog("Molly")
>>> p.name >>> p.name
u'Molly' u'Molly'
>>> p.bark() >>> p.bark()
...@@ -446,8 +446,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth ...@@ -446,8 +446,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
Enumerations and internal types Enumerations and internal types
=============================== ===============================
Let's now suppose that the example class contains an internal enumeration type, Let's now suppose that the example class contains internal types like enumerations, e.g.:
e.g.:
.. code-block:: cpp .. code-block:: cpp
...@@ -457,10 +456,15 @@ e.g.: ...@@ -457,10 +456,15 @@ e.g.:
Cat Cat
}; };
struct Attributes {
float age = 0;
};
Pet(const std::string &name, Kind type) : name(name), type(type) { } Pet(const std::string &name, Kind type) : name(name), type(type) { }
std::string name; std::string name;
Kind type; Kind type;
Attributes attr;
}; };
The binding code for this example looks as follows: The binding code for this example looks as follows:
...@@ -471,22 +475,28 @@ The binding code for this example looks as follows: ...@@ -471,22 +475,28 @@ The binding code for this example looks as follows:
pet.def(py::init<const std::string &, Pet::Kind>()) pet.def(py::init<const std::string &, Pet::Kind>())
.def_readwrite("name", &Pet::name) .def_readwrite("name", &Pet::name)
.def_readwrite("type", &Pet::type); .def_readwrite("type", &Pet::type)
.def_readwrite("attr", &Pet::attr);
py::enum_<Pet::Kind>(pet, "Kind") py::enum_<Pet::Kind>(pet, "Kind")
.value("Dog", Pet::Kind::Dog) .value("Dog", Pet::Kind::Dog)
.value("Cat", Pet::Kind::Cat) .value("Cat", Pet::Kind::Cat)
.export_values(); .export_values();
To ensure that the ``Kind`` type is created within the scope of ``Pet``, the py::class_<Pet::Attributes> attributes(pet, "Attributes")
``pet`` :class:`class_` instance must be supplied to the :class:`enum_`. .def(py::init<>())
.def_readwrite("age", &Pet::Attributes::age);
To ensure that the nested types ``Kind`` and ``Attributes`` are created within the scope of ``Pet``, the
``pet`` :class:`class_` instance must be supplied to the :class:`enum_` and :class:`class_`
constructor. The :func:`enum_::export_values` function exports the enum entries constructor. The :func:`enum_::export_values` function exports the enum entries
into the parent scope, which should be skipped for newer C++11-style strongly into the parent scope, which should be skipped for newer C++11-style strongly
typed enums. typed enums.
.. code-block:: pycon .. code-block:: pycon
>>> p = Pet('Lucy', Pet.Cat) >>> p = Pet("Lucy", Pet.Cat)
>>> p.type >>> p.type
Kind.Cat Kind.Cat
>>> int(p.type) >>> int(p.type)
...@@ -508,7 +518,7 @@ The ``name`` property returns the name of the enum value as a unicode string. ...@@ -508,7 +518,7 @@ The ``name`` property returns the name of the enum value as a unicode string.
.. code-block:: pycon .. code-block:: pycon
>>> p = Pet( "Lucy", Pet.Cat ) >>> p = Pet("Lucy", Pet.Cat)
>>> pet_type = p.type >>> pet_type = p.type
>>> pet_type >>> pet_type
Pet.Cat Pet.Cat
......
...@@ -42,10 +42,7 @@ An example of a ``setup.py`` using pybind11's helpers: ...@@ -42,10 +42,7 @@ An example of a ``setup.py`` using pybind11's helpers:
), ),
] ]
setup( setup(..., ext_modules=ext_modules)
...,
ext_modules=ext_modules
)
If you want to do an automatic search for the highest supported C++ standard, If you want to do an automatic search for the highest supported C++ standard,
that is supported via a ``build_ext`` command override; it will only affect that is supported via a ``build_ext`` command override; it will only affect
...@@ -64,11 +61,7 @@ that is supported via a ``build_ext`` command override; it will only affect ...@@ -64,11 +61,7 @@ that is supported via a ``build_ext`` command override; it will only affect
), ),
] ]
setup( setup(..., cmdclass={"build_ext": build_ext}, ext_modules=ext_modules)
...,
cmdclass={"build_ext": build_ext},
ext_modules=ext_modules
)
If you have single-file extension modules that are directly stored in the If you have single-file extension modules that are directly stored in the
Python source tree (``foo.cpp`` in the same directory as where a ``foo.py`` Python source tree (``foo.cpp`` in the same directory as where a ``foo.py``
...@@ -113,7 +106,7 @@ with the following: ...@@ -113,7 +106,7 @@ with the following:
from pybind11.setup_helpers import ParallelCompile, naive_recompile from pybind11.setup_helpers import ParallelCompile, naive_recompile
SmartCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install()
If you have a more complex build, you can implement a smarter function and pass If you have a more complex build, you can implement a smarter function and pass
......
...@@ -13,12 +13,11 @@ ...@@ -13,12 +13,11 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import sys
import os import os
import shlex import re
import subprocess import subprocess
import sys
from pathlib import Path from pathlib import Path
import re
DIR = Path(__file__).parent.resolve() DIR = Path(__file__).parent.resolve()
......
...@@ -54,7 +54,7 @@ provided by the caller -- in fact, it does nothing at all. ...@@ -54,7 +54,7 @@ provided by the caller -- in fact, it does nothing at all.
.. code-block:: python .. code-block:: python
def increment(i): def increment(i):
i += 1 # nope.. i += 1 # nope..
pybind11 is also affected by such language-level conventions, which means that pybind11 is also affected by such language-level conventions, which means that
binding ``increment`` or ``increment_ptr`` will also create Python functions binding ``increment`` or ``increment_ptr`` will also create Python functions
......
...@@ -63,6 +63,9 @@ Convenience functions converting to Python types ...@@ -63,6 +63,9 @@ Convenience functions converting to Python types
.. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...) .. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_key_iterator(Type &, Extra&&...) .. doxygenfunction:: make_key_iterator(Type &, Extra&&...)
.. doxygenfunction:: make_value_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_value_iterator(Type &, Extra&&...)
.. _extras: .. _extras:
Passing extra arguments to ``def`` or ``class_`` Passing extra arguments to ``def`` or ``class_``
......
...@@ -12,13 +12,17 @@ ...@@ -12,13 +12,17 @@
#include "cast.h" #include "cast.h"
#include <functional>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/// \addtogroup annotations /// \addtogroup annotations
/// @{ /// @{
/// Annotation for methods /// Annotation for methods
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; struct is_method { handle class_;
explicit is_method(const handle &c) : class_(c) {}
};
/// Annotation for operators /// Annotation for operators
struct is_operator { }; struct is_operator { };
...@@ -27,16 +31,24 @@ struct is_operator { }; ...@@ -27,16 +31,24 @@ struct is_operator { };
struct is_final { }; struct is_final { };
/// Annotation for parent scope /// Annotation for parent scope
struct scope { handle value; scope(const handle &s) : value(s) { } }; struct scope { handle value;
explicit scope(const handle &s) : value(s) {}
};
/// Annotation for documentation /// Annotation for documentation
struct doc { const char *value; doc(const char *value) : value(value) { } }; struct doc { const char *value;
explicit doc(const char *value) : value(value) {}
};
/// Annotation for function names /// Annotation for function names
struct name { const char *value; name(const char *value) : value(value) { } }; struct name { const char *value;
explicit name(const char *value) : value(value) {}
};
/// Annotation indicating that a function is an overload associated with a given "sibling" /// Annotation indicating that a function is an overload associated with a given "sibling"
struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; struct sibling { handle value;
explicit sibling(const handle &value) : value(value.ptr()) {}
};
/// Annotation indicating that a class derives from another given type /// Annotation indicating that a class derives from another given type
template <typename T> struct base { template <typename T> struct base {
...@@ -69,8 +81,27 @@ struct metaclass { ...@@ -69,8 +81,27 @@ struct metaclass {
explicit metaclass(handle value) : value(value) { } explicit metaclass(handle value) : value(value) { }
}; };
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
/// may be used to customize the Python type.
///
/// The callback is invoked immediately before `PyType_Ready`.
///
/// Note: This is an advanced interface, and uses of it may require changes to
/// work with later versions of pybind11. You may wish to consult the
/// implementation of `make_new_python_type` in `detail/classes.h` to understand
/// the context in which the callback will be run.
struct custom_type_setup {
using callback = std::function<void(PyHeapTypeObject *heap_type)>;
explicit custom_type_setup(callback value) : value(std::move(value)) {}
callback value;
};
/// Annotation that marks a class as local to the module: /// Annotation that marks a class as local to the module:
struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; struct module_local { const bool value;
constexpr explicit module_local(bool v = true) : value(v) {}
};
/// Annotation to mark enums as an arithmetic type /// Annotation to mark enums as an arithmetic type
struct arithmetic { }; struct arithmetic { };
...@@ -124,7 +155,7 @@ enum op_id : int; ...@@ -124,7 +155,7 @@ enum op_id : int;
enum op_type : int; enum op_type : int;
struct undefined_t; struct undefined_t;
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_; template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
/// Internal data structure which holds metadata about a keyword argument /// Internal data structure which holds metadata about a keyword argument
struct argument_record { struct argument_record {
...@@ -260,6 +291,9 @@ struct type_record { ...@@ -260,6 +291,9 @@ struct type_record {
/// Custom metaclass (optional) /// Custom metaclass (optional)
handle metaclass; handle metaclass;
/// Custom type setup.
custom_type_setup::callback custom_type_setup_callback;
/// Multiple inheritance marker /// Multiple inheritance marker
bool multiple_inheritance : 1; bool multiple_inheritance : 1;
...@@ -465,6 +499,13 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr> ...@@ -465,6 +499,13 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
}; };
template <> template <>
struct process_attribute<custom_type_setup> {
static void init(const custom_type_setup &value, type_record *r) {
r->custom_type_setup_callback = value.value;
}
};
template <>
struct process_attribute<is_final> : process_attribute_default<is_final> { struct process_attribute<is_final> : process_attribute_default<is_final> {
static void init(const is_final &, type_record *r) { r->is_final = true; } static void init(const is_final &, type_record *r) { r->is_final = true; }
}; };
...@@ -516,20 +557,31 @@ template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurs ...@@ -516,20 +557,31 @@ template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurs
/// Recursively iterate over variadic template arguments /// Recursively iterate over variadic template arguments
template <typename... Args> struct process_attributes { template <typename... Args> struct process_attributes {
static void init(const Args&... args, function_record *r) { static void init(const Args&... args, function_record *r) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
ignore_unused(unused); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
using expander = int[];
(void) expander{
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
} }
static void init(const Args&... args, type_record *r) { static void init(const Args&... args, type_record *r) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
ignore_unused(unused); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
using expander = int[];
(void) expander{0,
(process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
} }
static void precall(function_call &call) { static void precall(function_call &call) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(call), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
ignore_unused(unused); using expander = int[];
(void) expander{0,
(process_attribute<typename std::decay<Args>::type>::precall(call), 0)...};
} }
static void postcall(function_call &call, handle fn_ret) { static void postcall(function_call &call, handle fn_ret) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
ignore_unused(unused); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret);
using expander = int[];
(void) expander{
0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0)...};
} }
}; };
...@@ -545,6 +597,7 @@ template <typename... Extra, ...@@ -545,6 +597,7 @@ template <typename... Extra,
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...), size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)> size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
} }
......
...@@ -82,7 +82,7 @@ public: ...@@ -82,7 +82,7 @@ public:
return caster_t::cast(&src.get(), policy, parent); return caster_t::cast(&src.get(), policy, parent);
} }
template <typename T> using cast_op_type = std::reference_wrapper<type>; template <typename T> using cast_op_type = std::reference_wrapper<type>;
operator std::reference_wrapper<type>() { return cast_op<type &>(subcaster); } explicit operator std::reference_wrapper<type>() { return cast_op<type &>(subcaster); }
}; };
#define PYBIND11_TYPE_CASTER(type, py_name) \ #define PYBIND11_TYPE_CASTER(type, py_name) \
...@@ -102,9 +102,9 @@ public: ...@@ -102,9 +102,9 @@ public:
} \ } \
return cast(*src, policy, parent); \ return cast(*src, policy, parent); \
} \ } \
operator type *() { return &value; } \ operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \
operator type &() { return value; } \ operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \
operator type &&() && { return std::move(value); } \ operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \
template <typename T_> \ template <typename T_> \
using cast_op_type = pybind11::detail::movable_cast_op_type<T_> using cast_op_type = pybind11::detail::movable_cast_op_type<T_>
...@@ -145,9 +145,8 @@ public: ...@@ -145,9 +145,8 @@ public:
py_value = (py_type) PyFloat_AsDouble(src.ptr()); py_value = (py_type) PyFloat_AsDouble(src.ptr());
else else
return false; return false;
} else if (PyFloat_Check(src.ptr())) { } else if (PyFloat_Check(src.ptr())
return false; || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) {
} else if (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr())) {
return false; return false;
} else { } else {
handle src_or_index = src; handle src_or_index = src;
...@@ -280,7 +279,7 @@ public: ...@@ -280,7 +279,7 @@ public:
} }
template <typename T> using cast_op_type = void*&; template <typename T> using cast_op_type = void*&;
operator void *&() { return value; } explicit operator void *&() { return value; }
static constexpr auto name = _("capsule"); static constexpr auto name = _("capsule");
private: private:
void *value = nullptr; void *value = nullptr;
...@@ -378,13 +377,33 @@ template <typename StringType, bool IsView = false> struct string_caster { ...@@ -378,13 +377,33 @@ template <typename StringType, bool IsView = false> struct string_caster {
#endif #endif
} }
#if PY_VERSION_HEX >= 0x03030000
// On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes`
// object by using `PyUnicode_AsUTF8AndSize`.
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) {
Py_ssize_t size = -1;
const auto *buffer
= reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size));
if (!buffer) {
PyErr_Clear();
return false;
}
value = StringType(buffer, static_cast<size_t>(size));
return true;
}
#endif
auto utfNbytes = reinterpret_steal<object>(PyUnicode_AsEncodedString( auto utfNbytes = reinterpret_steal<object>(PyUnicode_AsEncodedString(
load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr));
if (!utfNbytes) { PyErr_Clear(); return false; } if (!utfNbytes) { PyErr_Clear(); return false; }
const auto *buffer = reinterpret_cast<const CharT *>(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); const auto *buffer = 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);
if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 // Skip BOM for UTF-16/32
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) {
buffer++;
length--;
}
value = StringType(buffer, length); value = StringType(buffer, length);
// If we're loading a string_view we need to keep the encoded Python object alive: // If we're loading a string_view we need to keep the encoded Python object alive:
...@@ -484,8 +503,10 @@ public: ...@@ -484,8 +503,10 @@ public:
return StringCaster::cast(StringType(1, src), policy, parent); return StringCaster::cast(StringType(1, src), policy, parent);
} }
operator CharT*() { return none ? nullptr : const_cast<CharT *>(static_cast<StringType &>(str_caster).c_str()); } explicit operator CharT *() {
operator CharT&() { return none ? nullptr : const_cast<CharT *>(static_cast<StringType &>(str_caster).c_str());
}
explicit operator CharT &() {
if (none) if (none)
throw value_error("Cannot convert None to a character"); throw value_error("Cannot convert None to a character");
...@@ -499,7 +520,7 @@ public: ...@@ -499,7 +520,7 @@ public:
// out how long the first encoded character is in bytes to distinguish between these two // out how long the first encoded character is in bytes to distinguish between these two
// errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those
// can fit into a single char value. // can fit into a single char value.
if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { if (PYBIND11_SILENCE_MSVC_C4127(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
...@@ -524,7 +545,7 @@ public: ...@@ -524,7 +545,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 (StringCaster::UTF_N == 16 && str_len == 2) { else if (PYBIND11_SILENCE_MSVC_C4127(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)");
...@@ -578,8 +599,8 @@ public: ...@@ -578,8 +599,8 @@ public:
template <typename T> using cast_op_type = type; template <typename T> using cast_op_type = type;
operator type() & { return implicit_cast(indices{}); } explicit operator type() & { return implicit_cast(indices{}); }
operator type() && { return std::move(*this).implicit_cast(indices{}); } explicit operator type() && { return std::move(*this).implicit_cast(indices{}); }
protected: protected:
template <size_t... Is> template <size_t... Is>
...@@ -605,6 +626,8 @@ protected: ...@@ -605,6 +626,8 @@ protected:
/* Implementation: Convert a C++ tuple into a Python tuple */ /* Implementation: Convert a C++ tuple into a Python tuple */
template <typename T, size_t... Is> template <typename T, size_t... Is>
static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence<Is...>) { static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence<Is...>) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(src, policy, parent);
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(policy, parent);
std::array<object, size> entries{{ std::array<object, size> entries{{
reinterpret_steal<object>(make_caster<Ts>::cast(std::get<Is>(std::forward<T>(src)), policy, parent))... reinterpret_steal<object>(make_caster<Ts>::cast(std::get<Is>(std::forward<T>(src)), policy, parent))...
}}; }};
...@@ -777,7 +800,7 @@ struct pyobject_caster { ...@@ -777,7 +800,7 @@ struct pyobject_caster {
// For Python 2, without this implicit conversion, Python code would // For Python 2, without this implicit conversion, Python code would
// need to be cluttered with six.ensure_text() or similar, only to be // need to be cluttered with six.ensure_text() or similar, only to be
// un-cluttered later after Python 2 support is dropped. // un-cluttered later after Python 2 support is dropped.
if (std::is_same<T, str>::value && isinstance<bytes>(src)) { if (PYBIND11_SILENCE_MSVC_C4127(std::is_same<T, str>::value) && isinstance<bytes>(src)) {
PyObject *str_from_bytes = PyUnicode_FromEncodedObject(src.ptr(), "utf-8", nullptr); PyObject *str_from_bytes = PyUnicode_FromEncodedObject(src.ptr(), "utf-8", nullptr);
if (!str_from_bytes) throw error_already_set(); if (!str_from_bytes) throw error_already_set();
value = reinterpret_steal<type>(str_from_bytes); value = reinterpret_steal<type>(str_from_bytes);
...@@ -1013,6 +1036,16 @@ template <return_value_policy policy = return_value_policy::automatic_reference, ...@@ -1013,6 +1036,16 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
return result; return result;
} }
#if PY_VERSION_HEX >= 0x03030000
template <typename... Args,
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
object make_simple_namespace(Args&&... args_) {
PyObject *ns = _PyNamespace_New(dict(std::forward<Args>(args_)...).ptr());
if (!ns) throw error_already_set();
return reinterpret_steal<object>(ns);
}
#endif
/// \ingroup annotations /// \ingroup annotations
/// Annotation for arguments /// Annotation for arguments
struct arg { struct arg {
...@@ -1161,13 +1194,14 @@ public: ...@@ -1161,13 +1194,14 @@ public:
} }
template <typename Return, typename Guard, typename Func> template <typename Return, typename Guard, typename Func>
// NOLINTNEXTLINE(readability-const-return-type)
enable_if_t<!std::is_void<Return>::value, Return> call(Func &&f) && { enable_if_t<!std::is_void<Return>::value, Return> call(Func &&f) && {
return std::move(*this).template call_impl<Return>(std::forward<Func>(f), indices{}, Guard{}); return std::move(*this).template call_impl<remove_cv_t<Return>>(std::forward<Func>(f), indices{}, Guard{});
} }
template <typename Return, typename Guard, typename Func> template <typename Return, typename Guard, typename Func>
enable_if_t<std::is_void<Return>::value, void_type> call(Func &&f) && { enable_if_t<std::is_void<Return>::value, void_type> call(Func &&f) && {
std::move(*this).template call_impl<Return>(std::forward<Func>(f), indices{}, Guard{}); std::move(*this).template call_impl<remove_cv_t<Return>>(std::forward<Func>(f), indices{}, Guard{});
return void_type(); return void_type();
} }
...@@ -1231,8 +1265,8 @@ public: ...@@ -1231,8 +1265,8 @@ public:
// Tuples aren't (easily) resizable so a list is needed for collection, // Tuples aren't (easily) resizable so a list is needed for collection,
// but the actual function call strictly requires a tuple. // but the actual function call strictly requires a tuple.
auto args_list = list(); auto args_list = list();
int _[] = { 0, (process(args_list, std::forward<Ts>(values)), 0)... }; using expander = int[];
ignore_unused(_); (void) expander{0, (process(args_list, std::forward<Ts>(values)), 0)...};
m_args = std::move(args_list); m_args = std::move(args_list);
} }
......
...@@ -210,7 +210,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) { ...@@ -210,7 +210,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
internals.direct_conversions.erase(tindex); internals.direct_conversions.erase(tindex);
if (tinfo->module_local) if (tinfo->module_local)
registered_local_types_cpp().erase(tindex); get_local_internals().registered_types_cpp.erase(tindex);
else else
internals.registered_types_cpp.erase(tindex); internals.registered_types_cpp.erase(tindex);
internals.registered_types_py.erase(tinfo->type); internals.registered_types_py.erase(tinfo->type);
...@@ -683,11 +683,13 @@ inline PyObject* make_new_python_type(const type_record &rec) { ...@@ -683,11 +683,13 @@ inline PyObject* make_new_python_type(const type_record &rec) {
if (rec.buffer_protocol) if (rec.buffer_protocol)
enable_buffer_protocol(heap_type); enable_buffer_protocol(heap_type);
if (rec.custom_type_setup_callback)
rec.custom_type_setup_callback(heap_type);
if (PyType_Ready(type) < 0) if (PyType_Ready(type) < 0)
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
: !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
/* Register type with the parent scope */ /* Register type with the parent scope */
if (rec.scope) if (rec.scope)
......
...@@ -26,12 +26,14 @@ struct descr { ...@@ -26,12 +26,14 @@ struct descr {
char text[N + 1]{'\0'}; char text[N + 1]{'\0'};
constexpr descr() = default; constexpr descr() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { } constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
template <size_t... Is> template <size_t... Is>
constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { } constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { }
template <typename... Chars> template <typename... Chars>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { } constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { }
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() { static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
...@@ -42,6 +44,7 @@ struct descr { ...@@ -42,6 +44,7 @@ struct descr {
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2> template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b, constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b,
index_sequence<Is1...>, index_sequence<Is2...>) { index_sequence<Is1...>, index_sequence<Is2...>) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
return {a.text[Is1]..., b.text[Is2]...}; return {a.text[Is1]..., b.text[Is2]...};
} }
...@@ -74,7 +77,8 @@ constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; } ...@@ -74,7 +77,8 @@ constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; }
template <bool B, typename T1, typename T2> template <bool B, typename T1, typename T2>
constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; } constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; }
template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) { template <size_t Size>
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
return int_to_str<Size / 10, Size % 10>::digits; return int_to_str<Size / 10, Size % 10>::digits;
} }
......
...@@ -23,7 +23,7 @@ public: ...@@ -23,7 +23,7 @@ public:
} }
template <typename> using cast_op_type = value_and_holder &; template <typename> using cast_op_type = value_and_holder &;
operator value_and_holder &() { return *value; } explicit operator value_and_holder &() { return *value; }
static constexpr auto name = _<value_and_holder>(); static constexpr auto name = _<value_and_holder>();
private: private:
...@@ -94,8 +94,9 @@ void construct(...) { ...@@ -94,8 +94,9 @@ void construct(...) {
// construct an Alias from the returned base instance. // construct an Alias from the returned base instance.
template <typename Class> 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);
no_nullptr(ptr); no_nullptr(ptr);
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { if (PYBIND11_SILENCE_MSVC_C4127(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
...@@ -131,10 +132,11 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) { ...@@ -131,10 +132,11 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
// derived type (through those holder's implicit conversion from derived class holder constructors). // derived type (through those holder's implicit conversion from derived class holder constructors).
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(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 (Class::has_alias && need_alias && !is_alias<Class>(ptr)) if (PYBIND11_SILENCE_MSVC_C4127(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");
...@@ -148,9 +150,10 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { ...@@ -148,9 +150,10 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
// need it, we simply move-construct the cpp value into a new instance. // need it, we simply move-construct the cpp value into a new instance.
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
static_assert(std::is_move_constructible<Cpp<Class>>::value, static_assert(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 (Class::has_alias && need_alias) if (PYBIND11_SILENCE_MSVC_C4127(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));
...@@ -219,7 +222,8 @@ template <typename Func, typename Return, typename... Args> ...@@ -219,7 +222,8 @@ template <typename Func, typename Return, typename... Args>
struct factory<Func, void_type (*)(), Return(Args...)> { struct factory<Func, void_type (*)(), Return(Args...)> {
remove_reference_t<Func> class_factory; remove_reference_t<Func> class_factory;
factory(Func &&f) : class_factory(std::forward<Func>(f)) { } // NOLINTNEXTLINE(google-explicit-constructor)
factory(Func &&f) : class_factory(std::forward<Func>(f)) {}
// The given class either has no alias or has no separate alias factory; // The given class either has no alias or has no separate alias factory;
// this always constructs the class itself. If the class is registered with an alias // this always constructs the class itself. If the class is registered with an alias
......
...@@ -29,7 +29,7 @@ inline void erase_all(std::string &string, const std::string &search) { ...@@ -29,7 +29,7 @@ inline void erase_all(std::string &string, const std::string &search) {
} }
} }
PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { PYBIND11_NOINLINE void clean_type_id(std::string &name) {
#if defined(__GNUG__) #if defined(__GNUG__)
int status = 0; int status = 0;
std::unique_ptr<char, void (*)(void *)> res { std::unique_ptr<char, void (*)(void *)> res {
......
...@@ -9,29 +9,13 @@ ...@@ -9,29 +9,13 @@
#pragma once #pragma once
#include "numpy.h" /* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
See also:
#if defined(__INTEL_COMPILER) https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
#elif defined(__GNUG__) || defined(__clang__) */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
# ifdef __clang__
// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated
// under Clang, so disable that warning here:
# pragma GCC diagnostic ignored "-Wdeprecated"
# endif
# if __GNUC__ >= 7
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
# endif
#endif
#if defined(_MSC_VER) #include "numpy.h"
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
#endif
#include <Eigen/Core> #include <Eigen/Core>
#include <Eigen/SparseCore> #include <Eigen/SparseCore>
...@@ -77,6 +61,7 @@ template <bool EigenRowMajor> struct EigenConformable { ...@@ -77,6 +61,7 @@ template <bool EigenRowMajor> struct EigenConformable {
EigenDStride stride{0, 0}; // Only valid if negativestrides is false! EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride! bool negativestrides = false; // If true, do not use stride!
// NOLINTNEXTLINE(google-explicit-constructor)
EigenConformable(bool fits = false) : conformable{fits} {} EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type: // Matrix type:
EigenConformable(EigenIndex r, EigenIndex c, EigenConformable(EigenIndex r, EigenIndex c,
...@@ -104,6 +89,7 @@ template <bool EigenRowMajor> struct EigenConformable { ...@@ -104,6 +89,7 @@ template <bool EigenRowMajor> struct EigenConformable {
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
(EigenRowMajor ? rows : cols) == 1); (EigenRowMajor ? rows : cols) == 1);
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return conformable; } operator bool() const { return conformable; }
}; };
...@@ -153,7 +139,8 @@ template <typename Type_> struct EigenProps { ...@@ -153,7 +139,8 @@ template <typename Type_> struct EigenProps {
np_cols = a.shape(1), np_cols = a.shape(1),
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)), np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar)); np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows) ||
(PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols))
return false; return false;
return {np_rows, np_cols, np_rstride, np_cstride}; return {np_rows, np_cols, np_rstride, np_cstride};
...@@ -165,7 +152,7 @@ template <typename Type_> struct EigenProps { ...@@ -165,7 +152,7 @@ template <typename Type_> struct EigenProps {
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)); stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
if (vector) { // Eigen type is a compile-time vector if (vector) { // Eigen type is a compile-time vector
if (fixed && size != n) if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n)
return false; // Vector size mismatch return false; // Vector size mismatch
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
} }
...@@ -179,7 +166,7 @@ template <typename Type_> struct EigenProps { ...@@ -179,7 +166,7 @@ template <typename Type_> struct EigenProps {
if (cols != n) return false; if (cols != n) return false;
return {1, n, stride}; return {1, n, stride};
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector } // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
if (fixed_rows && rows != n) return false; if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) return false;
return {n, 1, stride}; return {n, 1, stride};
} }
...@@ -341,8 +328,11 @@ public: ...@@ -341,8 +328,11 @@ public:
static constexpr auto name = props::descriptor; static constexpr auto name = props::descriptor;
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return &value; } operator Type*() { return &value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return value; } operator Type&() { return value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&&() && { return std::move(value); } operator Type&&() && { return std::move(value); }
template <typename T> using cast_op_type = movable_cast_op_type<T>; template <typename T> using cast_op_type = movable_cast_op_type<T>;
...@@ -466,7 +456,9 @@ public: ...@@ -466,7 +456,9 @@ public:
return true; return true;
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return ref.get(); } operator Type*() { return ref.get(); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return *ref; } operator Type&() { return *ref; }
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>; template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
...@@ -596,9 +588,3 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> { ...@@ -596,9 +588,3 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUG__) || defined(__clang__)
# pragma GCC diagnostic pop
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include "pybind11.h" #include "pybind11.h"
#include "eval.h" #include "eval.h"
#include <memory>
#include <vector>
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
# error Embedding the interpreter is not supported with PyPy # error Embedding the interpreter is not supported with PyPy
#endif #endif
...@@ -45,25 +48,23 @@ ...@@ -45,25 +48,23 @@
}); });
} }
\endrst */ \endrst */
#define PYBIND11_EMBEDDED_MODULE(name, variable) \ #define PYBIND11_EMBEDDED_MODULE(name, variable) \
static ::pybind11::module_::module_def \ static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
PYBIND11_CONCAT(pybind11_module_def_, name); \ static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ auto m = ::pybind11::module_::create_extension_module( \
auto m = ::pybind11::module_::create_extension_module( \ PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
PYBIND11_TOSTRING(name), nullptr, \ try { \
&PYBIND11_CONCAT(pybind11_module_def_, name)); \ PYBIND11_CONCAT(pybind11_init_, name)(m); \
try { \ return m.ptr(); \
PYBIND11_CONCAT(pybind11_init_, name)(m); \ } \
return m.ptr(); \ PYBIND11_CATCH_INIT_EXCEPTIONS \
} PYBIND11_CATCH_INIT_EXCEPTIONS \ } \
} \ PYBIND11_EMBEDDED_MODULE_IMPL(name) \
PYBIND11_EMBEDDED_MODULE_IMPL(name) \ ::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
(PYBIND11_TOSTRING(name), \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
PYBIND11_CONCAT(pybind11_init_impl_, name)); \ & variable) // NOLINT(bugprone-macro-parentheses)
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -85,29 +86,106 @@ struct embedded_module { ...@@ -85,29 +86,106 @@ struct embedded_module {
} }
}; };
struct wide_char_arg_deleter {
void operator()(wchar_t *ptr) const {
#if PY_VERSION_HEX >= 0x030500f0
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
PyMem_RawFree(ptr);
#else
delete[] ptr;
#endif
}
};
inline wchar_t *widen_chars(const char *safe_arg) {
#if PY_VERSION_HEX >= 0x030500f0
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
#else
wchar_t *widened_arg = nullptr;
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
size_t count = strlen(safe_arg);
# else
size_t count = mbstowcs(nullptr, safe_arg, 0);
# endif
if (count != static_cast<size_t>(-1)) {
widened_arg = new wchar_t[count + 1];
mbstowcs(widened_arg, safe_arg, count + 1);
}
#endif
return widened_arg;
}
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
// Before it was special-cased in python 3.8, passing an empty or null argv
// caused a segfault, so we have to reimplement the special case ourselves.
bool special_case = (argv == nullptr || argc <= 0);
const char *const empty_argv[]{"\0"};
const char *const *safe_argv = special_case ? empty_argv : argv;
if (special_case)
argc = 1;
auto argv_size = static_cast<size_t>(argc);
#if PY_MAJOR_VERSION >= 3
// SetArgv* on python 3 takes wchar_t, so we have to convert.
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
widened_argv_entries.reserve(argv_size);
for (size_t ii = 0; ii < argv_size; ++ii) {
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
if (!widened_argv_entries.back()) {
// A null here indicates a character-encoding failure or the python
// interpreter out of memory. Give up.
return;
}
widened_argv[ii] = widened_argv_entries.back().get();
}
auto pysys_argv = widened_argv.get();
#else
// python 2.x
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
std::vector<char *> char_strings{argv_size};
for (std::size_t i = 0; i < argv_size; ++i)
char_strings[i] = &strings[i][0];
char **pysys_argv = char_strings.data();
#endif
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/** \rst /** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be 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 called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
optional parameter can be used to skip the registration of signal handlers (see the optional `init_signal_handlers` parameter can be used to skip the registration of
`Python documentation`_ for details). Calling this function again after the interpreter signal handlers (see the `Python documentation`_ for details). Calling this function
has already been initialized is a fatal error. again after the interpreter has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This 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 is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.) 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 .. _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 */ \endrst */
inline void initialize_interpreter(bool init_signal_handlers = true) { 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");
Py_InitializeEx(init_signal_handlers ? 1 : 0); Py_InitializeEx(init_signal_handlers ? 1 : 0);
// Make .py files in the working directory available by default detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
module_::import("sys").attr("path").cast<list>().append(".");
} }
/** \rst /** \rst
...@@ -169,6 +247,8 @@ inline void finalize_interpreter() { ...@@ -169,6 +247,8 @@ inline void finalize_interpreter() {
Scope guard version of `initialize_interpreter` and `finalize_interpreter`. Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
This a move-only guard and only a single instance can exist. This a move-only guard and only a single instance can exist.
See `initialize_interpreter` for a discussion of its constructor arguments.
.. code-block:: cpp .. code-block:: cpp
#include <pybind11/embed.h> #include <pybind11/embed.h>
...@@ -180,8 +260,11 @@ inline void finalize_interpreter() { ...@@ -180,8 +260,11 @@ inline void finalize_interpreter() {
\endrst */ \endrst */
class scoped_interpreter { class scoped_interpreter {
public: public:
scoped_interpreter(bool init_signal_handlers = true) { explicit scoped_interpreter(bool init_signal_handlers = true,
initialize_interpreter(init_signal_handlers); int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
} }
scoped_interpreter(const scoped_interpreter &) = delete; scoped_interpreter(const scoped_interpreter &) = delete;
......
...@@ -136,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object()) ...@@ -136,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object())
pybind11_fail("File \"" + fname_str + "\" could not be opened!"); pybind11_fail("File \"" + fname_str + "\" could not be opened!");
} }
// In Python2, this should be encoded by getfilesystemencoding.
// We don't boher setting it since Python2 is past EOL anyway.
// See PR#3233
#if PY_VERSION_HEX >= 0x03000000
if (!global.contains("__file__")) {
global["__file__"] = std::move(fname);
}
#endif
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) #if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
local.ptr()); local.ptr());
......
...@@ -69,6 +69,10 @@ public: ...@@ -69,6 +69,10 @@ public:
// ensure GIL is held during functor destruction // ensure GIL is held during functor destruction
struct func_handle { struct func_handle {
function f; function f;
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17) && PY_MAJOR_VERSION < 3)
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept : f(std::move(f_)) {} func_handle(function &&f_) noexcept : f(std::move(f_)) {}
func_handle(const func_handle &f_) { operator=(f_); } func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) { func_handle &operator=(const func_handle &f_) {
...@@ -85,7 +89,7 @@ public: ...@@ -85,7 +89,7 @@ public:
// to emulate 'move initialization capture' in C++11 // to emulate 'move initialization capture' in C++11
struct func_wrapper { struct func_wrapper {
func_handle hfunc; func_handle hfunc;
func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {} explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
Return operator()(Args... args) const { Return operator()(Args... args) const {
gil_scoped_acquire acq; gil_scoped_acquire acq;
object retval(hfunc.f(std::forward<Args>(args)...)); object retval(hfunc.f(std::forward<Args>(args)...));
......
...@@ -50,7 +50,7 @@ PYBIND11_NAMESPACE_END(detail) ...@@ -50,7 +50,7 @@ PYBIND11_NAMESPACE_END(detail)
class gil_scoped_acquire { class gil_scoped_acquire {
public: public:
PYBIND11_NOINLINE gil_scoped_acquire() { PYBIND11_NOINLINE gil_scoped_acquire() {
auto const &internals = detail::get_internals(); auto &internals = detail::get_internals();
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
if (!tstate) { if (!tstate) {
...@@ -132,7 +132,7 @@ public: ...@@ -132,7 +132,7 @@ public:
// `get_internals()` must be called here unconditionally in order to initialize // `get_internals()` must be called here unconditionally in order to initialize
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
// initialization race could occur as multiple threads try `gil_scoped_acquire`. // initialization race could occur as multiple threads try `gil_scoped_acquire`.
const auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
tstate = PyEval_SaveThread(); tstate = PyEval_SaveThread();
if (disassoc) { if (disassoc) {
auto key = internals.tstate; auto key = internals.tstate;
......
...@@ -123,7 +123,7 @@ private: ...@@ -123,7 +123,7 @@ private:
} }
public: public:
pythonbuf(const object &pyostream, size_t buffer_size = 1024) explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
: buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")), : buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
pyflush(pyostream.attr("flush")) { pyflush(pyostream.attr("flush")) {
setp(d_buffer.get(), d_buffer.get() + buf_size - 1); setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
...@@ -171,8 +171,9 @@ protected: ...@@ -171,8 +171,9 @@ protected:
detail::pythonbuf buffer; detail::pythonbuf buffer;
public: public:
scoped_ostream_redirect(std::ostream &costream = std::cout, explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
const object &pyostream = module_::import("sys").attr("stdout")) const object &pyostream
= module_::import("sys").attr("stdout"))
: costream(costream), buffer(pyostream) { : costream(costream), buffer(pyostream) {
old = costream.rdbuf(&buffer); old = costream.rdbuf(&buffer);
} }
...@@ -201,8 +202,9 @@ public: ...@@ -201,8 +202,9 @@ public:
\endrst */ \endrst */
class scoped_estream_redirect : public scoped_ostream_redirect { class scoped_estream_redirect : public scoped_ostream_redirect {
public: public:
scoped_estream_redirect(std::ostream &costream = std::cerr, explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
const object &pyostream = module_::import("sys").attr("stderr")) const object &pyostream
= module_::import("sys").attr("stderr"))
: scoped_ostream_redirect(costream, pyostream) {} : scoped_ostream_redirect(costream, pyostream) {}
}; };
...@@ -217,7 +219,7 @@ class OstreamRedirect { ...@@ -217,7 +219,7 @@ class OstreamRedirect {
std::unique_ptr<scoped_estream_redirect> redirect_stderr; std::unique_ptr<scoped_estream_redirect> redirect_stderr;
public: public:
OstreamRedirect(bool do_stdout = true, bool do_stderr = true) explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
: do_stdout_(do_stdout), do_stderr_(do_stderr) {} : do_stdout_(do_stdout), do_stderr_(do_stderr) {}
void enter() { void enter() {
......
...@@ -25,11 +25,6 @@ ...@@ -25,11 +25,6 @@
#include <vector> #include <vector>
#include <typeindex> #include <typeindex>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
/* This will be true on all flat address space platforms and allows us to reduce the /* This will be true on all flat address space platforms and allows us to reduce the
whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
and dimension types (e.g. shape, strides, indexing), instead of inflicting this and dimension types (e.g. shape, strides, indexing), instead of inflicting this
...@@ -104,7 +99,7 @@ struct numpy_internals { ...@@ -104,7 +99,7 @@ struct numpy_internals {
} }
}; };
inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) {
ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals"); ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
} }
...@@ -203,6 +198,9 @@ struct npy_api { ...@@ -203,6 +198,9 @@ struct npy_api {
// Unused. Not removed because that affects ABI of the class. // Unused. Not removed because that affects ABI of the class.
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int);
PyObject* (*PyArray_Newshape_)(PyObject*, PyArray_Dims*, int);
PyObject* (*PyArray_View_)(PyObject*, PyObject*, PyObject*);
private: private:
enum functions { enum functions {
API_PyArray_GetNDArrayCFeatureVersion = 211, API_PyArray_GetNDArrayCFeatureVersion = 211,
...@@ -217,10 +215,12 @@ private: ...@@ -217,10 +215,12 @@ private:
API_PyArray_NewCopy = 85, API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94, API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 96, API_PyArray_DescrNewFromType = 96,
API_PyArray_Newshape = 135,
API_PyArray_Squeeze = 136,
API_PyArray_View = 137,
API_PyArray_DescrConverter = 174, API_PyArray_DescrConverter = 174,
API_PyArray_EquivTypes = 182, API_PyArray_EquivTypes = 182,
API_PyArray_GetArrayParamsFromObject = 278, API_PyArray_GetArrayParamsFromObject = 278,
API_PyArray_Squeeze = 136,
API_PyArray_SetBaseObject = 282 API_PyArray_SetBaseObject = 282
}; };
...@@ -248,11 +248,14 @@ private: ...@@ -248,11 +248,14 @@ private:
DECL_NPY_API(PyArray_NewCopy); DECL_NPY_API(PyArray_NewCopy);
DECL_NPY_API(PyArray_NewFromDescr); DECL_NPY_API(PyArray_NewFromDescr);
DECL_NPY_API(PyArray_DescrNewFromType); DECL_NPY_API(PyArray_DescrNewFromType);
DECL_NPY_API(PyArray_Newshape);
DECL_NPY_API(PyArray_Squeeze);
DECL_NPY_API(PyArray_View);
DECL_NPY_API(PyArray_DescrConverter); DECL_NPY_API(PyArray_DescrConverter);
DECL_NPY_API(PyArray_EquivTypes); DECL_NPY_API(PyArray_EquivTypes);
DECL_NPY_API(PyArray_GetArrayParamsFromObject); DECL_NPY_API(PyArray_GetArrayParamsFromObject);
DECL_NPY_API(PyArray_Squeeze);
DECL_NPY_API(PyArray_SetBaseObject); DECL_NPY_API(PyArray_SetBaseObject);
#undef DECL_NPY_API #undef DECL_NPY_API
return api; return api;
} }
...@@ -319,7 +322,7 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type; ...@@ -319,7 +322,7 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type;
template <typename T> using is_pod_struct = all_of< template <typename T> using is_pod_struct = all_of<
std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type
#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803) #if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
// libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5) // libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5)
// don't implement is_trivially_copyable, so approximate it // don't implement is_trivially_copyable, so approximate it
std::is_trivially_destructible<T>, std::is_trivially_destructible<T>,
...@@ -474,15 +477,15 @@ public: ...@@ -474,15 +477,15 @@ public:
m_ptr = from_args(pybind11::str(format)).release().ptr(); m_ptr = from_args(pybind11::str(format)).release().ptr();
} }
dtype(const char *format) : dtype(std::string(format)) { } explicit dtype(const char *format) : dtype(std::string(format)) {}
dtype(list names, list formats, list offsets, ssize_t itemsize) { dtype(list names, list formats, list offsets, ssize_t itemsize) {
dict args; dict args;
args["names"] = names; args["names"] = std::move(names);
args["formats"] = formats; args["formats"] = std::move(formats);
args["offsets"] = offsets; args["offsets"] = std::move(offsets);
args["itemsize"] = pybind11::int_(itemsize); args["itemsize"] = pybind11::int_(itemsize);
m_ptr = from_args(args).release().ptr(); m_ptr = from_args(std::move(args)).release().ptr();
} }
/// This is essentially the same as calling numpy.dtype(args) in Python. /// This is essentially the same as calling numpy.dtype(args) in Python.
...@@ -560,7 +563,7 @@ private: ...@@ -560,7 +563,7 @@ private:
formats.append(descr.format); formats.append(descr.format);
offsets.append(descr.offset); offsets.append(descr.offset);
} }
return dtype(names, formats, offsets, itemsize); return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
} }
}; };
...@@ -747,7 +750,7 @@ public: ...@@ -747,7 +750,7 @@ public:
* and the caller must take care not to access invalid dimensions or dimension indices. * and the caller must take care not to access invalid dimensions or dimension indices.
*/ */
template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & { template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
if (Dims >= 0 && ndim() != Dims) if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims)); "; expected " + std::to_string(Dims));
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim()); return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
...@@ -761,7 +764,7 @@ public: ...@@ -761,7 +764,7 @@ public:
* invalid dimensions or dimension indices. * invalid dimensions or dimension indices.
*/ */
template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & { template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & {
if (Dims >= 0 && ndim() != Dims) if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims)); "; expected " + std::to_string(Dims));
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim()); return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
...@@ -790,6 +793,33 @@ public: ...@@ -790,6 +793,33 @@ public:
if (isinstance<array>(new_array)) { *this = std::move(new_array); } if (isinstance<array>(new_array)) { *this = std::move(new_array); }
} }
/// Optional `order` parameter omitted, to be added as needed.
array reshape(ShapeContainer new_shape) {
detail::npy_api::PyArray_Dims d
= {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
auto new_array
= reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
if (!new_array) {
throw error_already_set();
}
return new_array;
}
/// Create a view of an array in a different data type.
/// This function may fundamentally reinterpret the data in the array.
/// It is the responsibility of the caller to ensure that this is safe.
/// Only supports the `dtype` argument, the `type` argument is omitted,
/// to be added as needed.
array view(const std::string &dtype) {
auto &api = detail::npy_api::get();
auto new_view = reinterpret_steal<array>(api.PyArray_View_(
m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
if (!new_view) {
throw error_already_set();
}
return new_view;
}
/// Ensure that the argument is a NumPy array /// Ensure that the argument is a NumPy array
/// In case of an error, nullptr is returned and the Python error is cleared. /// In case of an error, nullptr is returned and the Python error is cleared.
static array ensure(handle h, int ExtraFlags = 0) { static array ensure(handle h, int ExtraFlags = 0) {
...@@ -864,6 +894,7 @@ public: ...@@ -864,6 +894,7 @@ public:
if (!is_borrowed) Py_XDECREF(h.ptr()); if (!is_borrowed) Py_XDECREF(h.ptr());
} }
// NOLINTNEXTLINE(google-explicit-constructor)
array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
if (!m_ptr) throw error_already_set(); if (!m_ptr) throw error_already_set();
} }
...@@ -1110,7 +1141,7 @@ struct field_descriptor { ...@@ -1110,7 +1141,7 @@ struct field_descriptor {
dtype descr; dtype descr;
}; };
inline PYBIND11_NOINLINE void register_structured_dtype( PYBIND11_NOINLINE void register_structured_dtype(
any_container<field_descriptor> fields, any_container<field_descriptor> fields,
const std::type_info& tinfo, ssize_t itemsize, const std::type_info& tinfo, ssize_t itemsize,
bool (*direct_converter)(PyObject *, void *&)) { bool (*direct_converter)(PyObject *, void *&)) {
...@@ -1134,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype( ...@@ -1134,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
formats.append(field.descr); formats.append(field.descr);
offsets.append(pybind11::int_(field.offset)); offsets.append(pybind11::int_(field.offset));
} }
auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); auto dtype_ptr
= pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
.release()
.ptr();
// There is an existing bug in NumPy (as of v1.11): trailing bytes are // There is an existing bug in NumPy (as of v1.11): trailing bytes are
// not encoded explicitly into the format string. This will supposedly // not encoded explicitly into the format string. This will supposedly
...@@ -1551,8 +1585,11 @@ private: ...@@ -1551,8 +1585,11 @@ private:
"pybind11::vectorize(...) requires a function with at least one vectorizable argument"); "pybind11::vectorize(...) requires a function with at least one vectorizable argument");
public: public:
template <typename T> template <typename T,
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) { } // SFINAE to prevent shadowing the copy constructor.
typename = detail::enable_if_t<
!std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
object operator()(typename vectorize_arg<Args>::type... args) { object operator()(typename vectorize_arg<Args>::type... args) {
return run(args..., return run(args...,
...@@ -1702,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) { ...@@ -1702,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) {
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
...@@ -11,13 +11,6 @@ ...@@ -11,13 +11,6 @@
#include "pybind11.h" #include "pybind11.h"
#if defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -58,7 +51,8 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ { ...@@ -58,7 +51,8 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ {
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute, is_operator(), extra...); cl.def(op::name(), &op::execute, is_operator(), extra...);
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (id == op_truediv || id == op_itruediv) if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) ||
PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
&op::execute, is_operator(), extra...); &op::execute, is_operator(), extra...);
#endif #endif
...@@ -167,7 +161,3 @@ using detail::self; ...@@ -167,7 +161,3 @@ using detail::self;
using detail::hash; using detail::hash;
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#pragma once #pragma once
#include "detail/common.h"
#include "pybind11.h" #include "pybind11.h"
#include <set> #include <set>
#include <unordered_set> #include <unordered_set>
...@@ -19,33 +20,15 @@ ...@@ -19,33 +20,15 @@
#include <deque> #include <deque>
#include <valarray> #include <valarray>
#if defined(_MSC_VER) // See `detail/common.h` for implementation of these guards.
#pragma warning(push) #if defined(PYBIND11_HAS_OPTIONAL)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # include <optional>
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
# include <experimental/optional>
#endif #endif
#ifdef __has_include #if defined(PYBIND11_HAS_VARIANT)
// std::optional (but including it in c++14 mode isn't allowed)
# if defined(PYBIND11_CPP17) && __has_include(<optional>)
# include <optional>
# define PYBIND11_HAS_OPTIONAL 1
# endif
// std::experimental::optional (but not allowed in c++11 mode)
# if defined(PYBIND11_CPP14) && (__has_include(<experimental/optional>) && \
!__has_include(<optional>))
# include <experimental/optional>
# define PYBIND11_HAS_EXP_OPTIONAL 1
# endif
// std::variant
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
# include <variant>
# define PYBIND11_HAS_VARIANT 1
# endif
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
# include <optional>
# include <variant> # include <variant>
# define PYBIND11_HAS_OPTIONAL 1
# define PYBIND11_HAS_VARIANT 1
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
...@@ -173,12 +156,12 @@ public: ...@@ -173,12 +156,12 @@ public:
if (!std::is_lvalue_reference<T>::value) if (!std::is_lvalue_reference<T>::value)
policy = return_value_policy_override<Value>::policy(policy); policy = return_value_policy_override<Value>::policy(policy);
list l(src.size()); list l(src.size());
size_t index = 0; ssize_t index = 0;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) if (!value_)
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
...@@ -230,12 +213,12 @@ public: ...@@ -230,12 +213,12 @@ public:
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
size_t index = 0; ssize_t index = 0;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) if (!value_)
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
...@@ -390,7 +373,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) { ...@@ -390,7 +373,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
...@@ -595,6 +595,23 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ & ...@@ -595,6 +595,23 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
); );
} }
template<typename Map>
struct keys_view
{
Map &map;
};
template<typename Map>
struct values_view
{
Map &map;
};
template<typename Map>
struct items_view
{
Map &map;
};
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
...@@ -602,6 +619,9 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... ...@@ -602,6 +619,9 @@ 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 ValuesView = detail::values_view<Map>;
using ItemsView = detail::items_view<Map>;
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;
...@@ -615,6 +635,12 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -615,6 +635,12 @@ 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)...);
class_<KeysView> keys_view(
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local));
class_<ValuesView> values_view(
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local));
class_<ItemsView> items_view(
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local));
cl.def(init<>()); cl.def(init<>());
...@@ -628,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -628,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
cl.def("__iter__", cl.def("__iter__",
[](Map &m) { return make_key_iterator(m.begin(), m.end()); }, [](Map &m) { return make_key_iterator(m.begin(), m.end()); },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
);
cl.def("keys",
[](Map &m) { return KeysView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
cl.def("values",
[](Map &m) { return ValuesView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("items", cl.def("items",
[](Map &m) { return make_iterator(m.begin(), m.end()); }, [](Map &m) { return ItemsView{m}; },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("__getitem__", cl.def("__getitem__",
...@@ -654,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -654,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
return true; return true;
} }
); );
// Fallback for when the object is not of the key type
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
// Assignment provided only if the type is copyable // Assignment provided only if the type is copyable
detail::map_assignment<Map, Class_>(cl); detail::map_assignment<Map, Class_>(cl);
...@@ -669,6 +707,40 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -669,6 +707,40 @@ 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;
} }
......
import nox import nox
nox.options.sessions = ["lint", "tests", "tests_packaging"] nox.options.sessions = ["lint", "tests", "tests_packaging"]
PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"]
@nox.session(reuse_venv=True) @nox.session(reuse_venv=True)
def lint(session: nox.Session) -> None: def lint(session: nox.Session) -> None:
...@@ -13,7 +14,7 @@ def lint(session: nox.Session) -> None: ...@@ -13,7 +14,7 @@ def lint(session: nox.Session) -> None:
session.run("pre-commit", "run", "-a") session.run("pre-commit", "run", "-a")
@nox.session @nox.session(python=PYTHON_VERISONS)
def tests(session: nox.Session) -> None: def tests(session: nox.Session) -> None:
""" """
Run the tests (requires a compiler). Run the tests (requires a compiler).
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ._version import version_info, __version__ from ._version import __version__, version_info
from .commands import get_include, get_cmake_dir from .commands import get_cmake_dir, get_include
__all__ = ( __all__ = (
"version_info", "version_info",
......
...@@ -5,7 +5,7 @@ import argparse ...@@ -5,7 +5,7 @@ import argparse
import sys import sys
import sysconfig import sysconfig
from .commands import get_include, get_cmake_dir from .commands import get_cmake_dir, get_include
def print_includes(): def print_includes():
......
...@@ -8,5 +8,5 @@ def _to_int(s): ...@@ -8,5 +8,5 @@ def _to_int(s):
return s return s
__version__ = "2.7.1" __version__ = "2.8.0"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))
from typing import Union, Tuple from typing import Tuple, Union
def _to_int(s: str) -> Union[int, str]: ... def _to_int(s: str) -> Union[int, str]: ...
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))
......
...@@ -41,23 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -41,23 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import contextlib import contextlib
import os import os
import platform
import shutil import shutil
import sys import sys
import sysconfig
import tempfile import tempfile
import threading import threading
import platform
import warnings import warnings
import sysconfig
try: try:
from setuptools.command.build_ext import build_ext as _build_ext
from setuptools import Extension as _Extension from setuptools import Extension as _Extension
from setuptools.command.build_ext import build_ext as _build_ext
except ImportError: except ImportError:
from distutils.command.build_ext import build_ext as _build_ext from distutils.command.build_ext import build_ext as _build_ext
from distutils.extension import Extension as _Extension from distutils.extension import Extension as _Extension
import distutils.errors
import distutils.ccompiler import distutils.ccompiler
import distutils.errors
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
PY2 = sys.version_info[0] < 3 PY2 = sys.version_info[0] < 3
......
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / # IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
# pre-commit). # pre-commit).
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union import contextlib
from types import TracebackType import distutils.ccompiler
from distutils.command.build_ext import build_ext as _build_ext # type: ignore from distutils.command.build_ext import build_ext as _build_ext # type: ignore
from distutils.extension import Extension as _Extension from distutils.extension import Extension as _Extension
import distutils.ccompiler from types import TracebackType
import contextlib from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
WIN: bool WIN: bool
PY2: bool PY2: bool
......
...@@ -15,6 +15,12 @@ ignore = [ ...@@ -15,6 +15,12 @@ ignore = [
"noxfile.py", "noxfile.py",
] ]
[tool.isort]
# Needs the compiled .so modules and env.py from tests
known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
# For black compatibility
profile = "black"
[tool.mypy] [tool.mypy]
files = "pybind11" files = "pybind11"
python_version = "2.7" python_version = "2.7"
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
# Setup script for PyPI; use CMakeFile.txt to build extension modules # Setup script for PyPI; use CMakeFile.txt to build extension modules
import contextlib import contextlib
import io
import os import os
import re import re
import shutil import shutil
...@@ -11,7 +12,6 @@ import string ...@@ -11,7 +12,6 @@ import string
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import io
import setuptools.command.sdist import setuptools.command.sdist
......
...@@ -10,10 +10,10 @@ cmake_minimum_required(VERSION 3.4) ...@@ -10,10 +10,10 @@ cmake_minimum_required(VERSION 3.4)
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with # The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate # some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround: # the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.18) if(${CMAKE_VERSION} VERSION_LESS 3.21)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else() else()
cmake_policy(VERSION 3.18) cmake_policy(VERSION 3.21)
endif() endif()
# Only needed for CMake < 3.5 support # Only needed for CMake < 3.5 support
...@@ -104,6 +104,7 @@ set(PYBIND11_TEST_FILES ...@@ -104,6 +104,7 @@ set(PYBIND11_TEST_FILES
test_constants_and_functions.cpp test_constants_and_functions.cpp
test_copy_move.cpp test_copy_move.cpp
test_custom_type_casters.cpp test_custom_type_casters.cpp
test_custom_type_setup.cpp
test_docstring_options.cpp test_docstring_options.cpp
test_eigen.cpp test_eigen.cpp
test_enum.cpp test_enum.cpp
...@@ -129,6 +130,7 @@ set(PYBIND11_TEST_FILES ...@@ -129,6 +130,7 @@ set(PYBIND11_TEST_FILES
test_stl.cpp test_stl.cpp
test_stl_binders.cpp test_stl_binders.cpp
test_tagbased_polymorphic.cpp test_tagbased_polymorphic.cpp
test_thread.cpp
test_union.cpp test_union.cpp
test_virtual_functions.cpp) test_virtual_functions.cpp)
...@@ -169,6 +171,14 @@ set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_s ...@@ -169,6 +171,14 @@ set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_s
set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py)
set(PYBIND11_EIGEN_REPO
"https://gitlab.com/libeigen/eigen.git"
CACHE STRING "Eigen repository to use for tests")
# This hash is for 3.3.8, using a hash for security reasons
set(PYBIND11_EIGEN_VERSION
"dc252fbf00079ccab57948a164b1421703fe4361"
CACHE STRING "Eigen version to use for tests")
# 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).
...@@ -182,13 +192,11 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) ...@@ -182,13 +192,11 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN")
endif() endif()
set(EIGEN3_VERSION_STRING "3.3.8")
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
eigen eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
GIT_TAG ${EIGEN3_VERSION_STRING}) GIT_TAG "${PYBIND11_EIGEN_VERSION}")
FetchContent_GetProperties(eigen) FetchContent_GetProperties(eigen)
if(NOT eigen_POPULATED) if(NOT eigen_POPULATED)
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import sys
import subprocess import subprocess
import sys
from textwrap import dedent from textwrap import dedent
import pytest import pytest
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
/// Simple class used to test py::local: /// Simple class used to test py::local:
template <int> class LocalBase { template <int> class LocalBase {
public: public:
LocalBase(int i) : i(i) { } explicit LocalBase(int i) : i(i) { }
int i = -1; int i = -1;
}; };
...@@ -35,6 +35,25 @@ using NonLocalVec2 = std::vector<NonLocal2>; ...@@ -35,6 +35,25 @@ using NonLocalVec2 = std::vector<NonLocal2>;
using NonLocalMap = std::unordered_map<std::string, NonLocalType>; using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>; using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
// Exception that will be caught via the module local translator.
class LocalException : public std::exception {
public:
explicit LocalException(const char * m) : message{m} {}
const char * what() const noexcept override {return message.c_str();}
private:
std::string message = "";
};
// Exception that will be registered with register_local_exception_translator
class LocalSimpleException : public std::exception {
public:
explicit LocalSimpleException(const char * m) : message{m} {}
const char * what() const noexcept override {return message.c_str();}
private:
std::string message = "";
};
PYBIND11_MAKE_OPAQUE(LocalVec); PYBIND11_MAKE_OPAQUE(LocalVec);
PYBIND11_MAKE_OPAQUE(LocalVec2); PYBIND11_MAKE_OPAQUE(LocalVec2);
PYBIND11_MAKE_OPAQUE(LocalMap); PYBIND11_MAKE_OPAQUE(LocalMap);
...@@ -56,11 +75,11 @@ py::class_<T> bind_local(Args && ...args) { ...@@ -56,11 +75,11 @@ py::class_<T> bind_local(Args && ...args) {
namespace pets { namespace pets {
class Pet { class Pet {
public: public:
Pet(std::string name) : name_(std::move(name)) {} explicit Pet(std::string name) : name_(std::move(name)) {}
std::string name_; std::string name_;
const std::string &name() const { return name_; } const std::string &name() const { return name_; }
}; };
} // namespace pets } // namespace pets
struct MixGL { int i; MixGL(int i) : i{i} {} }; struct MixGL { int i; explicit MixGL(int i) : i{i} {} };
struct MixGL2 { int i; MixGL2(int i) : i{i} {} }; struct MixGL2 { int i; explicit MixGL2(int i) : i{i} {} };
...@@ -65,7 +65,7 @@ public: ...@@ -65,7 +65,7 @@ public:
ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); } ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); }
/// Construct a reference from a pointer /// Construct a reference from a pointer
ref(T *ptr) : m_ptr(ptr) { explicit ref(T *ptr) : m_ptr(ptr) {
if (m_ptr) ((Object *) m_ptr)->incRef(); if (m_ptr) ((Object *) m_ptr)->incRef();
print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer"); print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");
...@@ -110,7 +110,11 @@ public: ...@@ -110,7 +110,11 @@ public:
/// Overwrite this reference with another reference /// Overwrite this reference with another reference
ref& operator=(const ref& r) { ref& operator=(const ref& r) {
print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this); if (this == &r) {
return *this;
}
print_copy_assigned(this, "pointer", r.m_ptr);
track_copy_assigned((ref_tag *) this);
if (m_ptr == r.m_ptr) if (m_ptr == r.m_ptr)
return *this; return *this;
...@@ -161,7 +165,7 @@ public: ...@@ -161,7 +165,7 @@ public:
const T& operator*() const { return *m_ptr; } const T& operator*() const { return *m_ptr; }
/// Return a pointer to the referenced object /// Return a pointer to the referenced object
operator T* () { return m_ptr; } explicit operator T* () { return m_ptr; }
/// Return a const pointer to the referenced object /// Return a const pointer to the referenced object
T* get_ptr() { return m_ptr; } T* get_ptr() { return m_ptr; }
......
...@@ -29,11 +29,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { ...@@ -29,11 +29,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
bind_local<ExternalType2>(m, "ExternalType2", py::module_local()); bind_local<ExternalType2>(m, "ExternalType2", py::module_local());
// test_exceptions.py // test_exceptions.py
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); }); m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); });
m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); }); m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); });
m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); }); m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); });
m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); }); m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); });
m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); m.def("throw_stop_iteration", []() { throw py::stop_iteration(); });
m.def("throw_local_error", []() { throw LocalException("just local"); });
m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); });
py::register_exception_translator([](std::exception_ptr p) { py::register_exception_translator([](std::exception_ptr p) {
try { try {
if (p) std::rethrow_exception(p); if (p) std::rethrow_exception(p);
...@@ -42,6 +45,17 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { ...@@ -42,6 +45,17 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
} }
}); });
// translate the local exception into a key error but only in this module
py::register_local_exception_translator([](std::exception_ptr p) {
try {
if (p) {
std::rethrow_exception(p);
}
} catch (const LocalException &e) {
PyErr_SetString(PyExc_KeyError, e.what());
}
});
// test_local_bindings.py // test_local_bindings.py
// Local to both: // Local to both:
bind_local<LocalType, 1>(m, "LocalType", py::module_local()) bind_local<LocalType, 1>(m, "LocalType", py::module_local())
...@@ -94,7 +108,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { ...@@ -94,7 +108,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
// test_internal_locals_differ // test_internal_locals_differ
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); }); m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
// test_stl_caster_vs_stl_bind // test_stl_caster_vs_stl_bind
py::bind_vector<std::vector<int>>(m, "VectorInt"); py::bind_vector<std::vector<int>>(m, "VectorInt");
...@@ -109,7 +123,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { ...@@ -109,7 +123,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
class Dog : public pets::Pet { class Dog : public pets::Pet {
public: public:
Dog(std::string name) : Pet(std::move(name)) {} explicit Dog(std::string name) : Pet(std::move(name)) {}
}; };
py::class_<pets::Pet>(m, "Pet", py::module_local()) py::class_<pets::Pet>(m, "Pet", py::module_local())
.def("name", &pets::Pet::name); .def("name", &pets::Pet::name);
......
#pragma once #pragma once
// This must be kept first for MSVC 2015.
// Do not remove the empty line between the #includes.
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/eval.h> #include <pybind11/eval.h>
#if defined(_MSC_VER) && _MSC_VER < 1910 #if defined(_MSC_VER) && _MSC_VER < 1910
...@@ -19,15 +16,14 @@ class test_initializer { ...@@ -19,15 +16,14 @@ class test_initializer {
using Initializer = void (*)(py::module_ &); using Initializer = void (*)(py::module_ &);
public: public:
test_initializer(Initializer init); explicit test_initializer(Initializer init);
test_initializer(const char *submodule_name, Initializer init); test_initializer(const char *submodule_name, Initializer init);
}; };
#define TEST_SUBMODULE(name, variable) \ #define TEST_SUBMODULE(name, variable) \
void test_submodule_##name(py::module_ &); \ void test_submodule_##name(py::module_ &); \
test_initializer name(#name, test_submodule_##name); \ test_initializer name(#name, test_submodule_##name); \
void test_submodule_##name(py::module_ &variable) void test_submodule_##name(py::module_ &(variable))
/// Dummy type which is not exported anywhere -- something to trigger a conversion error /// Dummy type which is not exported anywhere -- something to trigger a conversion error
struct UnregisteredType { }; struct UnregisteredType { };
...@@ -36,7 +32,7 @@ struct UnregisteredType { }; ...@@ -36,7 +32,7 @@ struct UnregisteredType { };
class UserType { class UserType {
public: public:
UserType() = default; UserType() = default;
UserType(int i) : i(i) { } explicit UserType(int i) : i(i) { }
int value() const { return i; } int value() const { return i; }
void set(int set) { i = set; } void set(int set) { i = set; }
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" numpy==1.16.6; python_version<"3.6" and sys_platform!="win32"
numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6" numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6"
numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version=="3.6" numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version=="3.6"
numpy==1.20.0; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.7" and python_version<"3.10" numpy==1.21.2; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.7" and python_version<"3.10"
numpy==1.21.2; platform_python_implementation!="PyPy" and sys_platform=="linux" and python_version=="3.10"
pytest==4.6.9; python_version<"3.5" pytest==4.6.9; python_version<"3.5"
pytest==6.1.2; python_version=="3.5" pytest==6.1.2; python_version=="3.5"
pytest==6.2.1; python_version>="3.6" and python_version<="3.9" pytest==6.2.4; python_version>="3.6"
pytest @ git+https://github.com/pytest-dev/pytest@c117bc350ec1e570672fda3b2ad234fd52e72b53; python_version>="3.10"
pytest-timeout pytest-timeout
scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6"
scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10"
...@@ -40,7 +40,11 @@ TEST_SUBMODULE(buffers, m) { ...@@ -40,7 +40,11 @@ TEST_SUBMODULE(buffers, m) {
} }
Matrix &operator=(const Matrix &s) { Matrix &operator=(const Matrix &s) {
print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); if (this == &s) {
return *this;
}
print_copy_assigned(this,
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data; delete[] m_data;
m_rows = s.m_rows; m_rows = s.m_rows;
m_cols = s.m_cols; m_cols = s.m_cols;
...@@ -118,7 +122,7 @@ TEST_SUBMODULE(buffers, m) { ...@@ -118,7 +122,7 @@ TEST_SUBMODULE(buffers, m) {
// test_inherited_protocol // test_inherited_protocol
class SquareMatrix : public Matrix { class SquareMatrix : public Matrix {
public: public:
SquareMatrix(py::ssize_t n) : Matrix(n, n) { } explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {}
}; };
// Derived classes inherit the buffer protocol and the buffer access function // Derived classes inherit the buffer protocol and the buffer access function
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix") py::class_<SquareMatrix, Matrix>(m, "SquareMatrix")
...@@ -169,7 +173,7 @@ TEST_SUBMODULE(buffers, m) { ...@@ -169,7 +173,7 @@ TEST_SUBMODULE(buffers, m) {
struct BufferReadOnly { struct BufferReadOnly {
const uint8_t value = 0; const uint8_t value = 0;
BufferReadOnly(uint8_t value): value(value) {} explicit BufferReadOnly(uint8_t value) : value(value) {}
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() {
return py::buffer_info(&value, 1); return py::buffer_info(&value, 1);
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import ctypes
import io import io
import struct import struct
import ctypes
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import buffers as m
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import buffers as m
np = pytest.importorskip("numpy") np = pytest.importorskip("numpy")
......
...@@ -10,11 +10,6 @@ ...@@ -10,11 +10,6 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <pybind11/complex.h> #include <pybind11/complex.h>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
struct ConstRefCasted { struct ConstRefCasted {
int tag; int tag;
}; };
...@@ -30,16 +25,28 @@ class type_caster<ConstRefCasted> { ...@@ -30,16 +25,28 @@ class type_caster<ConstRefCasted> {
// cast operator. // cast operator.
bool load(handle, bool) { return true; } bool load(handle, bool) { return true; }
operator ConstRefCasted &&() { explicit operator ConstRefCasted &&() {
value = {1}; value = {1};
// NOLINTNEXTLINE(performance-move-const-arg) // NOLINTNEXTLINE(performance-move-const-arg)
return std::move(value); return std::move(value);
} }
operator ConstRefCasted&() { value = {2}; return value; } explicit operator ConstRefCasted &() {
operator ConstRefCasted*() { value = {3}; return &value; } value = {2};
return value;
}
explicit operator ConstRefCasted *() {
value = {3};
return &value;
}
operator const ConstRefCasted&() { value = {4}; return value; } explicit operator const ConstRefCasted &() {
operator const ConstRefCasted*() { value = {5}; return &value; } value = {4};
return value;
}
explicit operator const ConstRefCasted *() {
value = {5};
return &value;
}
// custom cast_op to explicitly propagate types to the conversion operators. // custom cast_op to explicitly propagate types to the conversion operators.
template <typename T_> template <typename T_>
...@@ -73,7 +80,7 @@ TEST_SUBMODULE(builtin_casters, m) { ...@@ -73,7 +80,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 (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32
wstr.push_back(0x7a); // z wstr.push_back(0x7a); // z
...@@ -83,11 +90,12 @@ TEST_SUBMODULE(builtin_casters, m) { ...@@ -83,11 +90,12 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); });
m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); });
#if PY_MAJOR_VERSION >= 3
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
if (PY_MAJOR_VERSION >= 3) 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 (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2)
m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); });
#endif
m.def("u8_Z", []() -> char { return 'Z'; }); m.def("u8_Z", []() -> char { return 'Z'; });
m.def("u8_eacute", []() -> char { return '\xe9'; }); m.def("u8_eacute", []() -> char { return '\xe9'; });
m.def("u16_ibang", [=]() -> char16_t { return ib16; }); m.def("u16_ibang", [=]() -> char16_t { return ib16; });
......
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import IncType, UserType
from pybind11_tests import builtin_casters as m from pybind11_tests import builtin_casters as m
from pybind11_tests import UserType, IncType
def test_simple_string(): def test_simple_string():
......
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import call_policies as m
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import call_policies as m
@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) @pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
......
...@@ -81,16 +81,55 @@ TEST_SUBMODULE(callbacks, m) { ...@@ -81,16 +81,55 @@ TEST_SUBMODULE(callbacks, m) {
}; };
// Export the payload constructor statistics for testing purposes: // Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>); m.def("payload_cstats", &ConstructorStats::get<Payload>);
/* Test cleanup of lambda closure */ m.def("test_lambda_closure_cleanup", []() -> std::function<void()> {
m.def("test_cleanup", []() -> std::function<void()> {
Payload p; Payload p;
// In this situation, `Func` in the implementation of
// `cpp_function::initialize` is NOT trivially destructible.
return [p]() { return [p]() {
/* p should be cleaned up when the returned function is garbage collected */ /* p should be cleaned up when the returned function is garbage collected */
(void) p; (void) p;
}; };
}); });
class CppCallable {
public:
CppCallable() { track_default_created(this); }
~CppCallable() { track_destroyed(this); }
CppCallable(const CppCallable &) { track_copy_created(this); }
CppCallable(CppCallable &&) noexcept { track_move_created(this); }
void operator()() {}
};
m.def("test_cpp_callable_cleanup", []() {
// Related issue: https://github.com/pybind/pybind11/issues/3228
// Related PR: https://github.com/pybind/pybind11/pull/3229
py::list alive_counts;
ConstructorStats &stat = ConstructorStats::get<CppCallable>();
alive_counts.append(stat.alive());
{
CppCallable cpp_callable;
alive_counts.append(stat.alive());
{
// In this situation, `Func` in the implementation of
// `cpp_function::initialize` IS trivially destructible,
// only `capture` is not.
py::cpp_function py_func(cpp_callable);
py::detail::silence_unused_warnings(py_func);
alive_counts.append(stat.alive());
}
alive_counts.append(stat.alive());
{
py::cpp_function py_func(std::move(cpp_callable));
py::detail::silence_unused_warnings(py_func);
alive_counts.append(stat.alive());
}
alive_counts.append(stat.alive());
}
alive_counts.append(stat.alive());
return alive_counts;
});
// test_cpp_function_roundtrip // test_cpp_function_roundtrip
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */ /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
m.def("dummy_function", &dummy_function); m.def("dummy_function", &dummy_function);
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest
from pybind11_tests import callbacks as m
from threading import Thread
import time import time
from threading import Thread
import pytest
import env # NOQA: F401 import env # NOQA: F401
from pybind11_tests import callbacks as m
def test_callbacks(): def test_callbacks():
...@@ -77,13 +79,18 @@ def test_keyword_args_and_generalized_unpacking(): ...@@ -77,13 +79,18 @@ def test_keyword_args_and_generalized_unpacking():
def test_lambda_closure_cleanup(): def test_lambda_closure_cleanup():
m.test_cleanup() m.test_lambda_closure_cleanup()
cstats = m.payload_cstats() cstats = m.payload_cstats()
assert cstats.alive() == 0 assert cstats.alive() == 0
assert cstats.copy_constructions == 1 assert cstats.copy_constructions == 1
assert cstats.move_constructions >= 1 assert cstats.move_constructions >= 1
def test_cpp_callable_cleanup():
alive_counts = m.test_cpp_callable_cleanup()
assert alive_counts == [0, 1, 2, 1, 2, 1, 0]
def test_cpp_function_roundtrip(): def test_cpp_function_roundtrip():
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from pybind11_tests import chrono as m
import datetime import datetime
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import chrono as m
def test_chrono_system_clock(): def test_chrono_system_clock():
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
// test_brace_initialization // test_brace_initialization
struct NoBraceInitialization { struct NoBraceInitialization {
NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {} explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
template <typename T> template <typename T>
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {} NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
...@@ -47,10 +47,26 @@ TEST_SUBMODULE(class_, m) { ...@@ -47,10 +47,26 @@ TEST_SUBMODULE(class_, m) {
} }
~NoConstructor() { print_destroyed(this); } ~NoConstructor() { print_destroyed(this); }
}; };
struct NoConstructorNew {
NoConstructorNew() = default;
NoConstructorNew(const NoConstructorNew &) = default;
NoConstructorNew(NoConstructorNew &&) = default;
static NoConstructorNew *new_instance() {
auto *ptr = new NoConstructorNew();
print_created(ptr, "via new_instance");
return ptr;
}
~NoConstructorNew() { print_destroyed(this); }
};
py::class_<NoConstructor>(m, "NoConstructor") py::class_<NoConstructor>(m, "NoConstructor")
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); .def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
py::class_<NoConstructorNew>(m, "NoConstructorNew")
.def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__
.def_static("__new__",
[](const py::object &) { return NoConstructorNew::new_instance(); });
// test_inheritance // test_inheritance
class Pet { class Pet {
public: public:
...@@ -65,18 +81,18 @@ TEST_SUBMODULE(class_, m) { ...@@ -65,18 +81,18 @@ TEST_SUBMODULE(class_, m) {
class Dog : public Pet { class Dog : public Pet {
public: public:
Dog(const std::string &name) : Pet(name, "dog") {} explicit Dog(const std::string &name) : Pet(name, "dog") {}
std::string bark() const { return "Woof!"; } std::string bark() const { return "Woof!"; }
}; };
class Rabbit : public Pet { class Rabbit : public Pet {
public: public:
Rabbit(const std::string &name) : Pet(name, "parrot") {} explicit Rabbit(const std::string &name) : Pet(name, "parrot") {}
}; };
class Hamster : public Pet { class Hamster : public Pet {
public: public:
Hamster(const std::string &name) : Pet(name, "rodent") {} explicit Hamster(const std::string &name) : Pet(name, "rodent") {}
}; };
class Chimera : public Pet { class Chimera : public Pet {
...@@ -208,7 +224,7 @@ TEST_SUBMODULE(class_, m) { ...@@ -208,7 +224,7 @@ TEST_SUBMODULE(class_, m) {
struct ConvertibleFromUserType { struct ConvertibleFromUserType {
int i; int i;
ConvertibleFromUserType(UserType u) : i(u.value()) { } explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
}; };
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType") py::class_<ConvertibleFromUserType>(m, "AcceptsUserType")
...@@ -263,7 +279,7 @@ TEST_SUBMODULE(class_, m) { ...@@ -263,7 +279,7 @@ TEST_SUBMODULE(class_, m) {
}; };
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
PyAliasedHasOpNewDelSize() = default; PyAliasedHasOpNewDelSize() = default;
PyAliasedHasOpNewDelSize(int) { } explicit PyAliasedHasOpNewDelSize(int) {}
std::uint64_t j; std::uint64_t j;
}; };
struct HasOpNewDelBoth { struct HasOpNewDelBoth {
...@@ -492,15 +508,15 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>; ...@@ -492,15 +508,15 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>;
using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>; using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>;
using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>; using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>;
using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>; using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>;
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \ #define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
"DoesntBreak" #N " has wrong type!") "DoesntBreak" #N " has wrong type!")
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8); CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \ #define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \
"DoesntBreak" #N " has wrong type_alias!") "DoesntBreak" #N " has wrong type_alias!")
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \ #define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
"DoesntBreak" #N " has type alias, but shouldn't!") "DoesntBreak" #N " has type alias, but shouldn't!")
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8); CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \ #define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
"DoesntBreak" #N " has wrong holder_type!") "DoesntBreak" #N " has wrong holder_type!")
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique); CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
...@@ -510,7 +526,7 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); ...@@ -510,7 +526,7 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
// failures occurs). // failures occurs).
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type: // We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \ #define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
"Breaks1 has wrong type!"); "Breaks1 has wrong type!");
//// Two holder classes: //// Two holder classes:
......
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import ConstructorStats, UserType
from pybind11_tests import class_ as m from pybind11_tests import class_ as m
from pybind11_tests import UserType, ConstructorStats
def test_repr(): def test_repr():
...@@ -26,6 +25,14 @@ def test_instance(msg): ...@@ -26,6 +25,14 @@ def test_instance(msg):
assert cstats.alive() == 0 assert cstats.alive() == 0
def test_instance_new(msg):
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
cstats = ConstructorStats.get(m.NoConstructorNew)
assert cstats.alive() == 1
del instance
assert cstats.alive() == 0
def test_type(): def test_type():
assert m.check_type(1) == m.DerivedClass1 assert m.check_type(1) == m.DerivedClass1
with pytest.raises(RuntimeError) as execinfo: with pytest.raises(RuntimeError) as execinfo:
......
...@@ -22,5 +22,7 @@ set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_bui ...@@ -22,5 +22,7 @@ set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_bui
# This may be needed to resolve header conflicts, e.g. between Python release and debug headers. # This may be needed to resolve header conflicts, e.g. between Python release and debug headers.
set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON)
add_custom_target(check_installed_embed $<TARGET_FILE:test_installed_embed> add_custom_target(
${PROJECT_SOURCE_DIR}/../test.py) check_installed_embed
$<TARGET_FILE:test_installed_embed> ${PROJECT_SOURCE_DIR}/../test.py
DEPENDS test_installed_embed)
...@@ -35,4 +35,5 @@ add_custom_target( ...@@ -35,4 +35,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function> PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_installed_function)
...@@ -42,4 +42,5 @@ add_custom_target( ...@@ -42,4 +42,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target> PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_installed_target)
...@@ -23,8 +23,10 @@ add_executable(test_subdirectory_embed ../embed.cpp) ...@@ -23,8 +23,10 @@ add_executable(test_subdirectory_embed ../embed.cpp)
target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed) target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed)
set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build) set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build)
add_custom_target(check_subdirectory_embed $<TARGET_FILE:test_subdirectory_embed> add_custom_target(
"${PROJECT_SOURCE_DIR}/../test.py") check_subdirectory_embed
$<TARGET_FILE:test_subdirectory_embed> "${PROJECT_SOURCE_DIR}/../test.py"
DEPENDS test_subdirectory_embed)
# Test custom export group -- PYBIND11_EXPORT_NAME # Test custom export group -- PYBIND11_EXPORT_NAME
add_library(test_embed_lib ../embed.cpp) add_library(test_embed_lib ../embed.cpp)
......
...@@ -31,4 +31,5 @@ add_custom_target( ...@@ -31,4 +31,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function> PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_subdirectory_function)
...@@ -37,4 +37,5 @@ add_custom_target( ...@@ -37,4 +37,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target> PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_subdirectory_target)
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
import test_cmake_build import test_cmake_build
if str is not bytes: # If not Python2
assert isinstance(__file__, str) # Test this is properly set
assert test_cmake_build.add(1, 2) == 3 assert test_cmake_build.add(1, 2) == 3
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
...@@ -37,7 +37,7 @@ template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {}; ...@@ -37,7 +37,7 @@ template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
class MoveOnlyInt { class MoveOnlyInt {
public: public:
MoveOnlyInt() { print_default_created(this); } MoveOnlyInt() { print_default_created(this); }
MoveOnlyInt(int v) : value{v} { print_created(this, value); } explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); }
MoveOnlyInt(MoveOnlyInt &&m) noexcept { MoveOnlyInt(MoveOnlyInt &&m) noexcept {
print_move_created(this, m.value); print_move_created(this, m.value);
std::swap(value, m.value); std::swap(value, m.value);
...@@ -56,7 +56,7 @@ public: ...@@ -56,7 +56,7 @@ public:
class MoveOrCopyInt { class MoveOrCopyInt {
public: public:
MoveOrCopyInt() { print_default_created(this); } MoveOrCopyInt() { print_default_created(this); }
MoveOrCopyInt(int v) : value{v} { print_created(this, value); } explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept { MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
print_move_created(this, m.value); print_move_created(this, m.value);
std::swap(value, m.value); std::swap(value, m.value);
...@@ -75,7 +75,7 @@ public: ...@@ -75,7 +75,7 @@ public:
class CopyOnlyInt { class CopyOnlyInt {
public: public:
CopyOnlyInt() { print_default_created(this); } CopyOnlyInt() { print_default_created(this); }
CopyOnlyInt(int v) : value{v} { print_created(this, value); } explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); }
CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; } CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; }
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
~CopyOnlyInt() { print_destroyed(this); } ~CopyOnlyInt() { print_destroyed(this); }
...@@ -107,8 +107,8 @@ public: ...@@ -107,8 +107,8 @@ public:
if (!src) return none().release(); if (!src) return none().release();
return cast(*src, policy, parent); return cast(*src, policy, parent);
} }
operator CopyOnlyInt*() { return &value; } explicit operator CopyOnlyInt *() { return &value; }
operator CopyOnlyInt&() { return value; } explicit operator CopyOnlyInt &() { return value; }
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>; template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
...@@ -219,7 +219,7 @@ TEST_SUBMODULE(copy_move_policies, m) { ...@@ -219,7 +219,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
// #389: rvp::move should fall-through to copy on non-movable objects // #389: rvp::move should fall-through to copy on non-movable objects
struct MoveIssue1 { struct MoveIssue1 {
int v; int v;
MoveIssue1(int v) : v{v} {} explicit MoveIssue1(int v) : v{v} {}
MoveIssue1(const MoveIssue1 &c) = default; MoveIssue1(const MoveIssue1 &c) = default;
MoveIssue1(MoveIssue1 &&) = delete; MoveIssue1(MoveIssue1 &&) = delete;
}; };
...@@ -227,7 +227,7 @@ TEST_SUBMODULE(copy_move_policies, m) { ...@@ -227,7 +227,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
struct MoveIssue2 { struct MoveIssue2 {
int v; int v;
MoveIssue2(int v) : v{v} {} explicit MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default; MoveIssue2(MoveIssue2 &&) = default;
}; };
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v); py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import copy_move_policies as m from pybind11_tests import copy_move_policies as m
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import custom_type_casters as m from pybind11_tests import custom_type_casters as m
......
/*
tests/test_custom_type_setup.cpp -- Tests `pybind11::custom_type_setup`
Copyright (c) Google LLC
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/pybind11.h>
#include "pybind11_tests.h"
namespace py = pybind11;
namespace {
struct OwnsPythonObjects {
py::object value = py::none();
};
} // namespace
TEST_SUBMODULE(custom_type_setup, m) {
py::class_<OwnsPythonObjects> cls(
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
Py_VISIT(self.value.ptr());
return 0;
};
type->tp_clear = [](PyObject *self_base) {
auto &self = py::cast<OwnsPythonObjects &>(py::handle(self_base));
self.value = py::none();
return 0;
};
}));
cls.def(py::init<>());
cls.def_readwrite("value", &OwnsPythonObjects::value);
}
# -*- coding: utf-8 -*-
import gc
import weakref
import pytest
import env # noqa: F401
from pybind11_tests import custom_type_setup as m
@pytest.fixture
def gc_tester():
"""Tests that an object is garbage collected.
Assumes that any unreferenced objects are fully collected after calling
`gc.collect()`. That is true on CPython, but does not appear to reliably
hold on PyPy.
"""
weak_refs = []
def add_ref(obj):
# PyPy does not support `gc.is_tracked`.
if hasattr(gc, "is_tracked"):
assert gc.is_tracked(obj)
weak_refs.append(weakref.ref(obj))
yield add_ref
gc.collect()
for ref in weak_refs:
assert ref() is None
# PyPy does not seem to reliably garbage collect.
@pytest.mark.skipif("env.PYPY")
def test_self_cycle(gc_tester):
obj = m.OwnsPythonObjects()
obj.value = obj
gc_tester(obj)
# PyPy does not seem to reliably garbage collect.
@pytest.mark.skipif("env.PYPY")
def test_indirect_cycle(gc_tester):
obj = m.OwnsPythonObjects()
obj_list = [obj]
obj.value = obj_list
gc_tester(obj)
...@@ -178,6 +178,7 @@ TEST_SUBMODULE(eigen, m) { ...@@ -178,6 +178,7 @@ TEST_SUBMODULE(eigen, m) {
ReturnTester() { print_created(this); } ReturnTester() { print_created(this); }
~ReturnTester() { print_destroyed(this); } ~ReturnTester() { print_destroyed(this); }
static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); } static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); }
// NOLINTNEXTLINE(readability-const-return-type)
static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); } static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); }
Eigen::MatrixXd &get() { return mat; } Eigen::MatrixXd &get() { return mat; }
Eigen::MatrixXd *getPtr() { return &mat; } Eigen::MatrixXd *getPtr() { return &mat; }
...@@ -244,6 +245,9 @@ TEST_SUBMODULE(eigen, m) { ...@@ -244,6 +245,9 @@ TEST_SUBMODULE(eigen, m) {
// test_fixed, and various other tests // test_fixed, and various other tests
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); }); m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
// Our Eigen does a hack which respects constness through the numpy writeable flag.
// Therefore, the const return actually affects this type despite being an rvalue.
// NOLINTNEXTLINE(readability-const-return-type)
m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); }); m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); });
m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); }); m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); });
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; }); m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
np = pytest.importorskip("numpy") np = pytest.importorskip("numpy")
......
...@@ -31,6 +31,7 @@ endif() ...@@ -31,6 +31,7 @@ endif()
add_custom_target( add_custom_target(
cpptest cpptest
COMMAND "$<TARGET_FILE:test_embed>" COMMAND "$<TARGET_FILE:test_embed>"
DEPENDS test_embed
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
pybind11_add_module(external_module THIN_LTO external_module.cpp) pybind11_add_module(external_module THIN_LTO external_module.cpp)
......
...@@ -9,7 +9,7 @@ namespace py = pybind11; ...@@ -9,7 +9,7 @@ namespace py = pybind11;
PYBIND11_MODULE(external_module, m) { PYBIND11_MODULE(external_module, m) {
class A { class A {
public: public:
A(int value) : v{value} {}; explicit A(int value) : v{value} {};
int v; int v;
}; };
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <catch.hpp> #include <catch.hpp>
#include <cstdlib>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <thread> #include <thread>
...@@ -18,11 +19,12 @@ using namespace py::literals; ...@@ -18,11 +19,12 @@ using namespace py::literals;
class Widget { class Widget {
public: public:
Widget(std::string message) : message(std::move(message)) {} explicit Widget(std::string message) : message(std::move(message)) {}
virtual ~Widget() = default; virtual ~Widget() = default;
std::string the_message() const { return message; } std::string the_message() const { return message; }
virtual int the_answer() const = 0; virtual int the_answer() const = 0;
virtual std::string argv0() const = 0;
private: private:
std::string message; std::string message;
...@@ -32,6 +34,7 @@ class PyWidget final : public Widget { ...@@ -32,6 +34,7 @@ class PyWidget final : public Widget {
using Widget::Widget; using Widget::Widget;
int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); } int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); }
std::string argv0() const override { PYBIND11_OVERRIDE_PURE(std::string, Widget, argv0); }
}; };
PYBIND11_EMBEDDED_MODULE(widget_module, m) { PYBIND11_EMBEDDED_MODULE(widget_module, m) {
...@@ -74,8 +77,24 @@ TEST_CASE("Import error handling") { ...@@ -74,8 +77,24 @@ TEST_CASE("Import error handling") {
REQUIRE_NOTHROW(py::module_::import("widget_module")); REQUIRE_NOTHROW(py::module_::import("widget_module"));
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), REQUIRE_THROWS_WITH(py::module_::import("throw_exception"),
"ImportError: C++ Error"); "ImportError: C++ Error");
#if PY_VERSION_HEX >= 0x03030000
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: initialization failed"));
auto locals = py::dict("is_keyerror"_a=false, "message"_a="not set");
py::exec(R"(
try:
import throw_error_already_set
except ImportError as e:
is_keyerror = type(e.__cause__) == KeyError
message = str(e.__cause__)
)", py::globals(), locals);
REQUIRE(locals["is_keyerror"].cast<bool>() == true);
REQUIRE(locals["message"].cast<std::string>() == "'missing'");
#else
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: KeyError")); Catch::Contains("ImportError: KeyError"));
#endif
} }
TEST_CASE("There can be only one interpreter") { TEST_CASE("There can be only one interpreter") {
...@@ -283,3 +302,25 @@ TEST_CASE("Reload module from file") { ...@@ -283,3 +302,25 @@ TEST_CASE("Reload module from file") {
result = module_.attr("test")().cast<int>(); result = module_.attr("test")().cast<int>();
REQUIRE(result == 2); REQUIRE(result == 2);
} }
TEST_CASE("sys.argv gets initialized properly") {
py::finalize_interpreter();
{
py::scoped_interpreter default_scope;
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().empty());
}
{
char *argv[] = {strdup("a.out")};
py::scoped_interpreter argv_scope(true, 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();
}
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys
from widget_module import Widget from widget_module import Widget
...@@ -8,3 +10,6 @@ class DerivedWidget(Widget): ...@@ -8,3 +10,6 @@ class DerivedWidget(Widget):
def the_answer(self): def the_answer(self):
return 42 return 42
def argv0(self):
return sys.argv[0]
...@@ -84,4 +84,65 @@ TEST_SUBMODULE(enums, m) { ...@@ -84,4 +84,65 @@ TEST_SUBMODULE(enums, m) {
.value("ONE", SimpleEnum::THREE) .value("ONE", SimpleEnum::THREE)
.export_values(); .export_values();
}); });
// test_enum_scalar
enum UnscopedUCharEnum : unsigned char {};
enum class ScopedShortEnum : short {};
enum class ScopedLongEnum : long {};
enum UnscopedUInt64Enum : std::uint64_t {};
static_assert(py::detail::all_of<
std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>,
std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>,
std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>,
std::is_same<py::enum_<UnscopedUInt64Enum>::Scalar, std::uint64_t>
>::value, "Error during the deduction of enum's scalar type with normal integer underlying");
// test_enum_scalar_with_char_underlying
enum class ScopedCharEnum : char { Zero, Positive };
enum class ScopedWCharEnum : wchar_t { Zero, Positive };
enum class ScopedChar32Enum : char32_t { Zero, Positive };
enum class ScopedChar16Enum : char16_t { Zero, Positive };
// test the scalar of char type enums according to chapter 'Character types'
// from https://en.cppreference.com/w/cpp/language/types
static_assert(py::detail::any_of<
std::is_same<py::enum_<ScopedCharEnum>::Scalar, signed char>, // e.g. gcc on x86
std::is_same<py::enum_<ScopedCharEnum>::Scalar, unsigned char> // e.g. arm linux
>::value, "char should be cast to either signed char or unsigned char");
static_assert(
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2 ||
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 4
, "wchar_t should be either 16 bits (Windows) or 32 (everywhere else)");
static_assert(py::detail::all_of<
std::is_same<py::enum_<ScopedChar32Enum>::Scalar, std::uint_least32_t>,
std::is_same<py::enum_<ScopedChar16Enum>::Scalar, std::uint_least16_t>
>::value, "char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined");
#if defined(PYBIND11_HAS_U8STRING)
enum class ScopedChar8Enum : char8_t { Zero, Positive };
static_assert(std::is_same<py::enum_<ScopedChar8Enum>::Scalar, unsigned char>::value);
#endif
// test_char_underlying_enum
py::enum_<ScopedCharEnum>(m, "ScopedCharEnum")
.value("Zero", ScopedCharEnum::Zero)
.value("Positive", ScopedCharEnum::Positive);
py::enum_<ScopedWCharEnum>(m, "ScopedWCharEnum")
.value("Zero", ScopedWCharEnum::Zero)
.value("Positive", ScopedWCharEnum::Positive);
py::enum_<ScopedChar32Enum>(m, "ScopedChar32Enum")
.value("Zero", ScopedChar32Enum::Zero)
.value("Positive", ScopedChar32Enum::Positive);
py::enum_<ScopedChar16Enum>(m, "ScopedChar16Enum")
.value("Zero", ScopedChar16Enum::Zero)
.value("Positive", ScopedChar16Enum::Positive);
// test_bool_underlying_enum
enum class ScopedBoolEnum : bool { FALSE, TRUE };
// bool is unsigned (std::is_signed returns false) and 1-byte long, so represented with u8
static_assert(std::is_same<py::enum_<ScopedBoolEnum>::Scalar, std::uint8_t>::value, "");
py::enum_<ScopedBoolEnum>(m, "ScopedBoolEnum")
.value("FALSE", ScopedBoolEnum::FALSE)
.value("TRUE", ScopedBoolEnum::TRUE);
} }
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
import env
from pybind11_tests import enums as m from pybind11_tests import enums as m
...@@ -217,10 +219,16 @@ def test_binary_operators(): ...@@ -217,10 +219,16 @@ def test_binary_operators():
def test_enum_to_int(): def test_enum_to_int():
m.test_enum_to_int(m.Flags.Read) m.test_enum_to_int(m.Flags.Read)
m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_int(m.ScopedCharEnum.Positive)
m.test_enum_to_int(m.ScopedBoolEnum.TRUE)
m.test_enum_to_uint(m.Flags.Read) m.test_enum_to_uint(m.Flags.Read)
m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode) m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_uint(m.ScopedCharEnum.Positive)
m.test_enum_to_uint(m.ScopedBoolEnum.TRUE)
m.test_enum_to_long_long(m.Flags.Read) m.test_enum_to_long_long(m.Flags.Read)
m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode) m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_long_long(m.ScopedCharEnum.Positive)
m.test_enum_to_long_long(m.ScopedBoolEnum.TRUE)
def test_duplicate_enum_name(): def test_duplicate_enum_name():
...@@ -229,6 +237,34 @@ def test_duplicate_enum_name(): ...@@ -229,6 +237,34 @@ def test_duplicate_enum_name():
assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!' assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!'
def test_char_underlying_enum(): # Issue #1331/PR #1334:
assert type(m.ScopedCharEnum.Positive.__int__()) is int
assert int(m.ScopedChar16Enum.Zero) == 0
assert hash(m.ScopedChar32Enum.Positive) == 1
if env.PY2:
assert m.ScopedCharEnum.Positive.__getstate__() == 1 # long
else:
assert type(m.ScopedCharEnum.Positive.__getstate__()) is int
assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive
with pytest.raises(TypeError):
# Even if the underlying type is char, only an int can be used to construct the enum:
m.ScopedCharEnum("0")
def test_bool_underlying_enum():
assert type(m.ScopedBoolEnum.TRUE.__int__()) is int
assert int(m.ScopedBoolEnum.FALSE) == 0
assert hash(m.ScopedBoolEnum.TRUE) == 1
if env.PY2:
assert m.ScopedBoolEnum.TRUE.__getstate__() == 1 # long
else:
assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int
assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE
# Enum could construct with a bool
# (bool is a strict subclass of int, and False will be converted to 0)
assert m.ScopedBoolEnum(False) == m.ScopedBoolEnum.FALSE
def test_docstring_signatures(): def test_docstring_signatures():
for enum_type in [m.ScopedEnum, m.UnscopedEnum]: for enum_type in [m.ScopedEnum, m.UnscopedEnum]:
for attr in enum_type.__dict__.values(): for attr in enum_type.__dict__.values():
......
...@@ -98,4 +98,22 @@ TEST_SUBMODULE(eval_, m) { ...@@ -98,4 +98,22 @@ TEST_SUBMODULE(eval_, m) {
auto int_class = py::eval("isinstance(42, int)", global); auto int_class = py::eval("isinstance(42, int)", global);
return global; return global;
}); });
// test_eval_closure
m.def("test_eval_closure", []() {
py::dict global;
global["closure_value"] = 42;
py::dict local;
local["closure_value"] = 0;
py::exec(R"(
local_value = closure_value
def func_global():
return closure_value
def func_local():
return local_value
)", global, local);
return std::make_pair(global, local);
});
} }
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