Merge branch 'master' into python_multiple_inheritance_test

This commit is contained in:
Ralf W. Grosse-Kunstleve 2023-10-30 11:37:22 -07:00
commit c51615129d
28 changed files with 303 additions and 94 deletions

View File

@ -135,7 +135,7 @@ The valid options are:
* Use `-G` and the name of a generator to use something different. `cmake * Use `-G` and the name of a generator to use something different. `cmake
--help` lists the generators available. --help` lists the generators available.
- On Unix, setting `CMAKE_GENERATER=Ninja` in your environment will give - On Unix, setting `CMAKE_GENERATER=Ninja` in your environment will give
you automatic mulithreading on all your CMake projects! you automatic multithreading on all your CMake projects!
* Open the `CMakeLists.txt` with QtCreator to generate for that IDE. * Open the `CMakeLists.txt` with QtCreator to generate for that IDE.
* You can use `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate the `.json` file * You can use `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate the `.json` file
that some tools expect. that some tools expect.

View File

@ -195,9 +195,10 @@ jobs:
matrix: matrix:
include: include:
# TODO: Fails on 3.10, investigate # TODO: Fails on 3.10, investigate
- python-version: "3.9" # JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4889
python-debug: true # - python-version: "3.9"
valgrind: true # python-debug: true
# valgrind: true
- python-version: "3.11" - python-version: "3.11"
python-debug: false python-debug: false
@ -1052,7 +1053,7 @@ jobs:
uses: jwlawson/actions-setup-cmake@v1.14 uses: jwlawson/actions-setup-cmake@v1.14
- name: Install ninja-build tool - name: Install ninja-build tool
uses: seanmiddleditch/gha-setup-ninja@v3 uses: seanmiddleditch/gha-setup-ninja@v4
- name: Run pip installs - name: Run pip installs
run: | run: |

View File

@ -25,27 +25,27 @@ repos:
# Clang format the codebase automatically # Clang format the codebase automatically
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v16.0.6" rev: "v17.0.3"
hooks: hooks:
- id: clang-format - id: clang-format
types_or: [c++, c, cuda] types_or: [c++, c, cuda]
# Black, the code formatter, natively supports pre-commit # Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black-pre-commit-mirror - repo: https://github.com/psf/black-pre-commit-mirror
rev: "23.7.0" # Keep in sync with blacken-docs rev: "23.10.1" # Keep in sync with blacken-docs
hooks: hooks:
- id: black - id: black
# Ruff, the Python auto-correcting linter written in Rust # Ruff, the Python auto-correcting linter written in Rust
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.287 rev: v0.1.2
hooks: hooks:
- id: ruff - id: ruff
args: ["--fix", "--show-fixes"] args: ["--fix", "--show-fixes"]
# 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.5.1" rev: "v1.6.1"
hooks: hooks:
- id: mypy - id: mypy
args: [] args: []
@ -67,7 +67,7 @@ repos:
# Standard hooks # Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v4.4.0" rev: "v4.5.0"
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
@ -98,7 +98,7 @@ repos:
# Avoid directional quotes # Avoid directional quotes
- repo: https://github.com/sirosen/texthooks - repo: https://github.com/sirosen/texthooks
rev: "0.5.0" rev: "0.6.2"
hooks: hooks:
- id: fix-ligatures - id: fix-ligatures
- id: fix-smartquotes - id: fix-smartquotes
@ -124,7 +124,7 @@ repos:
# Use tools/codespell_ignore_lines_from_errors.py # Use tools/codespell_ignore_lines_from_errors.py
# to rebuild .codespell-ignore-lines # to rebuild .codespell-ignore-lines
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: "v2.2.5" rev: "v2.2.6"
hooks: hooks:
- id: codespell - id: codespell
exclude: ".supp$" exclude: ".supp$"
@ -132,7 +132,7 @@ repos:
# Check for common shell mistakes # Check for common shell mistakes
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
rev: "v0.9.0.5" rev: "v0.9.0.6"
hooks: hooks:
- id: shellcheck - id: shellcheck
@ -142,12 +142,12 @@ repos:
- id: disallow-caps - id: disallow-caps
name: Disallow improper capitalization name: Disallow improper capitalization
language: pygrep language: pygrep
entry: PyBind|Numpy|Cmake|CCache|PyTest entry: PyBind|\bNumpy\b|Cmake|CCache|PyTest
exclude: ^\.pre-commit-config.yaml$ exclude: ^\.pre-commit-config.yaml$
# 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.0a7" rev: "v3.0.1"
hooks: hooks:
- id: pylint - id: pylint
files: ^pybind11 files: ^pybind11

View File

