Merge branch 'master' into sh_merge_master3

This commit is contained in:
Ralf W. Grosse-Kunstleve 2023-02-07 23:54:04 -08:00
commit 8c43d35210
18 changed files with 77 additions and 49 deletions

View File

@ -84,6 +84,9 @@ jobs:
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.13 uses: jwlawson/actions-setup-cmake@v1.13
# TEMPORARILY pin version because 3.26.0-rc1 is failing under macOS:
with:
cmake-version: '3.25.2'
- name: Cache wheels - name: Cache wheels
if: runner.os == 'macOS' if: runner.os == 'macOS'
@ -281,6 +284,8 @@ jobs:
- dev - dev
std: std:
- 11 - 11
container_suffix:
- ""
include: include:
- clang: 5 - clang: 5
std: 14 std: 14
@ -294,9 +299,12 @@ jobs:
std: 20 std: 20
- clang: 14 - clang: 14
std: 20 std: 20
- clang: 15
std: 20
container_suffix: "-bullseye"
name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64"
container: "silkeh/clang:${{ matrix.clang }}" container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -762,7 +770,7 @@ jobs:
uses: jwlawson/actions-setup-cmake@v1.13 uses: jwlawson/actions-setup-cmake@v1.13
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.12.0 uses: ilammy/msvc-dev-cmd@v1.12.1
with: with:
arch: x86 arch: x86
@ -815,7 +823,7 @@ jobs:
uses: jwlawson/actions-setup-cmake@v1.13 uses: jwlawson/actions-setup-cmake@v1.13
- name: Prepare MSVC - name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.12.0 uses: ilammy/msvc-dev-cmd@v1.12.1
with: with:
arch: x86 arch: x86
@ -1067,6 +1075,9 @@ jobs:
- name: Update CMake - name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.13 uses: jwlawson/actions-setup-cmake@v1.13
# TEMPORARILY pin version because 3.26.0-rc1 is failing under macOS:
with:
cmake-version: '3.25.2'
- name: Run pip installs - name: Run pip installs
run: | run: |

View File

@ -49,47 +49,47 @@ repos:
# Nicely sort includes # Nicely sort includes
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: "5.11.4" rev: "5.12.0"
hooks: hooks:
- id: isort - id: isort
# Black, the code formatter, natively supports pre-commit # Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: "22.12.0" # Keep in sync with blacken-docs rev: "23.1.0" # Keep in sync with blacken-docs
hooks: hooks:
- id: black - id: black
# Also code format the docs # Also code format the docs
- repo: https://github.com/asottile/blacken-docs - repo: https://github.com/asottile/blacken-docs
rev: "v1.12.1" rev: "1.13.0"
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: additional_dependencies:
- black==22.10.0 # keep in sync with black hook - black==23.1.0 # keep in sync with black hook
# Changes tabs to spaces # Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: "v1.3.1" rev: "v1.4.2"
hooks: hooks:
- id: remove-tabs - id: remove-tabs
exclude: (^docs/.*|\.patch)?$ exclude: (^docs/.*|\.patch)?$
- repo: https://github.com/sirosen/texthooks - repo: https://github.com/sirosen/texthooks
rev: "0.4.0" rev: "0.5.0"
hooks: hooks:
- id: fix-ligatures - id: fix-ligatures
- id: fix-smartquotes - id: fix-smartquotes
# Autoremoves unused imports # Autoremoves unused imports
- repo: https://github.com/hadialqattan/pycln - repo: https://github.com/hadialqattan/pycln
rev: "v2.1.2" rev: "v2.1.3"
hooks: hooks:
- id: pycln - id: pycln
stages: [manual] stages: [manual]
# Checking for common mistakes # Checking for common mistakes
- repo: https://github.com/pre-commit/pygrep-hooks - repo: https://github.com/pre-commit/pygrep-hooks
rev: "v1.9.0" rev: "v1.10.0"
hooks: hooks:
- id: python-check-blanket-noqa - id: python-check-blanket-noqa
- id: python-check-blanket-type-ignore - id: python-check-blanket-type-ignore
@ -119,7 +119,7 @@ repos:
# PyLint has native support - not always usable, but works for us # PyLint has native support - not always usable, but works for us
- repo: https://github.com/PyCQA/pylint - repo: https://github.com/PyCQA/pylint
rev: "v2.15.9" rev: "v2.16.1"
hooks: hooks:
- id: pylint - id: pylint
files: ^pybind11 files: ^pybind11
@ -178,7 +178,7 @@ repos:
# Clang format the codebase automatically # Clang format the codebase automatically
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v15.0.6" rev: "v15.0.7"
hooks: hooks:
- id: clang-format - id: clang-format
types_or: [c++, c, cuda] types_or: [c++, c, cuda]

