Merge branch 'master' into annotated_any

This commit is contained in:
Ralf W. Grosse-Kunstleve 2024-03-26 23:20:13 -07:00
commit 1e6bea27f7
31 changed files with 393 additions and 132 deletions

View File

@ -4,4 +4,12 @@ updates:
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:
interval: "daily" interval: "weekly"
groups:
actions:
patterns:
- "*"
ignore:
- dependency-name: actions/checkout
versions:
- "<5"

View File

@ -86,11 +86,11 @@ jobs:
run: brew install boost run: brew install boost
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Cache wheels - name: Cache wheels
if: runner.os == 'macOS' if: runner.os == 'macOS'
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
# This path is specific to macOS - we really only need it for PyPy NumPy wheels # This path is specific to macOS - we really only need it for PyPy NumPy wheels
# See https://github.com/actions/cache/blob/master/examples.md#python---pip # See https://github.com/actions/cache/blob/master/examples.md#python---pip
@ -108,12 +108,14 @@ jobs:
run: python -m pip install pytest-github-actions-annotate-failures run: python -m pip install pytest-github-actions-annotate-failures
# First build - C++11 mode and inplace # First build - C++11 mode and inplace
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here. # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here
# (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime).
- name: Configure C++11 ${{ matrix.args }} - name: Configure C++11 ${{ matrix.args }}
run: > run: >
cmake -S . -B . cmake -S . -B .
-DPYBIND11_WERROR=ON -DPYBIND11_WERROR=ON
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
-DPYBIND11_NUMPY_1_ONLY=ON
-DDOWNLOAD_CATCH=ON -DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON -DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=11 -DCMAKE_CXX_STANDARD=11
@ -138,11 +140,13 @@ jobs:
# Second build - C++17 mode and in a build directory # Second build - C++17 mode and in a build directory
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here. # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here.
# (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime).
- name: Configure C++17 - name: Configure C++17
run: > run: >
cmake -S . -B build2 cmake -S . -B build2
-DPYBIND11_WERROR=ON -DPYBIND11_WERROR=ON
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
-DPYBIND11_NUMPY_1_ONLY=ON
-DDOWNLOAD_CATCH=ON -DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON -DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD=17
@ -214,11 +218,11 @@ jobs:
debug: ${{ matrix.python-debug }} debug: ${{ matrix.python-debug }}
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Valgrind cache - name: Valgrind cache
if: matrix.valgrind if: matrix.valgrind
uses: actions/cache@v3 uses: actions/cache@v4
id: cache-valgrind id: cache-valgrind
with: with:
path: valgrind path: valgrind
@ -484,7 +488,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.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Configure - name: Configure
shell: bash shell: bash
@ -529,8 +533,6 @@ jobs:
# Testing on ICC using the oneAPI apt repo # Testing on ICC using the oneAPI apt repo
icc: icc:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
strategy:
fail-fast: false
name: "🐍 3 • ICC latest • x64" name: "🐍 3 • ICC latest • x64"
@ -662,6 +664,11 @@ jobs:
run: | run: |
python3 -m pip install cmake -r tests/requirements.txt python3 -m pip install cmake -r tests/requirements.txt
- name: Ensure NumPy 2 is used (required Python >= 3.9)
if: matrix.container == 'almalinux:9'
run: |
python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1'
- name: Configure - name: Configure
shell: bash shell: bash
run: > run: >
@ -797,10 +804,10 @@ jobs:
architecture: x86 architecture: x86
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.12.1 uses: ilammy/msvc-dev-cmd@v1.13.0
with: with:
arch: x86 arch: x86
@ -850,10 +857,10 @@ jobs:
architecture: x86 architecture: x86
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.12.1 uses: ilammy/msvc-dev-cmd@v1.13.0
with: with:
arch: x86 arch: x86
@ -897,11 +904,13 @@ jobs:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Prepare env - name: Prepare env
# Ensure use of NumPy 2 (via NumPy nightlies but can be changed soon)
run: | run: |
python3 -m pip install -r tests/requirements.txt python3 -m pip install -r tests/requirements.txt
python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1'
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Configure C++20 - name: Configure C++20
run: > run: >
@ -959,7 +968,6 @@ jobs:
mingw-w64-${{matrix.env}}-gcc mingw-w64-${{matrix.env}}-gcc
mingw-w64-${{matrix.env}}-python-pip mingw-w64-${{matrix.env}}-python-pip
mingw-w64-${{matrix.env}}-python-numpy mingw-w64-${{matrix.env}}-python-numpy
mingw-w64-${{matrix.env}}-python-scipy
mingw-w64-${{matrix.env}}-cmake mingw-w64-${{matrix.env}}-cmake
mingw-w64-${{matrix.env}}-make mingw-w64-${{matrix.env}}-make
mingw-w64-${{matrix.env}}-python-pytest mingw-w64-${{matrix.env}}-python-pytest
@ -967,6 +975,14 @@ jobs:
mingw-w64-${{matrix.env}}-boost mingw-w64-${{matrix.env}}-boost
mingw-w64-${{matrix.env}}-catch mingw-w64-${{matrix.env}}-catch
- uses: msys2/setup-msys2@v2
if: matrix.sys == 'mingw64'
with:
msystem: ${{matrix.sys}}
install: >-
git
mingw-w64-${{matrix.env}}-python-scipy
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Configure C++11 - name: Configure C++11
@ -1058,7 +1074,7 @@ jobs:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Install ninja-build tool - name: Install ninja-build tool
uses: seanmiddleditch/gha-setup-ninja@v4 uses: seanmiddleditch/gha-setup-ninja@v4
@ -1128,7 +1144,7 @@ jobs:
run: clang++ --version run: clang++ --version
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Run pip installs - name: Run pip installs
run: | run: |

View File

@ -63,7 +63,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.14 uses: jwlawson/actions-setup-cmake@v2.0
with: with:
cmake-version: ${{ matrix.cmake }} cmake-version: ${{ matrix.cmake }}

View File

@ -31,7 +31,7 @@ jobs:
python-version: "3.x" python-version: "3.x"
- name: Add matchers - name: Add matchers
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
- uses: pre-commit/action@v3.0.0 - uses: pre-commit/action@v3.0.1
with: with:
# Slow hooks are marked with manual - slow is okay here, run them too # Slow hooks are marked with manual - slow is okay here, run them too
extra_args: --hook-stage manual --all-files extra_args: --hook-stage manual --all-files

