Skip to content

Commit e5a5a83

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents 6df8693 + 1874f8f commit e5a5a83

File tree

11 files changed

+161
-66
lines changed

11 files changed

+161
-66
lines changed

.codespell-ignore-lines

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
2+
template <typename ThisT>
3+
auto &this_ = static_cast<ThisT &>(*this);
4+
if (load_impl<ThisT>(temp, false)) {
5+
ssize_t nd = 0;
6+
auto trivial = broadcast(buffers, nd, shape);
7+
auto ndim = (size_t) nd;
8+
int nd;
9+
ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
10+
using op = op_impl<id, ot, Base, L_type, R_type>;
11+
template <op_id id, op_type ot, typename L, typename R>
12+
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
13+
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
14+
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
15+
int valu;
16+
explicit movable_int(int v) : valu{v} {}
17+
movable_int(movable_int &&other) noexcept : valu(other.valu) { other.valu = 91; }
18+
explicit indestructible_int(int v) : valu{v} {}
19+
REQUIRE(hld.as_raw_ptr_unowned<zombie>()->valu == 19);
20+
REQUIRE(othr.valu == 19);
21+
REQUIRE(orig.valu == 91);
22+
(m.pass_valu, "Valu", "pass_valu:Valu(_MvCtor)*_CpCtor"),
23+
atyp_valu rtrn_valu() { atyp_valu obj{"Valu"}; return obj; }
24+
assert m.atyp_valu().get_mtxt() == "Valu"
25+
// valu(e), ref(erence), ptr or p (pointer), r = rvalue, m = mutable, c = const,
26+
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
27+
struct IntStruct {
28+
explicit IntStruct(int v) : value(v){};
29+
~IntStruct() { value = -value; }
30+
IntStruct(const IntStruct &) = default;
31+
IntStruct &operator=(const IntStruct &) = default;
32+
py::class_<IntStruct>(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); }));
33+
py::implicitly_convertible<int, IntStruct>();
34+
m.def("test", [](int expected, const IntStruct &in) {
35+
[](int expected, const IntStruct &in) {

.codespell-ignorelines

Lines changed: 0 additions & 13 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,7 @@ jobs:
755755
uses: jwlawson/actions-setup-cmake@v1.12
756756

757757
- name: Prepare MSVC
758-
uses: ilammy/msvc-dev-cmd@v1.10.0
758+
uses: ilammy/msvc-dev-cmd@v1.11.0
759759
with:
760760
arch: x86
761761

@@ -808,7 +808,7 @@ jobs:
808808
uses: jwlawson/actions-setup-cmake@v1.12
809809

810810
- name: Prepare MSVC
811-
uses: ilammy/msvc-dev-cmd@v1.10.0
811+
uses: ilammy/msvc-dev-cmd@v1.11.0
812812
with:
813813
arch: x86
814814

.pre-commit-config.yaml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ repos:
4949

5050
# Black, the code formatter, natively supports pre-commit
5151
- repo: https://github.com/psf/black
52-
rev: "22.6.0" # Keep in sync with blacken-docs
52+
rev: "22.8.0" # Keep in sync with blacken-docs
5353
hooks:
5454
- id: black
5555

@@ -59,17 +59,17 @@ repos:
5959
hooks:
6060
- id: blacken-docs
6161
additional_dependencies:
62-
- black==22.6.0 # keep in sync with black hook
62+
- black==22.8.0 # keep in sync with black hook
6363

6464
# Changes tabs to spaces
6565
- repo: https://github.com/Lucas-C/pre-commit-hooks
66-
rev: "v1.3.0"
66+
rev: "v1.3.1"
6767
hooks:
6868
- id: remove-tabs
6969
exclude: (^docs/.*|\.patch)?$
7070

7171
- repo: https://github.com/sirosen/texthooks
72-
rev: "0.3.1"
72+
rev: "0.4.0"
7373
hooks:
7474
- id: fix-ligatures
7575
- id: fix-smartquotes
@@ -113,7 +113,7 @@ repos:
113113

114114
# PyLint has native support - not always usable, but works for us
115115
- repo: https://github.com/PyCQA/pylint
116-
rev: "v2.14.5"
116+
rev: "v2.15.2"
117117
hooks:
118118
- id: pylint
119119
files: ^pybind11
@@ -146,12 +146,14 @@ repos:
146146
additional_dependencies: [cmake, ninja]
147147

148148
# Check for spelling
149+
# Use tools/codespell_ignore_lines_from_errors.py
150+
# to rebuild .codespell-ignore-lines
149151
- repo: https://github.com/codespell-project/codespell
150-
rev: "v2.1.0"
152+
rev: "v2.2.1"
151153
hooks:
152154
- id: codespell
153155
exclude: ".supp$"
154-
args: ["-L", "nd,ot,thist", "--exclude-file", ".codespell-ignorelines"]
156+
args: ["-x", ".codespell-ignore-lines"]
155157

156158
# Check for common shell mistakes
157159
- repo: https://github.com/shellcheck-py/shellcheck-py

docs/advanced/misc.rst

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,42 @@ The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds.
3939
Global Interpreter Lock (GIL)
4040
=============================
4141

42-
When calling a C++ function from Python, the GIL is always held.
42+
The Python C API dictates that the Global Interpreter Lock (GIL) must always
43+
be held by the current thread to safely access Python objects. As a result,
44+
when Python calls into C++ via pybind11 the GIL must be held, and pybind11
45+
will never implicitly release the GIL.
46+
47+
.. code-block:: cpp
48+
49+
void my_function() {
50+
/* GIL is held when this function is called from Python */
51+
}
52+
53+
PYBIND11_MODULE(example, m) {
54+
m.def("my_function", &my_function);
55+
}
56+
57+
pybind11 will ensure that the GIL is held when it knows that it is calling
58+
Python code. For example, if a Python callback is passed to C++ code via
59+
``std::function``, when C++ code calls the function the built-in wrapper
60+
will acquire the GIL before calling the Python callback. Similarly, the
61+
``PYBIND11_OVERRIDE`` family of macros will acquire the GIL before calling
62+
back into Python.
63+
64+
When writing C++ code that is called from other C++ code, if that code accesses
65+
Python state, it must explicitly acquire and release the GIL.
66+
4367
The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be
4468
used to acquire and release the global interpreter lock in the body of a C++
4569
function call. In this way, long-running C++ code can be parallelized using
46-
multiple Python threads. Taking :ref:`overriding_virtuals` as an example, this
70+
multiple Python threads, **but great care must be taken** when any
71+
:class:`gil_scoped_release` appear: if there is any way that the C++ code
72+
can access Python objects, :class:`gil_scoped_acquire` should be used to
73+
reacquire the GIL. Taking :ref:`overriding_virtuals` as an example, this
4774
could be realized as follows (important changes highlighted):
4875

4976
.. code-block:: cpp
50-
:emphasize-lines: 8,9,31,32
77+
:emphasize-lines: 8,30,31
5178
5279
class PyAnimal : public Animal {
5380
public:
@@ -56,9 +83,7 @@ could be realized as follows (important changes highlighted):
5683
5784
/* Trampoline (need one for each virtual function) */
5885
std::string go(int n_times) {
59-
/* Acquire GIL before calling Python code */
60-
py::gil_scoped_acquire acquire;
61-
86+
/* PYBIND11_OVERRIDE_PURE will acquire the GIL before accessing Python state */
6287
PYBIND11_OVERRIDE_PURE(
6388
std::string, /* Return type */
6489
Animal, /* Parent class */
@@ -78,7 +103,8 @@ could be realized as follows (important changes highlighted):
78103
.def(py::init<>());
79104
80105
m.def("call_go", [](Animal *animal) -> std::string {
81-
/* Release GIL before calling into (potentially long-running) C++ code */
106+
// GIL is held when called from Python code. Release GIL before
107+
// calling into (potentially long-running) C++ code
82108
py::gil_scoped_release release;
83109
return call_go(animal);
84110
});

include/pybind11/detail/class.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec
5555
return PyProperty_Type.tp_descr_set(self, cls, value);
5656
}
5757

58+
// Forward declaration to use in `make_static_property_type()`
59+
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type);
60+
5861
/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()`
5962
methods are modified to always use the object type instead of a concrete instance.
6063
Return value: New reference. */
@@ -87,6 +90,13 @@ inline PyTypeObject *make_static_property_type() {
8790
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
8891
}
8992

93+
# if PY_VERSION_HEX >= 0x030C0000
94+
// PRE 3.12 FEATURE FREEZE. PLEASE REVIEW AFTER FREEZE.
95+
// Since Python-3.12 property-derived types are required to
96+
// have dynamic attributes (to set `__doc__`)
97+
enable_dynamic_attributes(heap_type);
98+
# endif
99+
90100
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
91101
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
92102

include/pybind11/numpy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1401,7 +1401,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
14011401
oss << '}';
14021402
auto format_str = oss.str();
14031403

1404-
// Sanity check: verify that NumPy properly parses our buffer format string
1404+
// Smoke test: verify that NumPy properly parses our buffer format string
14051405
auto &api = npy_api::get();
14061406
auto arr = array(buffer_info(nullptr, itemsize, format_str, 1));
14071407
if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) {

include/pybind11/pytypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ struct error_fetch_and_normalize {
473473
+ " failed to obtain the name "
474474
"of the normalized active exception type.");
475475
}
476-
#if defined(PYPY_VERSION)
476+
#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00
477477
// This behavior runs the risk of masking errors in the error handling, but avoids a
478478
// conflict with PyPy, which relies on the normalization here to change OSError to
479479
// FileNotFoundError (https://github.com/pybind/pybind11/issues/4075).

tests/constructor_stats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class ConstructorStats {
115115
#if defined(PYPY_VERSION)
116116
PyObject *globals = PyEval_GetGlobals();
117117
PyObject *result = PyRun_String("import gc\n"
118-
"for i in range(2):"
118+
"for i in range(2):\n"
119119
" gc.collect()\n",
120120
Py_file_input,
121121
globals,

tests/test_eigen.py

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -251,14 +251,14 @@ def array_copy_but_one(a, r, c, v):
251251
def test_eigen_return_references():
252252
"""Tests various ways of returning references and non-referencing copies"""
253253

254-
master = np.ones((10, 10))
254+
primary = np.ones((10, 10))
255255
a = m.ReturnTester()
256256
a_get1 = a.get()
257257
assert not a_get1.flags.owndata and a_get1.flags.writeable
258-
assign_both(a_get1, master, 3, 3, 5)
258+
assign_both(a_get1, primary, 3, 3, 5)
259259
a_get2 = a.get_ptr()
260260
assert not a_get2.flags.owndata and a_get2.flags.writeable
261-
assign_both(a_get1, master, 2, 3, 6)
261+
assign_both(a_get1, primary, 2, 3, 6)
262262

263263
a_view1 = a.view()
264264
assert not a_view1.flags.owndata and not a_view1.flags.writeable
@@ -271,91 +271,91 @@ def test_eigen_return_references():
271271

272272
a_copy1 = a.copy_get()
273273
assert a_copy1.flags.owndata and a_copy1.flags.writeable
274-
np.testing.assert_array_equal(a_copy1, master)
274+
np.testing.assert_array_equal(a_copy1, primary)
275275
a_copy1[7, 7] = -44 # Shouldn't affect anything else
276-
c1want = array_copy_but_one(master, 7, 7, -44)
276+
c1want = array_copy_but_one(primary, 7, 7, -44)
277277
a_copy2 = a.copy_view()
278278
assert a_copy2.flags.owndata and a_copy2.flags.writeable
279-
np.testing.assert_array_equal(a_copy2, master)
279+
np.testing.assert_array_equal(a_copy2, primary)
280280
a_copy2[4, 4] = -22 # Shouldn't affect anything else
281-
c2want = array_copy_but_one(master, 4, 4, -22)
281+
c2want = array_copy_but_one(primary, 4, 4, -22)
282282

283283
a_ref1 = a.ref()
284284
assert not a_ref1.flags.owndata and a_ref1.flags.writeable
285-
assign_both(a_ref1, master, 1, 1, 15)
285+
assign_both(a_ref1, primary, 1, 1, 15)
286286
a_ref2 = a.ref_const()
287287
assert not a_ref2.flags.owndata and not a_ref2.flags.writeable
288288
with pytest.raises(ValueError):
289289
a_ref2[5, 5] = 33
290290
a_ref3 = a.ref_safe()
291291
assert not a_ref3.flags.owndata and a_ref3.flags.writeable
292-
assign_both(a_ref3, master, 0, 7, 99)
292+
assign_both(a_ref3, primary, 0, 7, 99)
293293
a_ref4 = a.ref_const_safe()
294294
assert not a_ref4.flags.owndata and not a_ref4.flags.writeable
295295
with pytest.raises(ValueError):
296296
a_ref4[7, 0] = 987654321
297297

298298
a_copy3 = a.copy_ref()
299299
assert a_copy3.flags.owndata and a_copy3.flags.writeable
300-
np.testing.assert_array_equal(a_copy3, master)
300+
np.testing.assert_array_equal(a_copy3, primary)
301301
a_copy3[8, 1] = 11
302-
c3want = array_copy_but_one(master, 8, 1, 11)
302+
c3want = array_copy_but_one(primary, 8, 1, 11)
303303
a_copy4 = a.copy_ref_const()
304304
assert a_copy4.flags.owndata and a_copy4.flags.writeable
305-
np.testing.assert_array_equal(a_copy4, master)
305+
np.testing.assert_array_equal(a_copy4, primary)
306306
a_copy4[8, 4] = 88
307-
c4want = array_copy_but_one(master, 8, 4, 88)
307+
c4want = array_copy_but_one(primary, 8, 4, 88)
308308

309309
a_block1 = a.block(3, 3, 2, 2)
310310
assert not a_block1.flags.owndata and a_block1.flags.writeable
311311
a_block1[0, 0] = 55
312-
master[3, 3] = 55
312+
primary[3, 3] = 55
313313
a_block2 = a.block_safe(2, 2, 3, 2)
314314
assert not a_block2.flags.owndata and a_block2.flags.writeable
315315
a_block2[2, 1] = -123
316-
master[4, 3] = -123
316+
primary[4, 3] = -123
317317
a_block3 = a.block_const(6, 7, 4, 3)
318318
assert not a_block3.flags.owndata and not a_block3.flags.writeable
319319
with pytest.raises(ValueError):
320320
a_block3[2, 2] = -44444
321321

322322
a_copy5 = a.copy_block(2, 2, 2, 3)
323323
assert a_copy5.flags.owndata and a_copy5.flags.writeable
324-
np.testing.assert_array_equal(a_copy5, master[2:4, 2:5])
324+
np.testing.assert_array_equal(a_copy5, primary[2:4, 2:5])
325325
a_copy5[1, 1] = 777
326-
c5want = array_copy_but_one(master[2:4, 2:5], 1, 1, 777)
326+
c5want = array_copy_but_one(primary[2:4, 2:5], 1, 1, 777)
327327

328328
a_corn1 = a.corners()
329329
assert not a_corn1.flags.owndata and a_corn1.flags.writeable
330330
a_corn1 *= 50
331331
a_corn1[1, 1] = 999
332-
master[0, 0] = 50
333-
master[0, 9] = 50
334-
master[9, 0] = 50
335-
master[9, 9] = 999
332+
primary[0, 0] = 50
333+
primary[0, 9] = 50
334+
primary[9, 0] = 50
335+
primary[9, 9] = 999
336336
a_corn2 = a.corners_const()
337337
assert not a_corn2.flags.owndata and not a_corn2.flags.writeable
338338
with pytest.raises(ValueError):
339339
a_corn2[1, 0] = 51
340340

341341
# All of the changes made all the way along should be visible everywhere
342342
# now (except for the copies, of course)
343-
np.testing.assert_array_equal(a_get1, master)
344-
np.testing.assert_array_equal(a_get2, master)
345-
np.testing.assert_array_equal(a_view1, master)
346-
np.testing.assert_array_equal(a_view2, master)
347-
np.testing.assert_array_equal(a_ref1, master)
348-
np.testing.assert_array_equal(a_ref2, master)
349-
np.testing.assert_array_equal(a_ref3, master)
350-
np.testing.assert_array_equal(a_ref4, master)
351-
np.testing.assert_array_equal(a_block1, master[3:5, 3:5])
352-
np.testing.assert_array_equal(a_block2, master[2:5, 2:4])
353-
np.testing.assert_array_equal(a_block3, master[6:10, 7:10])
343+
np.testing.assert_array_equal(a_get1, primary)
344+
np.testing.assert_array_equal(a_get2, primary)
345+
np.testing.assert_array_equal(a_view1, primary)
346+
np.testing.assert_array_equal(a_view2, primary)
347+
np.testing.assert_array_equal(a_ref1, primary)
348+
np.testing.assert_array_equal(a_ref2, primary)
349+
np.testing.assert_array_equal(a_ref3, primary)
350+
np.testing.assert_array_equal(a_ref4, primary)
351+
np.testing.assert_array_equal(a_block1, primary[3:5, 3:5])
352+
np.testing.assert_array_equal(a_block2, primary[2:5, 2:4])
353+
np.testing.assert_array_equal(a_block3, primary[6:10, 7:10])
354354
np.testing.assert_array_equal(
355-
a_corn1, master[0 :: master.shape[0] - 1, 0 :: master.shape[1] - 1]
355+
a_corn1, primary[0 :: primary.shape[0] - 1, 0 :: primary.shape[1] - 1]
356356
)
357357
np.testing.assert_array_equal(
358-
a_corn2, master[0 :: master.shape[0] - 1, 0 :: master.shape[1] - 1]
358+
a_corn2, primary[0 :: primary.shape[0] - 1, 0 :: primary.shape[1] - 1]
359359
)
360360

361361
np.testing.assert_array_equal(a_copy1, c1want)

0 commit comments

Comments
 (0)