Skip to content

Commit cc3209c

Browse files
Merge branch 'master' into expand-float-strict
2 parents 816298a + 42cda75 commit cc3209c

File tree

11 files changed

+114
-35
lines changed

11 files changed

+114
-35
lines changed

include/pybind11/cast.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,21 +1469,24 @@ template <>
14691469
struct handle_type_name<weakref> {
14701470
static constexpr auto name = const_name("weakref.ReferenceType");
14711471
};
1472+
// args/Args/kwargs/KWArgs have name as well as typehint included
14721473
template <>
14731474
struct handle_type_name<args> {
1474-
static constexpr auto name = const_name("*args");
1475+
static constexpr auto name = io_name("*args", "tuple");
14751476
};
14761477
template <typename T>
14771478
struct handle_type_name<Args<T>> {
1478-
static constexpr auto name = const_name("*args: ") + make_caster<T>::name;
1479+
static constexpr auto name
1480+
= io_name("*args: ", "tuple[") + make_caster<T>::name + io_name("", ", ...]");
14791481
};
14801482
template <>
14811483
struct handle_type_name<kwargs> {
1482-
static constexpr auto name = const_name("**kwargs");
1484+
static constexpr auto name = io_name("**kwargs", "dict[str, typing.Any]");
14831485
};
14841486
template <typename T>
14851487
struct handle_type_name<KWArgs<T>> {
1486-
static constexpr auto name = const_name("**kwargs: ") + make_caster<T>::name;
1488+
static constexpr auto name
1489+
= io_name("**kwargs: ", "dict[str, ") + make_caster<T>::name + io_name("", "]");
14871490
};
14881491
template <>
14891492
struct handle_type_name<obj_attr_accessor> {
@@ -1909,13 +1912,20 @@ inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
19091912
}
19101913
#endif
19111914