View File

@ -36,7 +36,7 @@ jobs:
run: sudo apt-get install libboost-dev run: sudo apt-get install libboost-dev
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v2.0
- name: Run pip installs - name: Run pip installs
run: | run: |

View File

@ -32,7 +32,7 @@ repos:
# Ruff, the Python auto-correcting linter/formatter written in Rust # Ruff, the Python auto-correcting linter/formatter written in Rust
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6 rev: v0.2.0
hooks: hooks:
- id: ruff - id: ruff
args: ["--fix", "--show-fixes"] args: ["--fix", "--show-fixes"]
@ -40,7 +40,7 @@ repos:
# Check static types with mypy # Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.7.1" rev: "v1.8.0"
hooks: hooks:
- id: mypy - id: mypy
args: [] args: []
@ -93,7 +93,7 @@ repos:
# Avoid directional quotes # Avoid directional quotes
- repo: https://github.com/sirosen/texthooks - repo: https://github.com/sirosen/texthooks
rev: "0.6.3" rev: "0.6.4"
hooks: hooks:
- id: fix-ligatures - id: fix-ligatures
- id: fix-smartquotes - id: fix-smartquotes
@ -142,7 +142,14 @@ repos:
# PyLint has native support - not always usable, but works for us # PyLint has native support - not always usable, but works for us
- repo: https://github.com/PyCQA/pylint - repo: https://github.com/PyCQA/pylint
rev: "v3.0.1" rev: "v3.0.3"
hooks: hooks:
- id: pylint - id: pylint
files: ^pybind11 files: ^pybind11
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.0
hooks:
- id: check-readthedocs
- id: check-github-workflows
- id: check-dependabot

View File

