Skip to content

Commit edad3ef

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents 2e6d06d + f41dae3 commit edad3ef

19 files changed

+502
-92
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ jobs:
174174
-DPYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION=ON
175175
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
176176
-DPYBIND11_NUMPY_1_ONLY=ON
177+
-DPYBIND11_PYTEST_ARGS=-v
177178
-DDOWNLOAD_CATCH=ON
178179
-DDOWNLOAD_EIGEN=ON
179180
-DCMAKE_CXX_STANDARD=11
@@ -203,6 +204,7 @@ jobs:
203204
-DPYBIND11_WERROR=ON
204205
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
205206
-DPYBIND11_NUMPY_1_ONLY=ON
207+
-DPYBIND11_PYTEST_ARGS=-v
206208
-DDOWNLOAD_CATCH=ON
207209
-DDOWNLOAD_EIGEN=ON
208210
-DCMAKE_CXX_STANDARD=17
@@ -222,6 +224,7 @@ jobs:
222224
run: >
223225
cmake -S . -B build3
224226
-DPYBIND11_WERROR=ON
227+
-DPYBIND11_PYTEST_ARGS=-v
225228
-DDOWNLOAD_CATCH=ON
226229
-DDOWNLOAD_EIGEN=ON
227230
-DCMAKE_CXX_STANDARD=17

.github/workflows/pip.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104
- uses: actions/download-artifact@v4
105105

106106
- name: Generate artifact attestation for sdist and wheel
107-
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
107+
uses: actions/attest-build-provenance@ef244123eb79f2f7a7e75d99086184180e6d0018 # v1.4.4
108108
with:
109109
subject-path: "*/pybind11*"
110110