1915+
namespace typing {
1916+
template <typename... Types>
1917+
class Tuple : public tuple {
1918+
using tuple::tuple;
1919+
};
1920+
} // namespace typing
1921+
19121922
template <return_value_policy policy = return_value_policy::automatic_reference>
1913-
tuple make_tuple() {
1923+
typing::Tuple<> make_tuple() {
19141924
return tuple(0);
19151925
}
19161926

19171927
template <return_value_policy policy = return_value_policy::automatic_reference, typename... Args>
1918-
tuple make_tuple(Args &&...args_) {
1928+
typing::Tuple<Args...> make_tuple(Args &&...args_) {
19191929
constexpr size_t size = sizeof...(Args);
19201930
std::array<object, size> args{{reinterpret_steal<object>(
19211931
detail::make_caster<Args>::cast(std::forward<Args>(args_), policy, nullptr))...}};
@@ -1934,7 +1944,12 @@ tuple make_tuple(Args &&...args_) {
19341944
for (auto &arg_value : args) {
19351945
PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr());
19361946
}
1947+
PYBIND11_WARNING_PUSH
1948+
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
1949+
PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
1950+
#endif
19371951
return result;
1952+
PYBIND11_WARNING_POP
19381953
}
19391954

19401955
/// \ingroup annotations

include/pybind11/detail/common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@
103103
# define PYBIND11_DTOR_CONSTEXPR
104104
#endif
105105

106+
#if defined(PYBIND11_CPP20) && defined(__has_include) && __has_include(<barrier>)
107+
# define PYBIND11_HAS_STD_BARRIER 1
108+
#endif
109+
106110
// Compiler version assertions
107111
#if defined(__INTEL_COMPILER)
108112
# if __INTEL_COMPILER < 1800

include/pybind11/detail/init.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,15 @@ template <typename Get,
501501
typename NewInstance,
502502
typename ArgState>
503503
struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
504-
static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
505-
"The type returned by `__getstate__` must be the same "
506-
"as the argument accepted by `__setstate__`");
504+
using Ret = intrinsic_t<RetState>;
505+
using Arg = intrinsic_t<ArgState>;
506+
507+
// Subclasses are now allowed for support between type hint and generic versions of types
508+
// (e.g.) typing::List <--> list
509+
static_assert(std::is_same<Ret, Arg>::value || std::is_base_of<Ret, Arg>::value
510+
|| std::is_base_of<Arg, Ret>::value,
511+
"The type returned by `__getstate__` must be the same or subclass of the "
512+
"argument accepted by `__setstate__`");
507513

508514
remove_reference_t<Get> get;
509515
remove_reference_t<Set> set;

include/pybind11/gil.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,11 @@ class gil_scoped_acquire {
120120
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
121121
}
122122
# endif
123+
// Make sure that PyThreadState_Clear is not recursively called by finalizers.
124+
// See issue #5827
125+
++tstate->gilstate_counter;
123126
PyThreadState_Clear(tstate);
127+
--tstate->gilstate_counter;
124128
if (active) {
125129
PyThreadState_DeleteCurrent();
126130
}

include/pybind11/pybind11.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
120120
const auto c = *pc;
121121
if (c == '{') {
122122
// Write arg name for everything except *args and **kwargs.
123-
is_starred = *(pc + 1) == '*';
123+
// Detect {@*args...} or {@**kwargs...}
124+
is_starred = *(pc + 1) == '@' && *(pc + 2) == '*';
124125
if (is_starred) {
125126
continue;
126127
}

include/pybind11/typing.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ PYBIND11_NAMESPACE_BEGIN(typing)
3434
There is no additional enforcement of types at runtime.
3535
*/
3636

37-
template <typename... Types>
38-
class Tuple : public tuple {
39-
using tuple::tuple;
40-
};
37+
// Tuple type hint defined in cast.h for use in py::make_tuple to avoid circular includes
4138

4239
template <typename K, typename V>
4340
class Dict : public dict {

tests/test_factory_constructors.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,10 +376,14 @@ TEST_SUBMODULE(factory_constructors, m) {
376376
py::print("noisy placement new");
377377
return p;
378378
}
379-
static void operator delete(void *p, size_t) {
379+
static void operator delete(void *p) noexcept {
380380
py::print("noisy delete");
381381
::operator delete(p);
382382
}
383+
static void operator delete(void *p, size_t) {
384+
py::print("noisy delete size");
385+
::operator delete(p);
386+
}
383387
static void operator delete(void *, void *) { py::print("noisy placement delete"); }
384388
};
385389

tests/test_kwargs_and_defaults.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ def test_function_signatures(doc):
3434
)
3535
assert doc(m.args_function) == "args_function(*args) -> tuple"
3636
assert (
37-
doc(m.args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple"
37+
doc(m.args_kwargs_function)
38+
== "args_kwargs_function(*args, **kwargs) -> tuple[tuple, dict[str, typing.Any]]"
3839
)
3940
assert (
4041
doc(m.args_kwargs_subclass_function)
41-
== "args_kwargs_subclass_function(*args: str, **kwargs: str) -> tuple"
42+
== "args_kwargs_subclass_function(*args: str, **kwargs: str) -> tuple[tuple[str, ...], dict[str, str]]"
4243
)
4344
assert (
4445
doc(m.KWClass.foo0)
@@ -138,7 +139,7 @@ def test_mixed_args_and_kwargs(msg):
138139
msg(excinfo.value)
139140
== """
140141
mixed_plus_args(): incompatible function arguments. The following argument types are supported:
141-
1. (arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex, *args) -> tuple
142+
1. (arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex, *args) -> tuple[int, float, tuple]
142143
143144
Invoked with: 1
144145
"""
@@ -149,7 +150,7 @@ def test_mixed_args_and_kwargs(msg):
149150
msg(excinfo.value)
150151
== """
151152
mixed_plus_args(): incompatible function arguments. The following argument types are supported:
152-
1. (arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex, *args) -> tuple
153+
1. (arg0: typing.SupportsInt | typing.SupportsIndex, arg1: typing.SupportsFloat | typing.SupportsIndex, *args) -> tuple[int, float, tuple]
153154
154155
Invoked with:
155156
"""
@@ -183,7 +184,7 @@ def test_mixed_args_and_kwargs(msg):
183184
msg(excinfo.value)
184185
== """
185186
mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported:
186-
1. (i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, **kwargs) -> tuple
187+
1. (i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, **kwargs) -> tuple[int, float, tuple, dict[str, typing.Any]]
187188
188189
Invoked with: 1; kwargs: i=1
189190
"""
@@ -194,7 +195,7 @@ def test_mixed_args_and_kwargs(msg):
194195
msg(excinfo.value)
195196
== """
196197
mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported:
197-
1. (i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, **kwargs) -> tuple
198+
1. (i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, **kwargs) -> tuple[int, float, tuple, dict[str, typing.Any]]
198199
199200
Invoked with: 1, 2; kwargs: j=1
200201
"""
@@ -211,7 +212,7 @@ def test_mixed_args_and_kwargs(msg):
211212
msg(excinfo.value)
212213
== """
213214
args_kwonly(): incompatible function arguments. The following argument types are supported:
214-
1. (i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsFloat | typing.SupportsIndex, *args, z: typing.SupportsInt | typing.SupportsIndex) -> tuple
215+
1. (i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsFloat | typing.SupportsIndex, *args, z: typing.SupportsInt | typing.SupportsIndex) -> tuple[int, float, tuple, int]
215216
216217
Invoked with: 2, 2.5, 22
217218
"""
@@ -233,12 +234,12 @@ def test_mixed_args_and_kwargs(msg):
233234
)
234235
assert (
235236
m.args_kwonly_kwargs.__doc__
236-
== "args_kwonly_kwargs(i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsFloat | typing.SupportsIndex, *args, z: typing.SupportsInt | typing.SupportsIndex, **kwargs) -> tuple\n"
237+
== "args_kwonly_kwargs(i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsFloat | typing.SupportsIndex, *args, z: typing.SupportsInt | typing.SupportsIndex, **kwargs) -> tuple[int, float, tuple, int, dict[str, typing.Any]]\n"
237238
)
238239

239240
assert (
240241
m.args_kwonly_kwargs_defaults.__doc__
241-
== "args_kwonly_kwargs_defaults(i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, z: typing.SupportsInt | typing.SupportsIndex = 42, **kwargs) -> tuple\n"
242+
== "args_kwonly_kwargs_defaults(i: typing.SupportsInt | typing.SupportsIndex = 1, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, z: typing.SupportsInt | typing.SupportsIndex = 42, **kwargs) -> tuple[int, float, tuple, int, dict[str, typing.Any]]\n"
242243
)
243244
assert m.args_kwonly_kwargs_defaults() == (1, 3.14159, (), 42, {})
244245
assert m.args_kwonly_kwargs_defaults(2) == (2, 3.14159, (), 42, {})
@@ -344,7 +345,7 @@ def test_positional_only_args():
344345
# Mix it with args and kwargs:
345346
assert (
346347
m.args_kwonly_full_monty.__doc__
347-
== "args_kwonly_full_monty(arg0: typing.SupportsInt | typing.SupportsIndex = 1, arg1: typing.SupportsInt | typing.SupportsIndex = 2, /, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, z: typing.SupportsInt | typing.SupportsIndex = 42, **kwargs) -> tuple\n"
348+
== "args_kwonly_full_monty(arg0: typing.SupportsInt | typing.SupportsIndex = 1, arg1: typing.SupportsInt | typing.SupportsIndex = 2, /, j: typing.SupportsFloat | typing.SupportsIndex = 3.14159, *args, z: typing.SupportsInt | typing.SupportsIndex = 42, **kwargs) -> tuple[int, int, float, tuple, int, dict[str, typing.Any]]\n"
348349
)
349350
assert m.args_kwonly_full_monty() == (1, 2, 3.14159, (), 42, {})
350351
assert m.args_kwonly_full_monty(8) == (8, 2, 3.14159, (), 42, {})
@@ -394,23 +395,23 @@ def test_positional_only_args():
394395
def test_signatures():
395396
assert (
396397
m.kw_only_all.__doc__
397-
== "kw_only_all(*, i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex) -> tuple\n"
398+
== "kw_only_all(*, i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex) -> tuple[int, int]\n"
398399
)
399400
assert (
400401
m.kw_only_mixed.__doc__
401-
== "kw_only_mixed(i: typing.SupportsInt | typing.SupportsIndex, *, j: typing.SupportsInt | typing.SupportsIndex) -> tuple\n"
402+
== "kw_only_mixed(i: typing.SupportsInt | typing.SupportsIndex, *, j: typing.SupportsInt | typing.SupportsIndex) -> tuple[int, int]\n"
402403
)
403404
assert (
404405
m.pos_only_all.__doc__
405-
== "pos_only_all(i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex, /) -> tuple\n"
406+
== "pos_only_all(i: typing.SupportsInt | typing.SupportsIndex, j: typing.SupportsInt | typing.SupportsIndex, /) -> tuple[int, int]\n"
406407
)
407408
assert (
408409
m.pos_only_mix.__doc__
409-
== "pos_only_mix(i: typing.SupportsInt | typing.SupportsIndex, /, j: typing.SupportsInt | typing.SupportsIndex) -> tuple\n"
410+
== "pos_only_mix(i: typing.SupportsInt | typing.SupportsIndex, /, j: typing.SupportsInt | typing.SupportsIndex) -> tuple[int, int]\n"
410411
)
411412
assert (
412413
m.pos_kw_only_mix.__doc__
413-
== "pos_kw_only_mix(i: typing.SupportsInt | typing.SupportsIndex, /, j: typing.SupportsInt | typing.SupportsIndex, *, k: typing.SupportsInt | typing.SupportsIndex) -> tuple\n"
414+
== "pos_kw_only_mix(i: typing.SupportsInt | typing.SupportsIndex, /, j: typing.SupportsInt | typing.SupportsIndex, *, k: typing.SupportsInt | typing.SupportsIndex) -> tuple[int, int, int]\n"
414415
)
415416

416417

tests/test_scoped_critical_section.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
#include <thread>
88
#include <utility>
99

10-
#if defined(PYBIND11_CPP20) && defined(__has_include) && __has_include(<barrier>)
11-
# define PYBIND11_HAS_BARRIER 1
10+
#if defined(PYBIND11_HAS_STD_BARRIER)
1211
# include <barrier>
1312
#endif
1413

@@ -39,7 +38,7 @@ class BoolWrapper {
3938
std::atomic_bool value_{false};
4039
};
4140

42-
#if defined(PYBIND11_HAS_BARRIER)
41+
#if defined(PYBIND11_HAS_STD_BARRIER)
4342

4443
// Modifying the C/C++ members of a Python object from multiple threads requires a critical section
4544
// to ensure thread safety and data integrity.
@@ -259,7 +258,7 @@ TEST_SUBMODULE(scoped_critical_section, m) {
259258
(void) BoolWrapperHandle.ptr(); // suppress unused variable warning
260259

261260
m.attr("has_barrier") =
262-
#ifdef PYBIND11_HAS_BARRIER
261+
#ifdef PYBIND11_HAS_STD_BARRIER
263262
true;
264263
#else
265264
false;

tests/test_thread.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
#include <chrono>
1616
#include <thread>
1717

18+
#if defined(PYBIND11_HAS_STD_BARRIER)
19+
# include <barrier>
20+
#endif
21+
1822
namespace py = pybind11;
1923

2024
namespace {
@@ -34,7 +38,6 @@ EmptyStruct SharedInstance;
3438
} // namespace
3539

3640
TEST_SUBMODULE(thread, m) {
37-
3841
py::class_<IntStruct>(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); }));
3942

4043
// implicitly_convertible uses loader_life_support when an implicit
@@ -67,6 +70,39 @@ TEST_SUBMODULE(thread, m) {
6770
py::class_<EmptyStruct>(m, "EmptyStruct")
6871
.def_readonly_static("SharedInstance", &SharedInstance);
6972

73+
#if defined(PYBIND11_HAS_STD_BARRIER)
74+
// In the free-threaded build, during PyThreadState_Clear, removing the thread from the biased
75+
// reference counting table may call destructors. Make sure that it doesn't crash.
76+
m.def("test_pythread_state_clear_destructor", [](py::type cls) {
77+
py::handle obj;
78+
79+
std::barrier barrier{2};
80+
std::thread thread1{[&]() {
81+
py::gil_scoped_acquire gil;
82+
obj = cls().release();
83+
barrier.arrive_and_wait();
84+
}};
85+
std::thread thread2{[&]() {
86+
py::gil_scoped_acquire gil;
87+
barrier.arrive_and_wait();
88+
// ob_ref_shared becomes negative; transition to the queued state
89+
obj.dec_ref();
90+
}};
91+
92+
// jthread is not supported by Apple Clang
93+
thread1.join();
94+
thread2.join();
95+
});
96+
#endif
97+
98+
m.attr("defined_PYBIND11_HAS_STD_BARRIER") =
99+
#ifdef PYBIND11_HAS_STD_BARRIER
100+
true;
101+
#else
102+
false;
103+
#endif
104+
m.def("acquire_gil", []() { py::gil_scoped_acquire gil_acquired; });
105+
70106
// NOTE: std::string_view also uses loader_life_support to ensure that
71107
// the string contents remain alive, but that's a C++ 17 feature.
72108
}

0 commit comments

Comments
 (0)