@ -109,6 +109,8 @@ 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)
option(PYBIND11_SIMPLE_GIL_MANAGEMENT option(PYBIND11_SIMPLE_GIL_MANAGEMENT
"Use simpler GIL management logic that does not support disassociation" OFF) "Use simpler GIL management logic that does not support disassociation" OFF)
option(PYBIND11_NUMPY_1_ONLY
"Disable NumPy 2 support to avoid changes to previous pybind11 versions." OFF)
set(PYBIND11_INTERNALS_VERSION set(PYBIND11_INTERNALS_VERSION
"" ""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.") CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
@ -116,6 +118,9 @@ set(PYBIND11_INTERNALS_VERSION
if(PYBIND11_SIMPLE_GIL_MANAGEMENT) if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT) add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
endif() endif()
if(PYBIND11_NUMPY_1_ONLY)
add_compile_definitions(PYBIND11_NUMPY_1_ONLY)
endif()
cmake_dependent_option( cmake_dependent_option(
USE_PYTHON_INCLUDE_DIR USE_PYTHON_INCLUDE_DIR

View File

@ -639,3 +639,11 @@ cross-project dependency management. Additionally, it is able to autogenerate
customizable pybind11-based wrappers by parsing C++ header files. customizable pybind11-based wrappers by parsing C++ header files.
.. [robotpy-build] https://robotpy-build.readthedocs.io .. [robotpy-build] https://robotpy-build.readthedocs.io
[litgen]_ is an automatic python bindings generator with a focus on generating
documented and discoverable bindings: bindings will nicely reproduce the documentation
found in headers. It is is based on srcML (srcml.org), a highly scalable, multi-language
parsing tool with a developer centric approach. The API that you want to expose to python
must be C++14 compatible (but your implementation can use more modern constructs).
.. [litgen] https://pthom.github.io/litgen

View File

@ -327,8 +327,9 @@ public:
value = false; value = false;
return true; return true;
} }
if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) { if (convert || is_numpy_bool(src)) {
// (allow non-implicit conversion for numpy booleans) // (allow non-implicit conversion for numpy booleans), use strncmp
// since NumPy 1.x had an additional trailing underscore.
Py_ssize_t res = -1; Py_ssize_t res = -1;
if (src.is_none()) { if (src.is_none()) {
@ -360,6 +361,15 @@ public:
return handle(src ? Py_True : Py_False).inc_ref(); return handle(src ? Py_True : Py_False).inc_ref();
} }
PYBIND11_TYPE_CASTER(bool, const_name("bool")); PYBIND11_TYPE_CASTER(bool, const_name("bool"));
private:
// Test if an object is a NumPy boolean (without fetching the type).
static inline bool is_numpy_bool(handle object) {
const char *type_name = Py_TYPE(object.ptr())->tp_name;
// Name changed to `numpy.bool` in NumPy 2, `numpy.bool_` is needed for 1.x support
return std::strcmp("numpy.bool", type_name) == 0
|| std::strcmp("numpy.bool_", type_name) == 0;
}
}; };
// Helper class for UTF-{8,16,32} C++ stl strings: // Helper class for UTF-{8,16,32} C++ stl strings:

View File

@ -296,6 +296,10 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
# undef copysign # undef copysign
#endif #endif
#if defined(PYBIND11_NUMPY_1_ONLY)
# define PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED
#endif
#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) #if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# define PYBIND11_SIMPLE_GIL_MANAGEMENT # define PYBIND11_SIMPLE_GIL_MANAGEMENT
#endif #endif

View File

@ -67,9 +67,14 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// `Py_LIMITED_API` anyway. // `Py_LIMITED_API` anyway.
# if PYBIND11_INTERNALS_VERSION > 4 # if PYBIND11_INTERNALS_VERSION > 4
# define PYBIND11_TLS_KEY_REF Py_tss_t & # define PYBIND11_TLS_KEY_REF Py_tss_t &
# if defined(__GNUC__) && !defined(__INTEL_COMPILER) # if defined(__clang__)
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer # define PYBIND11_TLS_KEY_INIT(var) \
// for every field. _Pragma("clang diagnostic push") /**/ \
_Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
Py_tss_t var \
= Py_tss_NEEDS_INIT; \
_Pragma("clang diagnostic pop")
# elif defined(__GNUC__) && !defined(__INTEL_COMPILER)
# define PYBIND11_TLS_KEY_INIT(var) \ # define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("GCC diagnostic push") /**/ \ _Pragma("GCC diagnostic push") /**/ \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \

View File

@ -29,10 +29,15 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#if defined(PYBIND11_NUMPY_1_ONLY) && !defined(PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED)
# error PYBIND11_NUMPY_1_ONLY must be defined before any pybind11 header is included.
#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
upon the library user. */ upon the library user.
Note that NumPy 2 now uses ssize_t for `npy_intp` to simplify this. */
static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t");
static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed"); static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
// We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares) // We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares)
@ -59,7 +64,8 @@ struct handle_type_name<array> {
template <typename type, typename SFINAE = void> template <typename type, typename SFINAE = void>
struct npy_format_descriptor; struct npy_format_descriptor;
struct PyArrayDescr_Proxy { /* NumPy 1 proxy (always includes legacy fields) */
struct PyArrayDescr1_Proxy {
PyObject_HEAD PyObject_HEAD
PyObject *typeobj; PyObject *typeobj;
char kind; char kind;
@ -74,6 +80,43 @@ struct PyArrayDescr_Proxy {
PyObject *names; PyObject *names;
}; };
#ifndef PYBIND11_NUMPY_1_ONLY
struct PyArrayDescr_Proxy {
PyObject_HEAD
PyObject *typeobj;
char kind;
char type;
char byteorder;
char _former_flags;
int type_num;
/* Additional fields are NumPy version specific. */
};
#else
/* NumPy 1.x only, we can expose all fields */
using PyArrayDescr_Proxy = PyArrayDescr1_Proxy;
#endif
/* NumPy 2 proxy, including legacy fields */
struct PyArrayDescr2_Proxy {
PyObject_HEAD
PyObject *typeobj;
char kind;
char type;
char byteorder;
char _former_flags;
int type_num;
std::uint64_t flags;
ssize_t elsize;
ssize_t alignment;
PyObject *metadata;
Py_hash_t hash;
void *reserved_null[2];
/* The following fields only exist if 0 <= type_num < 2056 */
char *subarray;
PyObject *fields;
PyObject *names;
};
struct PyArray_Proxy { struct PyArray_Proxy {
PyObject_HEAD PyObject_HEAD
char *data; char *data;
@ -137,6 +180,14 @@ PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name
object numpy_version = numpy_lib.attr("NumpyVersion")(version_string); object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
int major_version = numpy_version.attr("major").cast<int>(); int major_version = numpy_version.attr("major").cast<int>();
#ifdef PYBIND11_NUMPY_1_ONLY
if (major_version >= 2) {
throw std::runtime_error(
"This extension was built with PYBIND11_NUMPY_1_ONLY defined, "
"but NumPy 2 is used in this process. For NumPy2 compatibility, "
"this extension needs to be rebuilt without the PYBIND11_NUMPY_1_ONLY define.");
}
#endif
/* `numpy.core` was renamed to `numpy._core` in NumPy 2.0 as it officially /* `numpy.core` was renamed to `numpy._core` in NumPy 2.0 as it officially
became a private module. */ became a private module. */
std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core"; std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core";
@ -209,6 +260,8 @@ struct npy_api {
NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
}; };
unsigned int PyArray_RUNTIME_VERSION_;
struct PyArray_Dims { struct PyArray_Dims {
Py_intptr_t *ptr; Py_intptr_t *ptr;
int len; int len;
@ -247,6 +300,7 @@ struct npy_api {
PyObject *(*PyArray_FromAny_)(PyObject *, PyObject *, int, int, int, PyObject *); PyObject *(*PyArray_FromAny_)(PyObject *, PyObject *, int, int, int, PyObject *);
int (*PyArray_DescrConverter_)(PyObject *, PyObject **); int (*PyArray_DescrConverter_)(PyObject *, PyObject **);
bool (*PyArray_EquivTypes_)(PyObject *, PyObject *); bool (*PyArray_EquivTypes_)(PyObject *, PyObject *);
#ifdef PYBIND11_NUMPY_1_ONLY
int (*PyArray_GetArrayParamsFromObject_)(PyObject *, int (*PyArray_GetArrayParamsFromObject_)(PyObject *,
PyObject *, PyObject *,
unsigned char, unsigned char,
@ -255,6 +309,7 @@ struct npy_api {
Py_intptr_t *, Py_intptr_t *,
PyObject **, PyObject **,
PyObject *); PyObject *);
#endif
PyObject *(*PyArray_Squeeze_)(PyObject *); PyObject *(*PyArray_Squeeze_)(PyObject *);
// 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 *);
@ -272,7 +327,8 @@ private:
API_PyArray_DescrFromScalar = 57, API_PyArray_DescrFromScalar = 57,
API_PyArray_FromAny = 69, API_PyArray_FromAny = 69,
API_PyArray_Resize = 80, API_PyArray_Resize = 80,
API_PyArray_CopyInto = 82, // CopyInto was slot 82 and 50 was effectively an alias. NumPy 2 removed 82.
API_PyArray_CopyInto = 50,
API_PyArray_NewCopy = 85, API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94, API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 96, API_PyArray_DescrNewFromType = 96,
@ -281,7 +337,9 @@ private:
API_PyArray_View = 137, API_PyArray_View = 137,
API_PyArray_DescrConverter = 174, API_PyArray_DescrConverter = 174,
API_PyArray_EquivTypes = 182, API_PyArray_EquivTypes = 182,
#ifdef PYBIND11_NUMPY_1_ONLY
API_PyArray_GetArrayParamsFromObject = 278, API_PyArray_GetArrayParamsFromObject = 278,
#endif
API_PyArray_SetBaseObject = 282 API_PyArray_SetBaseObject = 282
}; };
@ -296,7 +354,8 @@ private:
npy_api api; npy_api api;
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) { api.PyArray_RUNTIME_VERSION_ = api.PyArray_GetNDArrayCFeatureVersion_();
if (api.PyArray_RUNTIME_VERSION_ < 0x7) {
pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0");
} }
DECL_NPY_API(PyArray_Type); DECL_NPY_API(PyArray_Type);
@ -315,7 +374,9 @@ private:
DECL_NPY_API(PyArray_View); 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);
#ifdef PYBIND11_NUMPY_1_ONLY
DECL_NPY_API(PyArray_GetArrayParamsFromObject); DECL_NPY_API(PyArray_GetArrayParamsFromObject);
#endif
DECL_NPY_API(PyArray_SetBaseObject); DECL_NPY_API(PyArray_SetBaseObject);
#undef DECL_NPY_API #undef DECL_NPY_API
@ -337,6 +398,14 @@ inline const PyArrayDescr_Proxy *array_descriptor_proxy(const PyObject *ptr) {
return reinterpret_cast<const PyArrayDescr_Proxy *>(ptr); return reinterpret_cast<const PyArrayDescr_Proxy *>(ptr);
} }
inline const PyArrayDescr1_Proxy *array_descriptor1_proxy(const PyObject *ptr) {
return reinterpret_cast<const PyArrayDescr1_Proxy *>(ptr);
}
inline const PyArrayDescr2_Proxy *array_descriptor2_proxy(const PyObject *ptr) {
return reinterpret_cast<const PyArrayDescr2_Proxy *>(ptr);
}
inline bool check_flags(const void *ptr, int flag) { inline bool check_flags(const void *ptr, int flag) {
return (flag == (array_proxy(ptr)->flags & flag)); return (flag == (array_proxy(ptr)->flags & flag));
} }
@ -616,10 +685,32 @@ public:
} }
/// Size of the data type in bytes. /// Size of the data type in bytes.
#ifdef PYBIND11_NUMPY_1_ONLY
ssize_t itemsize() const { return detail::array_descriptor_proxy(m_ptr)->elsize; } ssize_t itemsize() const { return detail::array_descriptor_proxy(m_ptr)->elsize; }
#else
ssize_t itemsize() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return detail::array_descriptor1_proxy(m_ptr)->elsize;
}
return detail::array_descriptor2_proxy(m_ptr)->elsize;
}
#endif
/// Returns true for structured data types. /// Returns true for structured data types.
#ifdef PYBIND11_NUMPY_1_ONLY
bool has_fields() const { return detail::array_descriptor_proxy(m_ptr)->names != nullptr; } bool has_fields() const { return detail::array_descriptor_proxy(m_ptr)->names != nullptr; }
#else
bool has_fields() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return detail::array_descriptor1_proxy(m_ptr)->names != nullptr;
}
const auto *proxy = detail::array_descriptor2_proxy(m_ptr);
if (proxy->type_num < 0 || proxy->type_num >= 2056) {
return false;
}
return proxy->names != nullptr;
}
#endif
/// Single-character code for dtype's kind. /// Single-character code for dtype's kind.
/// For example, floating point types are 'f' and integral types are 'i'. /// For example, floating point types are 'f' and integral types are 'i'.
@ -645,11 +736,29 @@ public:
/// Single character for byteorder /// Single character for byteorder
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; } char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
/// Alignment of the data type /// Alignment of the data type
#ifdef PYBIND11_NUMPY_1_ONLY
int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; } int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; }
#else
ssize_t alignment() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return detail::array_descriptor1_proxy(m_ptr)->alignment;
}
return detail::array_descriptor2_proxy(m_ptr)->alignment;
}
#endif
/// Flags for the array descriptor /// Flags for the array descriptor
#ifdef PYBIND11_NUMPY_1_ONLY
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; } char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
#else
std::uint64_t flags() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return (unsigned char) detail::array_descriptor1_proxy(m_ptr)->flags;
}
return detail::array_descriptor2_proxy(m_ptr)->flags;
}
#endif
private: private:
static object &_dtype_from_pep3118() { static object &_dtype_from_pep3118() {
@ -816,9 +925,7 @@ public:
} }
/// Byte size of a single element /// Byte size of a single element
ssize_t itemsize() const { ssize_t itemsize() const { return dtype().itemsize(); }
return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize;
}
/// Total number of bytes /// Total number of bytes
ssize_t nbytes() const { return size() * itemsize(); } ssize_t nbytes() const { return size() * itemsize(); }