View File

@ -358,7 +358,6 @@ def clean_up(app, exception):
def setup(app): def setup(app):
# Add hook for building doxygen xml when needed # Add hook for building doxygen xml when needed
app.connect("builder-inited", generate_doxygen_xml) app.connect("builder-inited", generate_doxygen_xml)

View File

@ -447,9 +447,17 @@ inline void clear_instance(PyObject *self) {
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` /// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
/// to destroy the C++ object itself, while the rest is Python bookkeeping. /// to destroy the C++ object itself, while the rest is Python bookkeeping.
extern "C" inline void pybind11_object_dealloc(PyObject *self) { extern "C" inline void pybind11_object_dealloc(PyObject *self) {
auto *type = Py_TYPE(self);
// If this is a GC tracked object, untrack it first
// Note that the track call is implicitly done by the
// default tp_alloc, which we never override.
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) {
PyObject_GC_UnTrack(self);
}
clear_instance(self); clear_instance(self);
auto *type = Py_TYPE(self);
type->tp_free(self); type->tp_free(self);
#if PY_VERSION_HEX < 0x03080000 #if PY_VERSION_HEX < 0x03080000

View File

@ -198,9 +198,10 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
init_signal_handlers, argc, argv, add_program_dir_to_path); init_signal_handlers, argc, argv, add_program_dir_to_path);
#else #else
PyConfig config; PyConfig config;
PyConfig_InitIsolatedConfig(&config); PyConfig_InitPythonConfig(&config);
config.isolated = 0; // See PR #4473 for background
config.use_environment = 1; config.parse_argv = 0;
config.install_signal_handlers = init_signal_handlers ? 1 : 0; config.install_signal_handlers = init_signal_handlers ? 1 : 0;
initialize_interpreter(&config, argc, argv, add_program_dir_to_path); initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
#endif #endif

View File

@ -152,8 +152,8 @@ public:
} }
} }
gil_scoped_release(const gil_scoped_acquire &) = delete; gil_scoped_release(const gil_scoped_release &) = delete;
gil_scoped_release &operator=(const gil_scoped_acquire &) = delete; gil_scoped_release &operator=(const gil_scoped_release &) = delete;
/// This method will disable the PyThreadState_DeleteCurrent call and the /// This method will disable the PyThreadState_DeleteCurrent call and the
/// GIL won't be acquired. This method should be used if the interpreter /// GIL won't be acquired. This method should be used if the interpreter
@ -203,7 +203,7 @@ class gil_scoped_release {
public: public:
gil_scoped_release() : state{PyEval_SaveThread()} {} gil_scoped_release() : 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_acquire &) = delete; gil_scoped_release &operator=(const gil_scoped_release &) = delete;
~gil_scoped_release() { PyEval_RestoreThread(state); } ~gil_scoped_release() { PyEval_RestoreThread(state); }
void disarm() {} void disarm() {}
}; };
@ -230,7 +230,7 @@ public:
(void) (this != (this + 1)); (void) (this != (this + 1));
} }
gil_scoped_release(const gil_scoped_release &) = delete; gil_scoped_release(const gil_scoped_release &) = delete;
gil_scoped_release &operator=(const gil_scoped_acquire &) = delete; gil_scoped_release &operator=(const gil_scoped_release &) = delete;
void disarm() {} void disarm() {}
}; };

View File

@ -24,7 +24,6 @@ def print_includes() -> None:
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
"--includes", "--includes",

View File

@ -118,7 +118,6 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
self.extra_link_args[:0] = flags self.extra_link_args[:0] = flags
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
self._cxx_level = 0 self._cxx_level = 0
cxx_std = kwargs.pop("cxx_std", 0) cxx_std = kwargs.pop("cxx_std", 0)
@ -174,7 +173,6 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
@cxx_std.setter @cxx_std.setter
def cxx_std(self, level: int) -> None: def cxx_std(self, level: int) -> None:
if self._cxx_level: if self._cxx_level:
warnings.warn("You cannot safely change the cxx_level after setting it!") warnings.warn("You cannot safely change the cxx_level after setting it!")
@ -439,7 +437,6 @@ class ParallelCompile:
extra_postargs: Optional[List[str]] = None, extra_postargs: Optional[List[str]] = None,
depends: Optional[List[str]] = None, depends: Optional[List[str]] = None,
) -> Any: ) -> Any:
# These lines are directly from distutils.ccompiler.CCompiler # These lines are directly from distutils.ccompiler.CCompiler
macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined] macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined]
output_dir, macros, include_dirs, sources, depends, extra_postargs output_dir, macros, include_dirs, sources, depends, extra_postargs

View File

@ -11,11 +11,17 @@ import multiprocessing
import os import os
import re import re
import textwrap import textwrap
import traceback
import pytest import pytest
# Early diagnostic for failed imports # Early diagnostic for failed imports
try:
import pybind11_tests import pybind11_tests
except Exception:
# pytest does not show the traceback without this.
traceback.print_exc()
raise
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)

View File

@ -143,7 +143,6 @@ def normalize_line_endings(value: bytes) -> bytes:
def test_build_sdist(monkeypatch, tmpdir): def test_build_sdist(monkeypatch, tmpdir):
monkeypatch.chdir(MAIN_DIR) monkeypatch.chdir(MAIN_DIR)
subprocess.run( subprocess.run(
@ -194,7 +193,6 @@ def test_build_sdist(monkeypatch, tmpdir):
def test_build_global_dist(monkeypatch, tmpdir): def test_build_global_dist(monkeypatch, tmpdir):
monkeypatch.chdir(MAIN_DIR) monkeypatch.chdir(MAIN_DIR)
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1") monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
subprocess.run( subprocess.run(

View File

@ -7,7 +7,6 @@ from pybind11_tests import chrono as m
def test_chrono_system_clock(): def test_chrono_system_clock():
# Get the time from both c++ and datetime # Get the time from both c++ and datetime
date0 = datetime.datetime.today() date0 = datetime.datetime.today()
date1 = m.test_chrono1() date1 = m.test_chrono1()
@ -122,7 +121,6 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
def test_chrono_duration_roundtrip(): def test_chrono_duration_roundtrip():
# Get the difference between two times (a timedelta) # Get the difference between two times (a timedelta)
date1 = datetime.datetime.today() date1 = datetime.datetime.today()
date2 = datetime.datetime.today() date2 = datetime.datetime.today()
@ -143,7 +141,6 @@ def test_chrono_duration_roundtrip():
def test_chrono_duration_subtraction_equivalence(): def test_chrono_duration_subtraction_equivalence():
date1 = datetime.datetime.today() date1 = datetime.datetime.today()
date2 = datetime.datetime.today() date2 = datetime.datetime.today()
@ -154,7 +151,6 @@ def test_chrono_duration_subtraction_equivalence():
def test_chrono_duration_subtraction_equivalence_date(): def test_chrono_duration_subtraction_equivalence_date():
date1 = datetime.date.today() date1 = datetime.date.today()
date2 = datetime.date.today() date2 = datetime.date.today()

View File

@ -185,7 +185,6 @@ def test_inheritance(msg):
def test_inheritance_init(msg): def test_inheritance_init(msg):
# Single base # Single base
class Python(m.Pet): class Python(m.Pet):
def __init__(self): def __init__(self):

View File

@ -94,7 +94,8 @@ def test_noconvert_args(msg):
def test_custom_caster_destruction(): def test_custom_caster_destruction():
"""Tests that returning a pointer to a type that gets converted with a custom type caster gets """Tests that returning a pointer to a type that gets converted with a custom type caster gets
destroyed when the function has py::return_value_policy::take_ownership policy applied.""" destroyed when the function has py::return_value_policy::take_ownership policy applied.
"""
cstats = m.destruction_tester_cstats() cstats = m.destruction_tester_cstats()
# This one *doesn't* have take_ownership: the pointer should be used but not destroyed: # This one *doesn't* have take_ownership: the pointer should be used but not destroyed:

View File

@ -59,7 +59,6 @@ def assert_equal_tensor_ref(mat, writeable=True, modified=None):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
@pytest.mark.parametrize("member_name", ["member", "member_view"]) @pytest.mark.parametrize("member_name", ["member", "member_view"])
def test_reference_internal(m, member_name): def test_reference_internal(m, member_name):
if not hasattr(sys, "getrefcount"): if not hasattr(sys, "getrefcount"):
pytest.skip("No reference counting") pytest.skip("No reference counting")
foo = m.CustomExample() foo = m.CustomExample()
@ -108,7 +107,6 @@ def test_convert_tensor_to_py(m, func_name):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
def test_bad_cpp_to_python_casts(m): def test_bad_cpp_to_python_casts(m):
with pytest.raises( with pytest.raises(
RuntimeError, match="Cannot use reference internal when there is no parent" RuntimeError, match="Cannot use reference internal when there is no parent"
): ):
@ -131,7 +129,6 @@ def test_bad_cpp_to_python_casts(m):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
def test_bad_python_to_cpp_casts(m): def test_bad_python_to_cpp_casts(m):
with pytest.raises( with pytest.raises(
TypeError, match=r"^round_trip_tensor\(\): incompatible function arguments" TypeError, match=r"^round_trip_tensor\(\): incompatible function arguments"
): ):
@ -194,7 +191,6 @@ def test_bad_python_to_cpp_casts(m):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
def test_references_actually_refer(m): def test_references_actually_refer(m):
a = m.reference_tensor() a = m.reference_tensor()
temp = a[indices] temp = a[indices]
a[indices] = 100 a[indices] = 100
@ -211,7 +207,6 @@ def test_references_actually_refer(m):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
def test_round_trip(m): def test_round_trip(m):
assert_equal_tensor_ref(m.round_trip_tensor(tensor_ref)) assert_equal_tensor_ref(m.round_trip_tensor(tensor_ref))
with pytest.raises(TypeError, match="^Cannot cast array data from"): with pytest.raises(TypeError, match="^Cannot cast array data from"):
@ -260,7 +255,6 @@ def test_round_trip(m):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
def test_round_trip_references_actually_refer(m): def test_round_trip_references_actually_refer(m):
# Need to create a copy that matches the type on the C side # Need to create a copy that matches the type on the C side
copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options) copy = np.array(tensor_ref, dtype=np.float64, order=m.needed_options)
a = m.round_trip_view_tensor(copy) a = m.round_trip_view_tensor(copy)

View File

@ -184,7 +184,7 @@ TEST_CASE("Custom PyConfig") {
py::initialize_interpreter(); py::initialize_interpreter();
} }
TEST_CASE("Custom PyConfig with argv") { TEST_CASE("scoped_interpreter with PyConfig_InitIsolatedConfig and argv") {
py::finalize_interpreter(); py::finalize_interpreter();
{ {
PyConfig config; PyConfig config;
@ -199,6 +199,26 @@ TEST_CASE("Custom PyConfig with argv") {
} }
py::initialize_interpreter(); py::initialize_interpreter();
} }
TEST_CASE("scoped_interpreter with PyConfig_InitPythonConfig and argv") {
py::finalize_interpreter();
{
PyConfig config;
PyConfig_InitPythonConfig(&config);
// `initialize_interpreter() overrides the default value for config.parse_argv (`1`) by
// changing it to `0`. This test exercises `scoped_interpreter` with the default config.
char *argv[] = {strdup("a.out"), strdup("arg1")};
py::scoped_interpreter argv_scope(&config, 2, argv);
std::free(argv[0]);
std::free(argv[1]);
auto module = py::module::import("test_interpreter");
auto py_widget = module.attr("DerivedWidget")("The question");
const auto &cpp_widget = py_widget.cast<const Widget &>();
REQUIRE(cpp_widget.argv0() == "arg1");
}
py::initialize_interpreter();
}
#endif #endif
TEST_CASE("Add program dir to path pre-PyConfig") { TEST_CASE("Add program dir to path pre-PyConfig") {

View File

@ -130,7 +130,8 @@ def test_stl_bind_global():
def test_mixed_local_global(): def test_mixed_local_global():
"""Local types take precedence over globally registered types: a module with a `module_local` """Local types take precedence over globally registered types: a module with a `module_local`
type can be registered even if the type is already registered globally. With the module, type can be registered even if the type is already registered globally. With the module,
casting will go to the local type; outside the module casting goes to the global type.""" casting will go to the local type; outside the module casting goes to the global type.
"""
import pybind11_cross_module_tests as cm import pybind11_cross_module_tests as cm
m.register_mixed_global() m.register_mixed_global()

View File

@ -107,11 +107,10 @@ def test_def_submodule_failures():
sm_name_orig = sm.__name__ sm_name_orig = sm.__name__
sm.__name__ = malformed_utf8 sm.__name__ = malformed_utf8
try: try:
with pytest.raises(Exception): # We want to assert that a bad __name__ causes some kind of failure, although we do not want to exercise
# Seen with Python 3.9: SystemError: nameless module # the internals of PyModule_GetName(). Currently all supported Python versions raise SystemError. If that
# But we do not want to exercise the internals of PyModule_GetName(), which could # changes in future Python versions, simply add the new expected exception types here.
# change in future versions of Python, but a bad __name__ is very likely to cause with pytest.raises(SystemError):
# some kind of failure indefinitely.
m.def_submodule(sm, b"SubSubModuleName") m.def_submodule(sm, b"SubSubModuleName")
finally: finally:
# Clean up to ensure nothing gets upset by a module with an invalid __name__. # Clean up to ensure nothing gets upset by a module with an invalid __name__.

View File

@ -130,7 +130,6 @@ def test_nested():
def test_overriding_eq_reset_hash(): def test_overriding_eq_reset_hash():
assert m.Comparable(15) is not m.Comparable(15) assert m.Comparable(15) is not m.Comparable(15)
assert m.Comparable(15) == m.Comparable(15) assert m.Comparable(15) == m.Comparable(15)