include/pybind11/cast.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,10 +1295,18 @@ template <>
12951295
struct handle_type_name<args> {
12961296
static constexpr auto name = const_name("*args");
12971297
};
1298+
template <typename T>
1299+
struct handle_type_name<Args<T>> {
1300+
static constexpr auto name = const_name("*args: ") + make_caster<T>::name;
1301+
};
12981302
template <>
12991303
struct handle_type_name<kwargs> {
13001304
static constexpr auto name = const_name("**kwargs");
13011305
};
1306+
template <typename T>
1307+
struct handle_type_name<KWArgs<T>> {
1308+
static constexpr auto name = const_name("**kwargs: ") + make_caster<T>::name;
1309+
};
13021310
template <>
13031311
struct handle_type_name<obj_attr_accessor> {
13041312
static constexpr auto name = const_name<obj_attr_accessor>();

include/pybind11/detail/init.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
483483

484484
template <typename Class, typename... Extra>
485485
void execute(Class &cl, const Extra &...extra) && {
486-
cl.def("__getstate__", std::move(get));
486+
cl.def("__getstate__", std::move(get), pos_only());
487487

488488
#if defined(PYBIND11_CPP14)
489489
cl.def(

include/pybind11/numpy.h

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ constexpr int platform_lookup(int I, Ints... Is) {
212212
}
213213

214214
struct npy_api {
215+
// If you change this code, please review `normalized_dtype_num` below.
215216
enum constants {
216217
NPY_ARRAY_C_CONTIGUOUS_ = 0x0001,
217218
NPY_ARRAY_F_CONTIGUOUS_ = 0x0002,
@@ -384,6 +385,74 @@ struct npy_api {
384385
}
385386
};
386387

388+
// This table normalizes typenums by mapping NPY_INT_, NPY_LONG, ... to NPY_INT32_, NPY_INT64, ...
389+
// This is needed to correctly handle situations where multiple typenums map to the same type,
390+
// e.g. NPY_LONG_ may be equivalent to NPY_INT_ or NPY_LONGLONG_ despite having a different
391+
// typenum. The normalized typenum should always match the values used in npy_format_descriptor.
392+
// If you change this code, please review `enum constants` above.
393+
static constexpr int normalized_dtype_num[npy_api::NPY_VOID_ + 1] = {
394+
// NPY_BOOL_ =>
395+
npy_api::NPY_BOOL_,
396+
// NPY_BYTE_ =>
397+
npy_api::NPY_BYTE_,
398+
// NPY_UBYTE_ =>
399+
npy_api::NPY_UBYTE_,
400+
// NPY_SHORT_ =>
401+
npy_api::NPY_INT16_,
402+
// NPY_USHORT_ =>
403+
npy_api::NPY_UINT16_,
404+
// NPY_INT_ =>
405+
sizeof(int) == sizeof(std::int16_t) ? npy_api::NPY_INT16_
406+
: sizeof(int) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
407+
: sizeof(int) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
408+
: npy_api::NPY_INT_,
409+
// NPY_UINT_ =>
410+
sizeof(unsigned int) == sizeof(std::uint16_t) ? npy_api::NPY_UINT16_
411+
: sizeof(unsigned int) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
412+
: sizeof(unsigned int) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
413+
: npy_api::NPY_UINT_,
414+
// NPY_LONG_ =>
415+
sizeof(long) == sizeof(std::int16_t) ? npy_api::NPY_INT16_
416+
: sizeof(long) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
417+
: sizeof(long) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
418+
: npy_api::NPY_LONG_,
419+
// NPY_ULONG_ =>
420+
sizeof(unsigned long) == sizeof(std::uint16_t) ? npy_api::NPY_UINT16_
421+
: sizeof(unsigned long) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
422+
: sizeof(unsigned long) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
423+
: npy_api::NPY_ULONG_,
424+
// NPY_LONGLONG_ =>
425+
sizeof(long long) == sizeof(std::int16_t) ? npy_api::NPY_INT16_
426+
: sizeof(long long) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
427+
: sizeof(long long) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
428+
: npy_api::NPY_LONGLONG_,
429+
// NPY_ULONGLONG_ =>
430+
sizeof(unsigned long long) == sizeof(std::uint16_t) ? npy_api::NPY_UINT16_
431+
: sizeof(unsigned long long) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
432+
: sizeof(unsigned long long) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
433+
: npy_api::NPY_ULONGLONG_,
434+
// NPY_FLOAT_ =>
435+
npy_api::NPY_FLOAT_,
436+
// NPY_DOUBLE_ =>
437+
npy_api::NPY_DOUBLE_,
438+
// NPY_LONGDOUBLE_ =>
439+
npy_api::NPY_LONGDOUBLE_,
440+
// NPY_CFLOAT_ =>
441+
npy_api::NPY_CFLOAT_,
442+
// NPY_CDOUBLE_ =>
443+
npy_api::NPY_CDOUBLE_,
444+
// NPY_CLONGDOUBLE_ =>
445+
npy_api::NPY_CLONGDOUBLE_,
446+
// NPY_OBJECT_ =>
447+
npy_api::NPY_OBJECT_,
448+
// NPY_STRING_ =>
449+
npy_api::NPY_STRING_,
450+
// NPY_UNICODE_ =>
451+
npy_api::NPY_UNICODE_,
452+
// NPY_VOID_ =>
453+
npy_api::NPY_VOID_,
454+
};
455+
387456
inline PyArray_Proxy *array_proxy(void *ptr) { return reinterpret_cast<PyArray_Proxy *>(ptr); }
388457

389458
inline const PyArray_Proxy *array_proxy(const void *ptr) {
@@ -684,6 +753,13 @@ class dtype : public object {
684753
return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::dtype();
685754
}
686755

756+
/// Return the type number associated with a C++ type.
757+
/// This is the constexpr equivalent of `dtype::of<T>().num()`.
758+
template <typename T>
759+
static constexpr int num_of() {
760+
return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::value;
761+
}
762+
687763
/// Size of the data type in bytes.
688764
#ifdef PYBIND11_NUMPY_1_ONLY
689765
ssize_t itemsize() const { return detail::array_descriptor_proxy(m_ptr)->elsize; }
@@ -725,14 +801,27 @@ class dtype : public object {
725801
return detail::array_descriptor_proxy(m_ptr)->type;
726802
}
727803

728-
/// type number of dtype.
804+
/// Type number of dtype. Note that different values may be returned for equivalent types,
805+
/// e.g. even though ``long`` may be equivalent to ``int`` or ``long long``, they still have
806+
/// different type numbers. Consider using `normalized_num` to avoid this.
729807
int num() const {
730808
// Note: The signature, `dtype::num` follows the naming of NumPy's public
731809
// Python API (i.e., ``dtype.num``), rather than its internal
732810
// C API (``PyArray_Descr::type_num``).
733811
return detail::array_descriptor_proxy(m_ptr)->type_num;
734812
}
735813

814+
/// Type number of dtype, normalized to match the return value of `num_of` for equivalent
815+
/// types. This function can be used to write switch statements that correctly handle
816+
/// equivalent types with different type numbers.
817+
int normalized_num() const {
818+
int value = num();
819+
if (value >= 0 && value <= detail::npy_api::NPY_VOID_) {
820+
return detail::normalized_dtype_num[value];
821+
}
822+
return value;
823+
}
824+
736825
/// Single character for byteorder
737826
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
738827

@@ -1428,7 +1517,11 @@ struct npy_format_descriptor<
14281517
};
14291518

14301519
template <typename T>
1431-
struct npy_format_descriptor<T, enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value>> {
1520+
struct npy_format_descriptor<
1521+
T,
1522+
enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value
1523+
|| ((std::is_same<T, handle>::value || std::is_same<T, object>::value)
1524+
&& sizeof(T) == sizeof(PyObject *))>> {
14321525
static constexpr auto name = const_name("object");
14331526

14341527
static constexpr int value = npy_api::NPY_OBJECT_;

include/pybind11/pybind11.h

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,20 @@ class cpp_function : public function {
303303
constexpr bool has_kw_only_args = any_of<std::is_same<kw_only, Extra>...>::value,
304304
has_pos_only_args = any_of<std::is_same<pos_only, Extra>...>::value,
305305
has_arg_annotations = any_of<is_keyword<Extra>...>::value;
306+
constexpr bool has_is_method = any_of<std::is_same<is_method, Extra>...>::value;
307+
// The implicit `self` argument is not present and not counted in method definitions.
308+
constexpr bool has_args = cast_in::args_pos >= 0;
309+
constexpr bool is_method_with_self_arg_only = has_is_method && !has_args;
306310
static_assert(has_arg_annotations || !has_kw_only_args,
307311
"py::kw_only requires the use of argument annotations");
308-
static_assert(has_arg_annotations || !has_pos_only_args,
312+
static_assert(((/* Need `py::arg("arg_name")` annotation in function/method. */
313+
has_arg_annotations)
314+
|| (/* Allow methods with no arguments `def method(self, /): ...`.
315+
* A method has at least one argument `self`. There can be no
316+
* `py::arg` annotation. E.g. `class.def("method", py::pos_only())`.
317+
*/
318+
is_method_with_self_arg_only))
319+
|| !has_pos_only_args,
309320
"py::pos_only requires the use of argument annotations (for docstrings "
310321
"and aligning the annotations to the argument)");
311322

@@ -2361,17 +2372,20 @@ struct enum_base {
23612372
.format(std::move(type_name), enum_name(arg), int_(arg));
23622373
},
23632374
name("__repr__"),
2364-
is_method(m_base));
2375+
is_method(m_base),
2376+
pos_only());
23652377

2366-
m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base)));
2378+
m_base.attr("name")
2379+
= property(cpp_function(&enum_name, name("name"), is_method(m_base), pos_only()));
23672380

23682381
m_base.attr("__str__") = cpp_function(
23692382
[](handle arg) -> str {
23702383
object type_name = type::handle_of(arg).attr("__name__");
23712384
return pybind11::str("{}.{}").format(std::move(type_name), enum_name(arg));
23722385
},
23732386
name("__str__"),
2374-
is_method(m_base));
2387+
is_method(m_base),
2388+
pos_only());
23752389

23762390
if (options::show_enum_members_docstring()) {
23772391
m_base.attr("__doc__") = static_property(
@@ -2426,7 +2440,8 @@ struct enum_base {
24262440
}, \
24272441
name(op), \
24282442
is_method(m_base), \
2429-
arg("other"))
2443+
arg("other"), \
2444+
pos_only())
24302445

24312446
#define PYBIND11_ENUM_OP_CONV(op, expr) \
24322447
m_base.attr(op) = cpp_function( \
@@ -2436,7 +2451,8 @@ struct enum_base {
24362451
}, \
24372452
name(op), \
24382453
is_method(m_base), \
2439-
arg("other"))
2454+
arg("other"), \
2455+
pos_only())
24402456

24412457
#define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \
24422458
m_base.attr(op) = cpp_function( \
@@ -2446,7 +2462,8 @@ struct enum_base {
24462462
}, \
24472463
name(op), \
24482464
is_method(m_base), \
2449-
arg("other"))
2465+
arg("other"), \
2466+
pos_only())
24502467