@ -5,6 +5,11 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
# Propagate this policy (FindPythonInterp removal) so it can be detected later
if(NOT CMAKE_VERSION VERSION_LESS "3.27")
cmake_policy(GET CMP0148 _pybind11_cmp0148)
endif()
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with # The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
@ -16,6 +21,11 @@ else()
cmake_policy(VERSION 3.26) cmake_policy(VERSION 3.26)
endif() endif()
if(_pybind11_cmp0148)
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
unset(_pybind11_cmp0148)
endif()
# Avoid infinite recursion if tests include this as a subdirectory # Avoid infinite recursion if tests include this as a subdirectory
if(DEFINED PYBIND11_MASTER_PROJECT) if(DEFINED PYBIND11_MASTER_PROJECT)
return() return()
@ -132,6 +142,7 @@ set(PYBIND11_HEADERS
include/pybind11/embed.h include/pybind11/embed.h
include/pybind11/eval.h include/pybind11/eval.h
include/pybind11/gil.h include/pybind11/gil.h
include/pybind11/gil_safe_call_once.h
include/pybind11/iostream.h include/pybind11/iostream.h
include/pybind11/functional.h include/pybind11/functional.h
include/pybind11/numpy.h include/pybind11/numpy.h

View File

@ -141,15 +141,14 @@ standard python RuntimeError:
.. code-block:: cpp .. code-block:: cpp
// This is a static object, so we must leak the Python reference: PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> exc_storage;
// It is undefined when the destructor will run, possibly only after the exc_storage.call_once_and_store_result(
// Python interpreter is finalized already. [&]() { return py::exception<MyCustomException>(m, "MyCustomError"); });
static py::handle exc = py::exception<MyCustomException>(m, "MyCustomError").release();
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);
} catch (const MyCustomException &e) { } catch (const MyCustomException &e) {
py::set_error(exc, e.what()); py::set_error(exc_storage.get_stored(), e.what());
} catch (const OtherException &e) { } catch (const OtherException &e) {
py::set_error(PyExc_RuntimeError, e.what()); py::set_error(PyExc_RuntimeError, e.what());
} }

View File

@ -42,13 +42,15 @@ using make_caster = type_caster<intrinsic_t<type>>;
// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
template <typename T> template <typename T>
typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) { typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) {
return caster.operator typename make_caster<T>::template cast_op_type<T>(); using result_t = typename make_caster<T>::template cast_op_type<T>; // See PR #4893
return caster.operator result_t();
} }
template <typename T> template <typename T>
typename make_caster<T>::template cast_op_type<typename std::add_rvalue_reference<T>::type> typename make_caster<T>::template cast_op_type<typename std::add_rvalue_reference<T>::type>
cast_op(make_caster<T> &&caster) { cast_op(make_caster<T> &&caster) {
return std::move(caster).operator typename make_caster<T>:: using result_t = typename make_caster<T>::template cast_op_type<
template cast_op_type<typename std::add_rvalue_reference<T>::type>(); typename std::add_rvalue_reference<T>::type>; // See PR #4893
return std::move(caster).operator result_t();
} }
template <typename type> template <typename type>

View File

@ -118,6 +118,14 @@
# endif # endif
#endif #endif
#if defined(PYBIND11_CPP20)
# define PYBIND11_CONSTINIT constinit
# define PYBIND11_DTOR_CONSTEXPR constexpr
#else
# define PYBIND11_CONSTINIT
# define PYBIND11_DTOR_CONSTEXPR
#endif
// Compiler version assertions // Compiler version assertions
#if defined(__INTEL_COMPILER) #if defined(__INTEL_COMPILER)
# if __INTEL_COMPILER < 1800 # if __INTEL_COMPILER < 1800

View File

@ -65,7 +65,7 @@ constexpr bool is_alias(void *) {
} }
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall // Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
// back to brace aggregate initiailization so that for aggregate initialization can be used with // back to brace aggregate initialization so that for aggregate initialization can be used with
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For // py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually // non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor). // works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).

View File

@ -451,6 +451,7 @@ inline object get_python_state_dict() {
#endif #endif
if (!state_dict) { if (!state_dict) {
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED"); raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
throw error_already_set();
} }
return state_dict; return state_dict;
} }
@ -463,6 +464,7 @@ inline internals **get_internals_pp_from_capsule(handle obj) {
void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr); void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr);
if (raw_ptr == nullptr) { if (raw_ptr == nullptr) {
raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED"); raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED");
throw error_already_set();
} }
return static_cast<internals **>(raw_ptr); return static_cast<internals **>(raw_ptr);
} }

View File

@ -521,8 +521,10 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
inline PyThreadState *get_thread_state_unchecked() { inline PyThreadState *get_thread_state_unchecked() {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
return PyThreadState_GET(); return PyThreadState_GET();
#else #elif PY_VERSION_HEX < 0x030D0000
return _PyThreadState_UncheckedGet(); return _PyThreadState_UncheckedGet();
#else
return PyThreadState_GetUnchecked();
#endif #endif
} }

View File