View File

@ -172,7 +172,7 @@ struct list_caster {
auto s = reinterpret_borrow<sequence>(src); auto s = reinterpret_borrow<sequence>(src);
value.clear(); value.clear();
reserve_maybe(s, &value); reserve_maybe(s, &value);
for (auto it : s) { for (const auto &it : s) {
value_conv conv; value_conv conv;
if (!conv.load(it, convert)) { if (!conv.load(it, convert)) {
return false; return false;
@ -247,7 +247,7 @@ public:
return false; return false;
} }
size_t ctr = 0; size_t ctr = 0;
for (auto it : l) { for (const auto &it : l) {
value_conv conv; value_conv conv;
if (!conv.load(it, convert)) { if (!conv.load(it, convert)) {
return false; return false;

View File

@ -645,49 +645,50 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name)
"Return the canonical string representation of this map."); "Return the canonical string representation of this map.");
} }
template <typename KeyType>
struct keys_view { struct keys_view {
virtual size_t len() = 0; virtual size_t len() = 0;
virtual iterator iter() = 0; virtual iterator iter() = 0;
virtual bool contains(const KeyType &k) = 0; virtual bool contains(const handle &k) = 0;
virtual bool contains(const object &k) = 0;
virtual ~keys_view() = default; virtual ~keys_view() = default;
}; };
template <typename MappedType>
struct values_view { struct values_view {
virtual size_t len() = 0; virtual size_t len() = 0;
virtual iterator iter() = 0; virtual iterator iter() = 0;
virtual ~values_view() = default; virtual ~values_view() = default;
}; };
template <typename KeyType, typename MappedType>
struct items_view { struct items_view {
virtual size_t len() = 0; virtual size_t len() = 0;
virtual iterator iter() = 0; virtual iterator iter() = 0;
virtual ~items_view() = default; virtual ~items_view() = default;
}; };
template <typename Map, typename KeysView> template <typename Map>
struct KeysViewImpl : public KeysView { struct KeysViewImpl : public detail::keys_view {
explicit KeysViewImpl(Map &map) : map(map) {} explicit KeysViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); } size_t len() override { return map.size(); }
iterator iter() override { return make_key_iterator(map.begin(), map.end()); } iterator iter() override { return make_key_iterator(map.begin(), map.end()); }
bool contains(const typename Map::key_type &k) override { return map.find(k) != map.end(); } bool contains(const handle &k) override {
bool contains(const object &) override { return false; } try {
return map.find(k.template cast<typename Map::key_type>()) != map.end();
} catch (const cast_error &) {
return false;
}
}
Map &map; Map &map;
}; };
template <typename Map, typename ValuesView> template <typename Map>
struct ValuesViewImpl : public ValuesView { struct ValuesViewImpl : public detail::values_view {
explicit ValuesViewImpl(Map &map) : map(map) {} explicit ValuesViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); } size_t len() override { return map.size(); }
iterator iter() override { return make_value_iterator(map.begin(), map.end()); } iterator iter() override { return make_value_iterator(map.begin(), map.end()); }
Map &map; Map &map;
}; };
template <typename Map, typename ItemsView> template <typename Map>
struct ItemsViewImpl : public ItemsView { struct ItemsViewImpl : public detail::items_view {
explicit ItemsViewImpl(Map &map) : map(map) {} explicit ItemsViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); } size_t len() override { return map.size(); }
iterator iter() override { return make_iterator(map.begin(), map.end()); } iterator iter() override { return make_iterator(map.begin(), map.end()); }
@ -700,11 +701,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 StrippedKeyType = detail::remove_cvref_t<KeyType>; using KeysView = detail::keys_view;
using StrippedMappedType = detail::remove_cvref_t<MappedType>; using ValuesView = detail::values_view;
using KeysView = detail::keys_view<StrippedKeyType>; using ItemsView = detail::items_view;
using ValuesView = detail::values_view<StrippedMappedType>;
using ItemsView = detail::items_view<StrippedKeyType, StrippedMappedType>;
using Class_ = class_<Map, holder_type>; using Class_ = class_<Map, holder_type>;
// If either type is a non-module-local bound type then make the map binding non-local as well; // If either type is a non-module-local bound type then make the map binding non-local as well;
@ -718,39 +717,20 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
} }
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
static constexpr auto key_type_descr = detail::make_caster<KeyType>::name;
static constexpr auto mapped_type_descr = detail::make_caster<MappedType>::name;
std::string key_type_name(key_type_descr.text), mapped_type_name(mapped_type_descr.text);
// If key type isn't properly wrapped, fall back to C++ names // Wrap KeysView if it wasn't already wrapped
if (key_type_name == "%") {
key_type_name = detail::type_info_description(typeid(KeyType));
}
// Similarly for value type:
if (mapped_type_name == "%") {
mapped_type_name = detail::type_info_description(typeid(MappedType));
}
// Wrap KeysView[KeyType] if it wasn't already wrapped
if (!detail::get_type_info(typeid(KeysView))) { if (!detail::get_type_info(typeid(KeysView))) {
class_<KeysView> keys_view( class_<KeysView> keys_view(scope, "KeysView", pybind11::module_local(local));
scope, ("KeysView[" + key_type_name + "]").c_str(), pybind11::module_local(local));
keys_view.def("__len__", &KeysView::len); keys_view.def("__len__", &KeysView::len);
keys_view.def("__iter__", keys_view.def("__iter__",
&KeysView::iter, &KeysView::iter,
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
); );
keys_view.def("__contains__", keys_view.def("__contains__", &KeysView::contains);
static_cast<bool (KeysView::*)(const KeyType &)>(&KeysView::contains));
// Fallback for when the object is not of the key type
keys_view.def("__contains__",
static_cast<bool (KeysView::*)(const object &)>(&KeysView::contains));
} }
// Similarly for ValuesView: // Similarly for ValuesView:
if (!detail::get_type_info(typeid(ValuesView))) { if (!detail::get_type_info(typeid(ValuesView))) {
class_<ValuesView> values_view(scope, class_<ValuesView> values_view(scope, "ValuesView", pybind11::module_local(local));
("ValuesView[" + mapped_type_name + "]").c_str(),
pybind11::module_local(local));
values_view.def("__len__", &ValuesView::len); values_view.def("__len__", &ValuesView::len);
values_view.def("__iter__", values_view.def("__iter__",
&ValuesView::iter, &ValuesView::iter,
@ -759,10 +739,7 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
} }
// Similarly for ItemsView: // Similarly for ItemsView:
if (!detail::get_type_info(typeid(ItemsView))) { if (!detail::get_type_info(typeid(ItemsView))) {
class_<ItemsView> items_view( class_<ItemsView> items_view(scope, "ItemsView", pybind11::module_local(local));
scope,
("ItemsView[" + key_type_name + ", ").append(mapped_type_name + "]").c_str(),
pybind11::module_local(local));
items_view.def("__len__", &ItemsView::len); items_view.def("__len__", &ItemsView::len);
items_view.def("__iter__", items_view.def("__iter__",
&ItemsView::iter, &ItemsView::iter,
@ -788,25 +765,19 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
cl.def( cl.def(
"keys", "keys",
[](Map &m) { [](Map &m) { return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map>(m)); },
return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map, KeysView>(m));
},
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def( cl.def(
"values", "values",
[](Map &m) { [](Map &m) { return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map>(m)); },
return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map, ValuesView>(m));
},
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def( cl.def(
"items", "items",
[](Map &m) { [](Map &m) { return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map>(m)); },
return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map, ItemsView>(m));
},
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );

View File

@ -79,6 +79,13 @@ struct handle_type_name<typing::Tuple<>> {
static constexpr auto name = const_name("tuple[()]"); static constexpr auto name = const_name("tuple[()]");
}; };
template <typename T>
struct handle_type_name<typing::Tuple<T, ellipsis>> {
// PEP 484 specifies this syntax for a variable-length tuple
static constexpr auto name
= const_name("tuple[") + make_caster<T>::name + const_name(", ...]");
};
template <typename K, typename V> template <typename K, typename V>
struct handle_type_name<typing::Dict<K, V>> { struct handle_type_name<typing::Dict<K, V>> {
static constexpr auto name = const_name("dict[") + make_caster<K>::name + const_name(", ") static constexpr auto name = const_name("dict[") + make_caster<K>::name + const_name(", ")

View File

@ -521,11 +521,15 @@ set(PYBIND11_TEST_PREFIX_COMMAND
"" ""
CACHE STRING "Put this before pytest, use for checkers and such") CACHE STRING "Put this before pytest, use for checkers and such")
set(PYBIND11_PYTEST_ARGS
""
CACHE STRING "Extra arguments for pytest")
# A single command to compile and run the tests # A single command to compile and run the tests
add_custom_target( add_custom_target(
pytest pytest
COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest
${PYBIND11_ABS_PYTEST_FILES} ${PYBIND11_ABS_PYTEST_FILES} ${PYBIND11_PYTEST_ARGS}
DEPENDS ${test_targets} DEPENDS ${test_targets}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
USES_TERMINAL) USES_TERMINAL)

View File

@ -218,4 +218,5 @@ def pytest_report_header(config):
f" {pybind11_tests.cpp_std}" f" {pybind11_tests.cpp_std}"
f" {pybind11_tests.PYBIND11_INTERNALS_ID}" f" {pybind11_tests.PYBIND11_INTERNALS_ID}"
f" PYBIND11_SIMPLE_GIL_MANAGEMENT={pybind11_tests.PYBIND11_SIMPLE_GIL_MANAGEMENT}" f" PYBIND11_SIMPLE_GIL_MANAGEMENT={pybind11_tests.PYBIND11_SIMPLE_GIL_MANAGEMENT}"
f" PYBIND11_NUMPY_1_ONLY={pybind11_tests.PYBIND11_NUMPY_1_ONLY}"
) )

View File

@ -80,10 +80,10 @@ PYBIND11_MODULE(pybind11_tests, m) {
// Intentionally kept minimal to not create a maintenance chore // Intentionally kept minimal to not create a maintenance chore
// ("just enough" to be conclusive). // ("just enough" to be conclusive).
#if defined(_MSC_FULL_VER) #if defined(__VERSION__)
m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER);
#elif defined(__VERSION__)
m.attr("compiler_info") = __VERSION__; m.attr("compiler_info") = __VERSION__;
#elif defined(_MSC_FULL_VER)
m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER);
#else #else
m.attr("compiler_info") = py::none(); m.attr("compiler_info") = py::none();
#endif #endif
@ -95,6 +95,12 @@ PYBIND11_MODULE(pybind11_tests, m) {
#else #else
false; false;
#endif #endif
m.attr("PYBIND11_NUMPY_1_ONLY") =
#if defined(PYBIND11_NUMPY_1_ONLY)
true;
#else
false;
#endif
bind_ConstructorStats(m); bind_ConstructorStats(m);

View File

@ -54,7 +54,11 @@ int f2(int x) noexcept(true) { return x + 2; }
int f3(int x) noexcept(false) { return x + 3; } int f3(int x) noexcept(false) { return x + 3; }
PYBIND11_WARNING_PUSH PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated") PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
#if defined(__clang_major__) && __clang_major__ >= 5
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated-dynamic-exception-spec")
#else
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated") PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
#endif
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true) int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
PYBIND11_WARNING_POP PYBIND11_WARNING_POP

View File

@ -608,7 +608,9 @@ def test_both_ref_mutators():
def test_nocopy_wrapper(): def test_nocopy_wrapper():
# get_elem requires a column-contiguous matrix reference, but should be # get_elem requires a column-contiguous matrix reference, but should be
# callable with other types of matrix (via copying): # callable with other types of matrix (via copying):
int_matrix_colmajor = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], order="F") int_matrix_colmajor = np.array(
[[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype="l", order="F"
)
dbl_matrix_colmajor = np.array( dbl_matrix_colmajor = np.array(
int_matrix_colmajor, dtype="double", order="F", copy=True int_matrix_colmajor, dtype="double", order="F", copy=True
) )

View File

@ -4,7 +4,7 @@ import pytest
import env import env
import pybind11_cross_module_tests as cm import pybind11_cross_module_tests as cm
import pybind11_tests # noqa: F401 import pybind11_tests
from pybind11_tests import exceptions as m from pybind11_tests import exceptions as m
@ -248,6 +248,11 @@ def test_nested_throws(capture):
assert str(excinfo.value) == "this is a helper-defined translated exception" assert str(excinfo.value) == "this is a helper-defined translated exception"
# TODO: Investigate this crash, see pybind/pybind11#5062 for background
@pytest.mark.skipif(
sys.platform.startswith("win32") and "Clang" in pybind11_tests.compiler_info,
reason="Started segfaulting February 2024",
)
def test_throw_nested_exception(): def test_throw_nested_exception():
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
m.throw_nested_exception() m.throw_nested_exception()

View File

@ -536,7 +536,12 @@ def test_format_descriptors_for_floating_point_types(test_func):
@pytest.mark.parametrize("contiguity", [None, "C", "F"]) @pytest.mark.parametrize("contiguity", [None, "C", "F"])
@pytest.mark.parametrize("noconvert", [False, True]) @pytest.mark.parametrize("noconvert", [False, True])
@pytest.mark.filterwarnings( @pytest.mark.filterwarnings(
"ignore:Casting complex values to real discards the imaginary part:numpy.ComplexWarning" "ignore:Casting complex values to real discards the imaginary part:"
+ (
"numpy.exceptions.ComplexWarning"
if hasattr(np, "exceptions")
else "numpy.ComplexWarning"
)
) )
def test_argument_conversions(forcecast, contiguity, noconvert): def test_argument_conversions(forcecast, contiguity, noconvert):
function_name = "accept_double" function_name = "accept_double"
@ -583,7 +588,8 @@ def test_argument_conversions(forcecast, contiguity, noconvert):
def test_dtype_refcount_leak(): def test_dtype_refcount_leak():
from sys import getrefcount from sys import getrefcount
dtype = np.dtype(np.float_) # Was np.float_ but that alias for float64 was removed in NumPy 2.
dtype = np.dtype(np.float64)
a = np.array([1], dtype=dtype) a = np.array([1], dtype=dtype)
before = getrefcount(dtype) before = getrefcount(dtype)
m.ndim(a) m.ndim(a)

View File

@ -405,10 +405,35 @@ TEST_SUBMODULE(numpy_dtypes, m) {
}); });
// test_dtype // test_dtype
// Below we use `L` for unsigned long as unfortunately the only name that
// works reliably on Both NumPy 2.x and old NumPy 1.x.
std::vector<const char *> dtype_names{ std::vector<const char *> dtype_names{
"byte", "short", "intc", "int_", "longlong", "ubyte", "ushort", "byte",
"uintc", "uint", "ulonglong", "half", "single", "double", "longdouble", "short",
"csingle", "cdouble", "clongdouble", "bool_", "datetime64", "timedelta64", "object_"}; "intc",
"long",
"longlong",
"ubyte",
"ushort",
"uintc",
"L",
"ulonglong",
"half",
"single",
"double",
"longdouble",
"csingle",
"cdouble",
"clongdouble",
"bool_",
"datetime64",
"timedelta64",
"object_",
// platform dependent aliases (int_ and uint are also NumPy version dependent on windows)
"int_",
"uint",
"intp",
"uintp"};
m.def("print_dtypes", []() { m.def("print_dtypes", []() {
py::list l; py::list l;

View File

@ -3,6 +3,7 @@ import re
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import PYBIND11_NUMPY_1_ONLY
from pybind11_tests import numpy_dtypes as m from pybind11_tests import numpy_dtypes as m
np = pytest.importorskip("numpy") np = pytest.importorskip("numpy")
@ -172,13 +173,20 @@ def test_dtype(simple_dtype):
np.zeros(1, m.trailing_padding_dtype()) np.zeros(1, m.trailing_padding_dtype())
) )
expected_chars = "bhilqBHILQefdgFDG?MmO" expected_chars = list("bhilqBHILQefdgFDG?MmO")
assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmO") # Note that int_ and uint size and mapping is NumPy version dependent:
expected_chars += [np.dtype(_).char for _ in ("int_", "uint", "intp", "uintp")]
assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmOiuiu")
assert m.test_dtype_char_() == list(expected_chars) assert m.test_dtype_char_() == list(expected_chars)
assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars] assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars]
assert m.test_dtype_byteorder() == [np.dtype(ch).byteorder for ch in expected_chars] assert m.test_dtype_byteorder() == [np.dtype(ch).byteorder for ch in expected_chars]
assert m.test_dtype_alignment() == [np.dtype(ch).alignment for ch in expected_chars] assert m.test_dtype_alignment() == [np.dtype(ch).alignment for ch in expected_chars]
assert m.test_dtype_flags() == [chr(np.dtype(ch).flags) for ch in expected_chars] if not PYBIND11_NUMPY_1_ONLY:
assert m.test_dtype_flags() == [np.dtype(ch).flags for ch in expected_chars]
else:
assert m.test_dtype_flags() == [
chr(np.dtype(ch).flags) for ch in expected_chars
]
def test_recarray(simple_dtype, packed_dtype): def test_recarray(simple_dtype, packed_dtype):