24512468
if (is_convertible) {
24522469
PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b));
@@ -2466,7 +2483,8 @@ struct enum_base {
24662483
m_base.attr("__invert__")
24672484
= cpp_function([](const object &arg) { return ~(int_(arg)); },
24682485
name("__invert__"),
2469-
is_method(m_base));
2486+
is_method(m_base),
2487+
pos_only());
24702488
}
24712489
} else {
24722490
PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
@@ -2486,11 +2504,15 @@ struct enum_base {
24862504
#undef PYBIND11_ENUM_OP_CONV
24872505
#undef PYBIND11_ENUM_OP_STRICT
24882506

2489-
m_base.attr("__getstate__") = cpp_function(
2490-
[](const object &arg) { return int_(arg); }, name("__getstate__"), is_method(m_base));
2507+
m_base.attr("__getstate__") = cpp_function([](const object &arg) { return int_(arg); },
2508+
name("__getstate__"),
2509+
is_method(m_base),
2510+
pos_only());
24912511

2492-
m_base.attr("__hash__") = cpp_function(
2493-
[](const object &arg) { return int_(arg); }, name("__hash__"), is_method(m_base));
2512+
m_base.attr("__hash__") = cpp_function([](const object &arg) { return int_(arg); },
2513+
name("__hash__"),
2514+
is_method(m_base),
2515+
pos_only());
24942516
}
24952517

24962518
PYBIND11_NOINLINE void value(char const *name_, object value, const char *doc = nullptr) {
@@ -2582,9 +2604,9 @@ class enum_ : public class_<Type> {
25822604
m_base.init(is_arithmetic, is_convertible);
25832605

25842606
def(init([](Scalar i) { return static_cast<Type>(i); }), arg("value"));
2585-
def_property_readonly("value", [](Type value) { return (Scalar) value; });
2586-
def("__int__", [](Type value) { return (Scalar) value; });
2587-
def("__index__", [](Type value) { return (Scalar) value; });
2607+
def_property_readonly("value", [](Type value) { return (Scalar) value; }, pos_only());
2608+
def("__int__", [](Type value) { return (Scalar) value; }, pos_only());
2609+
def("__index__", [](Type value) { return (Scalar) value; }, pos_only());
25882610
attr("__setstate__") = cpp_function(
25892611
[](detail::value_and_holder &v_h, Scalar arg) {
25902612
detail::initimpl::setstate<Base>(
@@ -2593,7 +2615,8 @@ class enum_ : public class_<Type> {
25932615
detail::is_new_style_constructor(),
25942616
pybind11::name("__setstate__"),
25952617
is_method(*this),
2596-
arg("state"));
2618+
arg("state"),
2619+
pos_only());
25972620
}
25982621

25992622
/// Export enumeration entries into the parent scope
@@ -2779,7 +2802,8 @@ iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
27792802

27802803
if (!detail::get_type_info(typeid(state), false)) {
27812804
class_<state>(handle(), "iterator", pybind11::module_local())
2782-
.def("__iter__", [](state &s) -> state & { return s; })
2805+
.def(
2806+
"__iter__", [](state &s) -> state & { return s; }, pos_only())
27832807
.def(
27842808
"__next__",
27852809
[](state &s) -> ValueType {
@@ -2796,6 +2820,7 @@ iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
27962820
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
27972821
},
27982822
std::forward<Extra>(extra)...,
2823+
pos_only(),
27992824
Policy);
28002825
}
28012826

include/pybind11/pytypes.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,6 +2226,18 @@ class kwargs : public dict {
22262226
PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check)
22272227
};
22282228

2229+
// Subclasses of args and kwargs to support type hinting
2230+
// as defined in PEP 484. See #5357 for more info.
2231+
template <typename T>
2232+
class Args : public args {
2233+
using args::args;
2234+
};
2235+
2236+
template <typename T>
2237+
class KWArgs : public kwargs {
2238+
using kwargs::kwargs;
2239+
};
2240+
22292241
class anyset : public object {
22302242
public:
22312243
PYBIND11_OBJECT(anyset, object, PyAnySet_Check)

tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,11 @@ def pytest_assertrepr_compare(op, left, right): # noqa: ARG001
198198

199199

200200
def gc_collect():
201-
"""Run the garbage collector twice (needed when running
201+
"""Run the garbage collector three times (needed when running
202202
reference counting tests with PyPy)"""
203203
gc.collect()
204204
gc.collect()
205+
gc.collect()
205206

206207

207208
def pytest_configure():

0 commit comments

Comments
 (0)