@ -11,6 +11,8 @@
#include "detail/common.h" #include "detail/common.h"
#include <cassert>
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) #if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# include "detail/internals.h" # include "detail/internals.h"
#endif #endif
@ -137,7 +139,9 @@ private:
class gil_scoped_release { class gil_scoped_release {
public: public:
// PRECONDITION: The GIL must be held when this constructor is called.
explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
assert(PyGILState_Check());
// `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`.
@ -201,7 +205,11 @@ class gil_scoped_release {
PyThreadState *state; PyThreadState *state;
public: public:
gil_scoped_release() : state{PyEval_SaveThread()} {} // PRECONDITION: The GIL must be held when this constructor is called.
gil_scoped_release() {
assert(PyGILState_Check());
state = PyEval_SaveThread();
}
gil_scoped_release(const gil_scoped_release &) = delete; gil_scoped_release(const gil_scoped_release &) = delete;
gil_scoped_release &operator=(const gil_scoped_release &) = delete; gil_scoped_release &operator=(const gil_scoped_release &) = delete;
~gil_scoped_release() { PyEval_RestoreThread(state); } ~gil_scoped_release() { PyEval_RestoreThread(state); }

View File

@ -0,0 +1,91 @@
// Copyright (c) 2023 The pybind Community.
#pragma once
#include "detail/common.h"
#include "gil.h"
#include <cassert>
#include <mutex>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Use the `gil_safe_call_once_and_store` class below instead of the naive
//
// static auto imported_obj = py::module_::import("module_name"); // BAD, DO NOT USE!
//
// which has two serious issues:
//
// 1. Py_DECREF() calls potentially after the Python interpreter was finalized already, and
// 2. deadlocks in multi-threaded processes (because of missing lock ordering).
//
// The following alternative avoids both problems:
//
// PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
// auto &imported_obj = storage // Do NOT make this `static`!
// .call_once_and_store_result([]() {
// return py::module_::import("module_name");
// })
// .get_stored();
//
// The parameter of `call_once_and_store_result()` must be callable. It can make
// CPython API calls, and in particular, it can temporarily release the GIL.
//
// `T` can be any C++ type, it does not have to involve CPython API types.
//
// The behavior with regard to signals, e.g. `SIGINT` (`KeyboardInterrupt`),
// is not ideal. If the main thread is the one to actually run the `Callable`,
// then a `KeyboardInterrupt` will interrupt it if it is running normal Python
// code. The situation is different if a non-main thread runs the
// `Callable`, and then the main thread starts waiting for it to complete:
// a `KeyboardInterrupt` will not interrupt the non-main thread, but it will
// get processed only when it is the main thread's turn again and it is running
// normal Python code. However, this will be unnoticeable for quick call-once
// functions, which is usually the case.
template <typename T>
class gil_safe_call_once_and_store {
public:
// PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called.
template <typename Callable>
gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn) {
if (!is_initialized_) { // This read is guarded by the GIL.
// Multiple threads may enter here, because the GIL is released in the next line and
// CPython API calls in the `fn()` call below may release and reacquire the GIL.
gil_scoped_release gil_rel; // Needed to establish lock ordering.
std::call_once(once_flag_, [&] {
// Only one thread will ever enter here.
gil_scoped_acquire gil_acq;
::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL.
is_initialized_ = true; // This write is guarded by the GIL.
});
// All threads will observe `is_initialized_` as true here.
}
// Intentionally not returning `T &` to ensure the calling code is self-documenting.
return *this;
}
// This must only be called after `call_once_and_store_result()` was called.
T &get_stored() {
assert(is_initialized_);
PYBIND11_WARNING_PUSH
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
// Needed for gcc 4.8.5
PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
#endif
return *reinterpret_cast<T *>(storage_);
PYBIND11_WARNING_POP
}
constexpr gil_safe_call_once_and_store() = default;
PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default;
private:
alignas(T) char storage_[sizeof(T)] = {};
std::once_flag once_flag_ = {};
bool is_initialized_ = false;
// The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
// but the latter does not have the triviality properties of former,
// therefore `std::optional` is not a viable alternative here.
};
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -10,7 +10,10 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include "detail/common.h"
#include "complex.h" #include "complex.h"
#include "gil_safe_call_once.h"
#include "pytypes.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@ -120,6 +123,20 @@ inline numpy_internals &get_numpy_internals() {
return *ptr; return *ptr;
} }
PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) {
module_ numpy = module_::import("numpy");
str version_string = numpy.attr("__version__");
module_ numpy_lib = module_::import("numpy.lib");
object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
int major_version = numpy_version.attr("major").cast<int>();
/* `numpy.core` was renamed to `numpy._core` in NumPy 2.0 as it officially
became a private module. */
std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core";
return module_::import((numpy_core_path + "." + submodule_name).c_str());
}
template <typename T> template <typename T>
struct same_size { struct same_size {
template <typename U> template <typename U>
@ -192,8 +209,8 @@ struct npy_api {
}; };
static npy_api &get() { static npy_api &get() {
static npy_api api = lookup(); PYBIND11_CONSTINIT static gil_safe_call_once_and_store<npy_api> storage;
return api; return storage.call_once_and_store_result(lookup).get_stored();
} }
bool PyArray_Check_(PyObject *obj) const { bool PyArray_Check_(PyObject *obj) const {
@ -263,9 +280,13 @@ private:
}; };
static npy_api lookup() { static npy_api lookup() {
module_ m = module_::import("numpy.core.multiarray"); module_ m = detail::import_numpy_core_submodule("multiarray");
auto c = m.attr("_ARRAY_API"); auto c = m.attr("_ARRAY_API");
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr); void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
if (api_ptr == nullptr) {
raise_from(PyExc_SystemError, "FAILURE obtaining numpy _ARRAY_API pointer.");
throw error_already_set();
}
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);
@ -625,13 +646,14 @@ public:
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; } char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
private: private:
static object _dtype_from_pep3118() { static object &_dtype_from_pep3118() {
static PyObject *obj = module_::import("numpy.core._internal") PYBIND11_CONSTINIT static gil_safe_call_once_and_store<object> storage;
.attr("_dtype_from_pep3118") return storage
.cast<object>() .call_once_and_store_result([]() {
.release() return detail::import_numpy_core_submodule("_internal")
.ptr(); .attr("_dtype_from_pep3118");
return reinterpret_borrow<object>(obj); })
.get_stored();
} }
dtype strip_padding(ssize_t itemsize) { dtype strip_padding(ssize_t itemsize) {

View File

@ -14,7 +14,9 @@
#include "detail/init.h" #include "detail/init.h"
#include "attr.h" #include "attr.h"
#include "gil.h" #include "gil.h"
#include "gil_safe_call_once.h"
#include "options.h" #include "options.h"
#include "typing.h"
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -57,12 +59,14 @@ inline std::string replace_newlines_and_squash(const char *text) {
std::string result(text); std::string result(text);
bool previous_is_whitespace = false; bool previous_is_whitespace = false;
if (result.size() >= 2) {
// Do not modify string representations // Do not modify string representations
char first_char = result[0]; char first_char = result[0];
char last_char = result[result.size() - 1]; char last_char = result[result.size() - 1];
if (first_char == last_char && first_char == '\'') { if (first_char == last_char && first_char == '\'') {
return result; return result;
} }
}
result.clear(); result.clear();
// Replace characters in whitespaces array with spaces and squash consecutive spaces // Replace characters in whitespaces array with spaces and squash consecutive spaces
@ -1140,7 +1144,7 @@ protected:
} }
msg += "kwargs: "; msg += "kwargs: ";
bool first = true; bool first = true;
for (auto kwarg : kwargs) { for (const auto &kwarg : kwargs) {
if (first) { if (first) {
first = false; first = false;
} else { } else {
@ -2445,7 +2449,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
typename Sentinel, typename Sentinel,
typename ValueType = typename detail::iterator_access<Iterator>::result_type, typename ValueType = typename detail::iterator_access<Iterator>::result_type,
typename... Extra> typename... Extra>
iterator make_iterator(Iterator first, Sentinel last, Extra &&...extra) { typing::Iterator<ValueType> make_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<detail::iterator_access<Iterator>, return detail::make_iterator_impl<detail::iterator_access<Iterator>,
Policy, Policy,
Iterator, Iterator,
@ -2463,7 +2467,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
typename Sentinel, typename Sentinel,
typename KeyType = typename detail::iterator_key_access<Iterator>::result_type, typename KeyType = typename detail::iterator_key_access<Iterator>::result_type,
typename... Extra> typename... Extra>
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) { typing::Iterator<KeyType> make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<detail::iterator_key_access<Iterator>, return detail::make_iterator_impl<detail::iterator_key_access<Iterator>,
Policy, Policy,
Iterator, Iterator,
@ -2481,7 +2485,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
typename Sentinel, typename Sentinel,
typename ValueType = typename detail::iterator_value_access<Iterator>::result_type, typename ValueType = typename detail::iterator_value_access<Iterator>::result_type,
typename... Extra> typename... Extra>
iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) { typing::Iterator<ValueType> make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<detail::iterator_value_access<Iterator>, return detail::make_iterator_impl<detail::iterator_value_access<Iterator>,
Policy, Policy,
Iterator, Iterator,
@ -2496,8 +2500,10 @@ iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
/// `std::begin()`/`std::end()` /// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal, template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type, typename Type,
typename ValueType = typename detail::iterator_access<
decltype(std::begin(std::declval<Type &>()))>::result_type,
typename... Extra> typename... Extra>
iterator make_iterator(Type &value, Extra &&...extra) { typing::Iterator<ValueType> make_iterator(Type &value, Extra &&...extra) {
return make_iterator<Policy>( return make_iterator<Policy>(
std::begin(value), std::end(value), std::forward<Extra>(extra)...); std::begin(value), std::end(value), std::forward<Extra>(extra)...);
} }
@ -2506,8 +2512,10 @@ iterator make_iterator(Type &value, Extra &&...extra) {
/// `std::begin()`/`std::end()` /// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal, template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type, typename Type,
typename KeyType = typename detail::iterator_key_access<
decltype(std::begin(std::declval<Type &>()))>::result_type,
typename... Extra> typename... Extra>
iterator make_key_iterator(Type &value, Extra &&...extra) { typing::Iterator<KeyType> make_key_iterator(Type &value, Extra &&...extra) {
return make_key_iterator<Policy>( return make_key_iterator<Policy>(
std::begin(value), std::end(value), std::forward<Extra>(extra)...); std::begin(value), std::end(value), std::forward<Extra>(extra)...);
} }
@ -2516,8 +2524,10 @@ iterator make_key_iterator(Type &value, Extra &&...extra) {
/// `std::begin()`/`std::end()` /// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal, template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type, typename Type,
typename ValueType = typename detail::iterator_value_access<
decltype(std::begin(std::declval<Type &>()))>::result_type,
typename... Extra> typename... Extra>
iterator make_value_iterator(Type &value, Extra &&...extra) { typing::Iterator<ValueType> make_value_iterator(Type &value, Extra &&...extra) {
return make_value_iterator<Policy>( return make_value_iterator<Policy>(
std::begin(value), std::end(value), std::forward<Extra>(extra)...); std::begin(value), std::end(value), std::forward<Extra>(extra)...);
} }
@ -2600,23 +2610,14 @@ public:
}; };
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
// Returns a reference to a function-local static exception object used in the simple
// register_exception approach below. (It would be simpler to have the static local variable
// directly in register_exception, but that makes clang <3.5 segfault - issue #1349).
template <typename CppException>
exception<CppException> &get_exception_object() {
static exception<CppException> ex;
return ex;
}
// Helper function for register_exception and register_local_exception // Helper function for register_exception and register_local_exception
template <typename CppException> template <typename CppException>
exception<CppException> & exception<CppException> &
register_exception_impl(handle scope, const char *name, handle base, bool isLocal) { register_exception_impl(handle scope, const char *name, handle base, bool isLocal) {
auto &ex = detail::get_exception_object<CppException>(); PYBIND11_CONSTINIT static gil_safe_call_once_and_store<exception<CppException>> exc_storage;
if (!ex) { exc_storage.call_once_and_store_result(
ex = exception<CppException>(scope, name, base); [&]() { return exception<CppException>(scope, name, base); });
}
auto register_func auto register_func
= isLocal ? &register_local_exception_translator : &register_exception_translator; = isLocal ? &register_local_exception_translator : &register_exception_translator;
@ -2628,10 +2629,10 @@ register_exception_impl(handle scope, const char *name, handle base, bool isLoca
try { try {
std::rethrow_exception(p); std::rethrow_exception(p);
} catch (const CppException &e) { } catch (const CppException &e) {
set_error(detail::get_exception_object<CppException>(), e.what()); set_error(exc_storage.get_stored(), e.what());
} }
}); });
return ex; return exc_storage.get_stored();
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -310,14 +310,14 @@ private:
"used for all translation units linked into a given pybind11 extension, otherwise " "used for all translation units linked into a given pybind11 extension, otherwise "
"there will be ODR violations.", "there will be ODR violations.",
function_name.c_str()); function_name.c_str());
fflush(stderr);
if (Py_TYPE(m_ptr)->tp_name != nullptr) { if (Py_TYPE(m_ptr)->tp_name != nullptr) {
fprintf(stderr, fprintf(stderr,
"The failing %s call was triggered on a %s object.\n", " The failing %s call was triggered on a %s object.",
function_name.c_str(), function_name.c_str(),
Py_TYPE(m_ptr)->tp_name); Py_TYPE(m_ptr)->tp_name);
fflush(stderr);
} }
fprintf(stderr, "\n");
fflush(stderr);
throw std::runtime_error(function_name + " PyGILState_Check() failure."); throw std::runtime_error(function_name + " PyGILState_Check() failure.");
} }
#endif #endif

View File

@ -19,7 +19,7 @@ ignore = [
[tool.mypy] [tool.mypy]
files = ["pybind11"] files = ["pybind11"]
python_version = "3.6" python_version = "3.7"
strict = true strict = true
show_error_codes = true show_error_codes = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
@ -57,10 +57,13 @@ messages_control.disable = [
"unused-argument", # covered by Ruff ARG "unused-argument", # covered by Ruff ARG
] ]
[tool.ruff] [tool.ruff]
select = [ target-version = "py37"
"E", "F", "W", # flake8 src = ["src"]
line-length = 120
[tool.ruff.lint]
extend-select = [
"B", # flake8-bugbear "B", # flake8-bugbear
"I", # isort "I", # isort
"N", # pep8-naming "N", # pep8-naming
@ -86,13 +89,10 @@ ignore = [
"PT004", # Fixture that doesn't return needs underscore (no, it is fine) "PT004", # Fixture that doesn't return needs underscore (no, it is fine)
"SIM118", # iter(x) is not always the same as iter(x.keys()) "SIM118", # iter(x) is not always the same as iter(x.keys())
] ]
target-version = "py37"
src = ["src"]
unfixable = ["T20"] unfixable = ["T20"]
exclude = [] exclude = []
line-length = 120
isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"] isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"]
[tool.ruff.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"tests/**" = ["EM", "N", "E721"] "tests/**" = ["EM", "N", "E721"]
"tests/test_call_policies.py" = ["PLC1901"] "tests/test_call_policies.py" = ["PLC1901"]

View File

@ -35,6 +35,7 @@ main_headers = {
"include/pybind11/eval.h", "include/pybind11/eval.h",
"include/pybind11/functional.h", "include/pybind11/functional.h",
"include/pybind11/gil.h", "include/pybind11/gil.h",
"include/pybind11/gil_safe_call_once.h",
"include/pybind11/iostream.h", "include/pybind11/iostream.h",
"include/pybind11/numpy.h", "include/pybind11/numpy.h",
"include/pybind11/operators.h", "include/pybind11/operators.h",

View File

@ -3,7 +3,8 @@ numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux"
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6" numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10" numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11" numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
pytest==7.0.0 pytest==7.0.0; platform_python_implementation!="PyPy" and python_version=="3.6"
pytest==7.2.0; platform_python_implementation!="PyPy" and python_version>="3.7"
pytest-timeout pytest-timeout
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10" scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
scipy==1.10.0; platform_python_implementation!="PyPy" and python_version=="3.10" scipy==1.10.0; platform_python_implementation!="PyPy" and python_version=="3.10"

View File

@ -6,6 +6,8 @@
All rights reserved. Use of this source code is governed by a All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <pybind11/gil_safe_call_once.h>
#include "test_exceptions.h" #include "test_exceptions.h"
#include "local_bindings.h" #include "local_bindings.h"
@ -114,7 +116,9 @@ TEST_SUBMODULE(exceptions, m) {
[]() { throw std::runtime_error("This exception was intentionally thrown."); }); []() { throw std::runtime_error("This exception was intentionally thrown."); });
// PLEASE KEEP IN SYNC with docs/advanced/exceptions.rst // PLEASE KEEP IN SYNC with docs/advanced/exceptions.rst
static py::handle ex = py::exception<MyException>(m, "MyException").release(); PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> ex_storage;
ex_storage.call_once_and_store_result(
[&]() { return py::exception<MyException>(m, "MyException"); });
py::register_exception_translator([](std::exception_ptr p) { py::register_exception_translator([](std::exception_ptr p) {
try { try {
if (p) { if (p) {
@ -122,7 +126,7 @@ TEST_SUBMODULE(exceptions, m) {
} }
} catch (const MyException &e) { } catch (const MyException &e) {
// Set MyException as the active python error // Set MyException as the active python error
py::set_error(ex, e.what()); py::set_error(ex_storage.get_stored(), e.what());
} }
}); });

View File

@ -85,6 +85,8 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
"kw_lb_func7", "kw_lb_func7",
[](const std::string &) {}, [](const std::string &) {},
py::arg("str_arg") = "First line.\n Second line."); py::arg("str_arg") = "First line.\n Second line.");
m.def(
"kw_lb_func8", [](const CustomRepr &) {}, py::arg("custom") = CustomRepr(""));
// test_args_and_kwargs // test_args_and_kwargs
m.def("args_function", [](py::args args) -> py::tuple { m.def("args_function", [](py::args args) -> py::tuple {

View File

@ -55,6 +55,10 @@ def test_function_signatures(doc):
doc(m.kw_lb_func7) doc(m.kw_lb_func7)
== "kw_lb_func7(str_arg: str = 'First line.\\n Second line.') -> None" == "kw_lb_func7(str_arg: str = 'First line.\\n Second line.') -> None"
) )
assert (
doc(m.kw_lb_func8)
== "kw_lb_func8(custom: m.kwargs_and_defaults.CustomRepr = ) -> None"
)
def test_named_arguments(): def test_named_arguments():

View File

@ -298,7 +298,7 @@ def test_constructors():
results = m.converting_constructors([1, 2, 3]) results = m.converting_constructors([1, 2, 3])
for a in results.values(): for a in results.values():
np.testing.assert_array_equal(a, [1, 2, 3]) np.testing.assert_array_equal(a, [1, 2, 3])
assert results["array"].dtype == np.int_ assert results["array"].dtype == np.dtype(int)
assert results["array_t<int32>"].dtype == np.int32 assert results["array_t<int32>"].dtype == np.int32
assert results["array_t<double>"].dtype == np.float64 assert results["array_t<double>"].dtype == np.float64

View File

@ -58,6 +58,15 @@ def test_generalized_iterators_simple():
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_values()) == [2, 4, 5] assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_values()) == [2, 4, 5]
def test_iterator_doc_annotations():
assert m.IntPairs.nonref.__doc__.endswith("-> Iterator[tuple[int, int]]\n")
assert m.IntPairs.nonref_keys.__doc__.endswith("-> Iterator[int]\n")
assert m.IntPairs.nonref_values.__doc__.endswith("-> Iterator[int]\n")
assert m.IntPairs.simple_iterator.__doc__.endswith("-> Iterator[tuple[int, int]]\n")
assert m.IntPairs.simple_keys.__doc__.endswith("-> Iterator[int]\n")
assert m.IntPairs.simple_values.__doc__.endswith("-> Iterator[int]\n")
def test_iterator_referencing(): def test_iterator_referencing():
"""Test that iterators reference rather than copy their referents.""" """Test that iterators reference rather than copy their referents."""
vec = m.VectorNonCopyableInt() vec = m.VectorNonCopyableInt()

View File

@ -95,6 +95,22 @@ if(NOT PythonLibsNew_FIND_VERSION)
set(PythonLibsNew_FIND_VERSION "3.6") set(PythonLibsNew_FIND_VERSION "3.6")
endif() endif()
if(NOT CMAKE_VERSION VERSION_LESS "3.27")
cmake_policy(GET CMP0148 _pybind11_cmp0148)
if(NOT _pybind11_cmp0148)
message(
AUTHOR_WARNING
"Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs "
"modules are removed. Run \"cmake --help-policy CMP0148\" for policy "
"details. Use the cmake_policy command to set the policy and suppress "
"this warning, or preferably upgrade to using FindPython, either by "
"calling it explicitly before pybind11, or by setting "
"PYBIND11_FINDPYTHON ON before pybind11.")
endif()
cmake_policy(SET CMP0148 OLD)
unset(_pybind11_cmp0148)
endif()
find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required} find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required}
${_pythonlibs_quiet}) ${_pythonlibs_quiet})
@ -172,13 +188,20 @@ _pybind11_get_if_undef(_PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
_pybind11_get_if_undef(_PYTHON_VALUES 1 PYTHON_PREFIX) _pybind11_get_if_undef(_PYTHON_VALUES 1 PYTHON_PREFIX)
_pybind11_get_if_undef(_PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) _pybind11_get_if_undef(_PYTHON_VALUES 2 PYTHON_INCLUDE_DIR)
_pybind11_get_if_undef(_PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) _pybind11_get_if_undef(_PYTHON_VALUES 3 PYTHON_SITE_PACKAGES)
_pybind11_get_if_undef(_PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION)
_pybind11_get_if_undef(_PYTHON_VALUES 5 PYTHON_IS_DEBUG) _pybind11_get_if_undef(_PYTHON_VALUES 5 PYTHON_IS_DEBUG)
_pybind11_get_if_undef(_PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) _pybind11_get_if_undef(_PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P)
_pybind11_get_if_undef(_PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) _pybind11_get_if_undef(_PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX)
_pybind11_get_if_undef(_PYTHON_VALUES 8 PYTHON_LIBDIR) _pybind11_get_if_undef(_PYTHON_VALUES 8 PYTHON_LIBDIR)
_pybind11_get_if_undef(_PYTHON_VALUES 9 PYTHON_MULTIARCH) _pybind11_get_if_undef(_PYTHON_VALUES 9 PYTHON_MULTIARCH)
list(GET _PYTHON_VALUES 4 _PYTHON_MODULE_EXT_SUFFIX)
if(PYBIND11_PYTHONLIBS_OVERWRITE OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX)
get_filename_component(PYTHON_MODULE_DEBUG_POSTFIX "${_PYTHON_MODULE_EXT_SUFFIX}" NAME_WE)
endif()
if(PYBIND11_PYTHONLIBS_OVERWRITE OR NOT DEFINED PYTHON_MODULE_EXTENSION)
get_filename_component(PYTHON_MODULE_EXTENSION "${_PYTHON_MODULE_EXT_SUFFIX}" EXT)
endif()
# Make sure the Python has the same pointer-size as the chosen compiler # Make sure the Python has the same pointer-size as the chosen compiler
# Skip if CMAKE_SIZEOF_VOID_P is not defined # Skip if CMAKE_SIZEOF_VOID_P is not defined
# This should be skipped for (non-Apple) cross-compiles (like EMSCRIPTEN) # This should be skipped for (non-Apple) cross-compiles (like EMSCRIPTEN)

View File

@ -95,26 +95,37 @@ endif()
# Get the suffix - SO is deprecated, should use EXT_SUFFIX, but this is # Get the suffix - SO is deprecated, should use EXT_SUFFIX, but this is
# required for PyPy3 (as of 7.3.1) # required for PyPy3 (as of 7.3.1)
if(NOT DEFINED PYTHON_MODULE_EXTENSION) if(NOT DEFINED PYTHON_MODULE_EXTENSION OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX)
execute_process( execute_process(
COMMAND COMMAND
"${${_Python}_EXECUTABLE}" "-c" "${${_Python}_EXECUTABLE}" "-c"
"import sys, importlib; s = importlib.import_module('distutils.sysconfig' if sys.version_info < (3, 10) else 'sysconfig'); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))" "import sys, importlib; s = importlib.import_module('distutils.sysconfig' if sys.version_info < (3, 10) else 'sysconfig'); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))"
OUTPUT_VARIABLE _PYTHON_MODULE_EXTENSION OUTPUT_VARIABLE _PYTHON_MODULE_EXT_SUFFIX
ERROR_VARIABLE _PYTHON_MODULE_EXTENSION_ERR ERROR_VARIABLE _PYTHON_MODULE_EXT_SUFFIX_ERR
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE)
if(_PYTHON_MODULE_EXTENSION STREQUAL "") if(_PYTHON_MODULE_EXT_SUFFIX STREQUAL "")
message( message(
FATAL_ERROR "pybind11 could not query the module file extension, likely the 'distutils'" FATAL_ERROR "pybind11 could not query the module file extension, likely the 'distutils'"
"package is not installed. Full error message:\n${_PYTHON_MODULE_EXTENSION_ERR}") "package is not installed. Full error message:\n${_PYTHON_MODULE_EXT_SUFFIX_ERR}"
)
endif() endif()
# This needs to be available for the pybind11_extension function # This needs to be available for the pybind11_extension function
if(NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX)
get_filename_component(_PYTHON_MODULE_DEBUG_POSTFIX "${_PYTHON_MODULE_EXT_SUFFIX}" NAME_WE)
set(PYTHON_MODULE_DEBUG_POSTFIX
"${_PYTHON_MODULE_DEBUG_POSTFIX}"
CACHE INTERNAL "")
endif()
if(NOT DEFINED PYTHON_MODULE_EXTENSION)
get_filename_component(_PYTHON_MODULE_EXTENSION "${_PYTHON_MODULE_EXT_SUFFIX}" EXT)
set(PYTHON_MODULE_EXTENSION set(PYTHON_MODULE_EXTENSION
"${_PYTHON_MODULE_EXTENSION}" "${_PYTHON_MODULE_EXTENSION}"
CACHE INTERNAL "") CACHE INTERNAL "")
endif() endif()
endif()
# Python debug libraries expose slightly different objects before 3.8 # Python debug libraries expose slightly different objects before 3.8
# https://docs.python.org/3.6/c-api/intro.html#debugging-builds # https://docs.python.org/3.6/c-api/intro.html#debugging-builds
@ -253,6 +264,9 @@ endfunction()
function(pybind11_extension name) function(pybind11_extension name)
# The extension is precomputed # The extension is precomputed
set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${PYTHON_MODULE_EXTENSION}") set_target_properties(
${name}
PROPERTIES PREFIX ""
DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}"
SUFFIX "${PYTHON_MODULE_EXTENSION}")
endfunction() endfunction()