View File

@ -834,6 +834,8 @@ TEST_SUBMODULE(pytypes, m) {
m.def("annotate_tuple_float_str", [](const py::typing::Tuple<py::float_, py::str> &) {}); m.def("annotate_tuple_float_str", [](const py::typing::Tuple<py::float_, py::str> &) {});
m.def("annotate_tuple_empty", [](const py::typing::Tuple<> &) {}); m.def("annotate_tuple_empty", [](const py::typing::Tuple<> &) {});
m.def("annotate_tuple_variable_length",
[](const py::typing::Tuple<py::float_, py::ellipsis> &) {});
m.def("annotate_dict_str_int", [](const py::typing::Dict<py::str, int> &) {}); m.def("annotate_dict_str_int", [](const py::typing::Dict<py::str, int> &) {});
m.def("annotate_list_int", [](const py::typing::List<int> &) {}); m.def("annotate_list_int", [](const py::typing::List<int> &) {});
m.def("annotate_set_str", [](const py::typing::Set<std::string> &) {}); m.def("annotate_set_str", [](const py::typing::Set<std::string> &) {});

View File

@ -911,6 +911,13 @@ def test_tuple_empty_annotations(doc):
) )
def test_tuple_variable_length_annotations(doc):
assert (
doc(m.annotate_tuple_variable_length)
== "annotate_tuple_variable_length(arg0: tuple[float, ...]) -> None"
)
def test_dict_annotations(doc): def test_dict_annotations(doc):
assert ( assert (
doc(m.annotate_dict_str_int) doc(m.annotate_dict_str_int)

View File

@ -103,21 +103,26 @@ private:
int value; int value;
}; };
template <typename T>
std::unordered_set<T *> &pointer_set() {
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
static auto singleton = new std::unordered_set<T *>();
return *singleton;
}
// test_unique_nodelete // test_unique_nodelete
// Object with a private destructor // Object with a private destructor
class MyObject4;
std::unordered_set<MyObject4 *> myobject4_instances;
class MyObject4 { class MyObject4 {
public: public:
explicit MyObject4(int value) : value{value} { explicit MyObject4(int value) : value{value} {
print_created(this); print_created(this);
myobject4_instances.insert(this); pointer_set<MyObject4>().insert(this);
} }
int value; int value;
static void cleanupAllInstances() { static void cleanupAllInstances() {
auto tmp = std::move(myobject4_instances); auto tmp = std::move(pointer_set<MyObject4>());
myobject4_instances.clear(); pointer_set<MyObject4>().clear();
for (auto *o : tmp) { for (auto *o : tmp) {
delete o; delete o;
} }
@ -125,7 +130,7 @@ public:
private: private:
~MyObject4() { ~MyObject4() {
myobject4_instances.erase(this); pointer_set<MyObject4>().erase(this);
print_destroyed(this); print_destroyed(this);
} }
}; };
@ -133,19 +138,17 @@ private:
// test_unique_deleter // test_unique_deleter
// Object with std::unique_ptr<T, D> where D is not matching the base class // Object with std::unique_ptr<T, D> where D is not matching the base class
// Object with a protected destructor // Object with a protected destructor
class MyObject4a;
std::unordered_set<MyObject4a *> myobject4a_instances;
class MyObject4a { class MyObject4a {
public: public:
explicit MyObject4a(int i) : value{i} { explicit MyObject4a(int i) : value{i} {
print_created(this); print_created(this);
myobject4a_instances.insert(this); pointer_set<MyObject4a>().insert(this);
}; };
int value; int value;
static void cleanupAllInstances() { static void cleanupAllInstances() {
auto tmp = std::move(myobject4a_instances); auto tmp = std::move(pointer_set<MyObject4a>());
myobject4a_instances.clear(); pointer_set<MyObject4a>().clear();
for (auto *o : tmp) { for (auto *o : tmp) {
delete o; delete o;
} }
@ -153,7 +156,7 @@ public:
protected: protected:
virtual ~MyObject4a() { virtual ~MyObject4a() {
myobject4a_instances.erase(this); pointer_set<MyObject4a>().erase(this);
print_destroyed(this); print_destroyed(this);
} }
}; };

View File

@ -192,6 +192,16 @@ TEST_SUBMODULE(stl_binders, m) {
py::bind_map<std::unordered_map<std::string, double const>>(m, py::bind_map<std::unordered_map<std::string, double const>>(m,
"UnorderedMapStringDoubleConst"); "UnorderedMapStringDoubleConst");
// test_map_view_types
py::bind_map<std::map<std::string, float>>(m, "MapStringFloat");
py::bind_map<std::unordered_map<std::string, float>>(m, "UnorderedMapStringFloat");
py::bind_map<std::map<std::pair<double, int>, int32_t>>(m, "MapPairDoubleIntInt32");
py::bind_map<std::map<std::pair<double, int>, int64_t>>(m, "MapPairDoubleIntInt64");
py::bind_map<std::map<int, py::object>>(m, "MapIntObject");
py::bind_map<std::map<std::string, py::object>>(m, "MapStringObject");
py::class_<E_nc>(m, "ENC").def(py::init<int>()).def_readwrite("value", &E_nc::value); py::class_<E_nc>(m, "ENC").def(py::init<int>()).def_readwrite("value", &E_nc::value);
// test_noncopyable_containers // test_noncopyable_containers

View File

@ -317,9 +317,9 @@ def test_map_view_types():
map_string_double_const = m.MapStringDoubleConst() map_string_double_const = m.MapStringDoubleConst()
unordered_map_string_double_const = m.UnorderedMapStringDoubleConst() unordered_map_string_double_const = m.UnorderedMapStringDoubleConst()
assert map_string_double.keys().__class__.__name__ == "KeysView[str]" assert map_string_double.keys().__class__.__name__ == "KeysView"
assert map_string_double.values().__class__.__name__ == "ValuesView[float]" assert map_string_double.values().__class__.__name__ == "ValuesView"
assert map_string_double.items().__class__.__name__ == "ItemsView[str, float]" assert map_string_double.items().__class__.__name__ == "ItemsView"
keys_type = type(map_string_double.keys()) keys_type = type(map_string_double.keys())
assert type(unordered_map_string_double.keys()) is keys_type assert type(unordered_map_string_double.keys()) is keys_type
@ -336,6 +336,30 @@ def test_map_view_types():
assert type(map_string_double_const.items()) is items_type assert type(map_string_double_const.items()) is items_type
assert type(unordered_map_string_double_const.items()) is items_type assert type(unordered_map_string_double_const.items()) is items_type
map_string_float = m.MapStringFloat()
unordered_map_string_float = m.UnorderedMapStringFloat()
assert type(map_string_float.keys()) is keys_type
assert type(unordered_map_string_float.keys()) is keys_type
assert type(map_string_float.values()) is values_type
assert type(unordered_map_string_float.values()) is values_type
assert type(map_string_float.items()) is items_type
assert type(unordered_map_string_float.items()) is items_type
map_pair_double_int_int32 = m.MapPairDoubleIntInt32()
map_pair_double_int_int64 = m.MapPairDoubleIntInt64()
assert type(map_pair_double_int_int32.values()) is values_type
assert type(map_pair_double_int_int64.values()) is values_type
map_int_object = m.MapIntObject()
map_string_object = m.MapStringObject()
assert type(map_int_object.keys()) is keys_type
assert type(map_string_object.keys()) is keys_type
assert type(map_int_object.items()) is items_type
assert type(map_string_object.items()) is items_type
def test_recursive_vector(): def test_recursive_vector():
recursive_vector = m.RecursiveVector() recursive_vector = m.RecursiveVector()

View File

@ -173,12 +173,16 @@ endif()
# Check to see which Python mode we are in, new, old, or no python # Check to see which Python mode we are in, new, old, or no python
if(PYBIND11_NOPYTHON) if(PYBIND11_NOPYTHON)
set(_pybind11_nopython ON) set(_pybind11_nopython ON)
# We won't use new FindPython if PYBIND11_FINDPYTHON is defined and falselike
# Otherwise, we use if FindPythonLibs is missing or if FindPython was already used
elseif( elseif(
_pybind11_missing_old_python STREQUAL "NEW" (NOT DEFINED PYBIND11_FINDPYTHON OR PYBIND11_FINDPYTHON)
OR PYBIND11_FINDPYTHON AND (_pybind11_missing_old_python STREQUAL "NEW"
OR Python_FOUND OR PYBIND11_FINDPYTHON
OR Python2_FOUND OR Python_FOUND
OR Python3_FOUND) OR Python3_FOUND
))
# New mode # New mode
include("${CMAKE_CURRENT_LIST_DIR}/pybind11NewTools.cmake") include("${CMAKE_CURRENT_LIST_DIR}/pybind11NewTools.cmake")

View File

@ -110,15 +110,17 @@ if(NOT DEFINED ${_Python}_EXECUTABLE)
endif() endif()
if(NOT ${_Python}_EXECUTABLE STREQUAL PYBIND11_PYTHON_EXECUTABLE_LAST) if(DEFINED PYBIND11_PYTHON_EXECUTABLE_LAST AND NOT ${_Python}_EXECUTABLE STREQUAL
PYBIND11_PYTHON_EXECUTABLE_LAST)
# Detect changes to the Python version/binary in subsequent CMake runs, and refresh config if needed # Detect changes to the Python version/binary in subsequent CMake runs, and refresh config if needed
unset(PYTHON_IS_DEBUG CACHE) unset(PYTHON_IS_DEBUG CACHE)
unset(PYTHON_MODULE_EXTENSION CACHE) unset(PYTHON_MODULE_EXTENSION CACHE)
set(PYBIND11_PYTHON_EXECUTABLE_LAST
"${${_Python}_EXECUTABLE}"
CACHE INTERNAL "Python executable during the last CMake run")
endif() endif()
set(PYBIND11_PYTHON_EXECUTABLE_LAST
"${${_Python}_EXECUTABLE}"
CACHE INTERNAL "Python executable during the last CMake run")
if(NOT DEFINED PYTHON_IS_DEBUG) if(NOT DEFINED PYTHON_IS_DEBUG)
# Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter # Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter
execute_process( execute_process(