View File

@ -65,6 +65,7 @@ _pybind11_promote_to_cache(PYTHON_INCLUDE_DIRS)
_pybind11_promote_to_cache(PYTHON_LIBRARIES) _pybind11_promote_to_cache(PYTHON_LIBRARIES)
_pybind11_promote_to_cache(PYTHON_MODULE_PREFIX) _pybind11_promote_to_cache(PYTHON_MODULE_PREFIX)
_pybind11_promote_to_cache(PYTHON_MODULE_EXTENSION) _pybind11_promote_to_cache(PYTHON_MODULE_EXTENSION)
_pybind11_promote_to_cache(PYTHON_MODULE_DEBUG_POSTFIX)
_pybind11_promote_to_cache(PYTHON_VERSION_MAJOR) _pybind11_promote_to_cache(PYTHON_VERSION_MAJOR)
_pybind11_promote_to_cache(PYTHON_VERSION_MINOR) _pybind11_promote_to_cache(PYTHON_VERSION_MINOR)
_pybind11_promote_to_cache(PYTHON_VERSION) _pybind11_promote_to_cache(PYTHON_VERSION)
@ -148,7 +149,10 @@ endif()
function(pybind11_extension name) function(pybind11_extension name)
# The prefix and extension are provided by FindPythonLibsNew.cmake # The prefix and extension are provided by FindPythonLibsNew.cmake
set_target_properties(${name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" set_target_properties(
${name}
PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}"
SUFFIX "${PYTHON_MODULE_EXTENSION}") SUFFIX "${PYTHON_MODULE_EXTENSION}")
endfunction() endfunction()