Skip to content

Commit 7ab23e4

Browse files
Merge branch 'pybind:master' into master
2 parents 27b37a5 + 566894d commit 7ab23e4

File tree

9 files changed

+150
-34
lines changed

9 files changed

+150
-34
lines changed

include/pybind11/cast.h

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,15 +1606,9 @@ inline void object::cast() && {
16061606

16071607
PYBIND11_NAMESPACE_BEGIN(detail)
16081608

1609-
// forward declaration (definition in attr.h)
1610-
struct function_record;
1611-
16121609
// forward declaration (definition in pybind11.h)
1613-
std::string generate_function_signature(const char *type_caster_name_field,
1614-
function_record *func_rec,
1615-
const std::type_info *const *types,
1616-
size_t &type_index,
1617-
size_t &arg_index);
1610+
template <typename T>
1611+
std::string generate_type_signature();
16181612

16191613
// Declared in pytypes.h:
16201614
template <typename T, enable_if_t<!is_pyobject<T>::value, int>>
@@ -1637,10 +1631,7 @@ str_attr_accessor object_api<D>::attr_with_type_hint(const char *key) const {
16371631
throw std::runtime_error("__annotations__[\"" + std::string(key) + "\"] was set already.");
16381632
}
16391633

1640-
const char *text = make_caster<T>::name.text;
1641-
1642-
size_t unused = 0;
1643-
ann[key] = generate_function_signature(text, nullptr, nullptr, unused, unused);
1634+
ann[key] = generate_type_signature<T>();
16441635
return {derived(), key};
16451636
}
16461637

include/pybind11/detail/common.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,9 @@
372372
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
373373
catch (pybind11::error_already_set & e) { \
374374
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
375-
return nullptr; \
376375
} \
377376
catch (const std::exception &e) { \
378377
::pybind11::set_error(PyExc_ImportError, e.what()); \
379-
return nullptr; \
380378
}
381379

382380
/** \rst
@@ -404,6 +402,7 @@
404402
return pybind11_init(); \
405403
} \
406404
PYBIND11_CATCH_INIT_EXCEPTIONS \
405+
return nullptr; \
407406
} \
408407
PyObject *pybind11_init()
409408

@@ -447,23 +446,33 @@
447446
PYBIND11_WARNING_PUSH
448447
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
449448
#define PYBIND11_MODULE(name, variable, ...) \
450-
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
451-
PYBIND11_MAYBE_UNUSED; \
452-
PYBIND11_MAYBE_UNUSED \
449+
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
450+
static ::pybind11::module_::slots_array PYBIND11_CONCAT(pybind11_module_slots_, name); \
451+
static int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject *); \
453452
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
454453
PYBIND11_PLUGIN_IMPL(name) { \
455454
PYBIND11_CHECK_PYTHON_VERSION \
456455
PYBIND11_ENSURE_INTERNALS_READY \
457-
auto m = ::pybind11::module_::create_extension_module( \
456+
auto &slots = PYBIND11_CONCAT(pybind11_module_slots_, name); \
457+
slots[0] \
458+
= {Py_mod_exec, reinterpret_cast<void *>(&PYBIND11_CONCAT(pybind11_exec_, name))}; \
459+
slots[1] = {0, nullptr}; \
460+
auto m = ::pybind11::module_::initialize_multiphase_module_def( \
458461
PYBIND11_TOSTRING(name), \
459462
nullptr, \
460463
&PYBIND11_CONCAT(pybind11_module_def_, name), \
464+
slots, \
461465
##__VA_ARGS__); \
466+
return m.ptr(); \
467+
} \
468+
int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject * pm) { \
462469
try { \
470+
auto m = pybind11::reinterpret_borrow<::pybind11::module_>(pm); \
463471
PYBIND11_CONCAT(pybind11_init_, name)(m); \
464-
return m.ptr(); \
472+
return 0; \
465473
} \
466474
PYBIND11_CATCH_INIT_EXCEPTIONS \
475+
return -1; \
467476
} \
468477
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
469478
PYBIND11_WARNING_POP

include/pybind11/embed.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
return m.ptr(); \
5050
} \
5151
PYBIND11_CATCH_INIT_EXCEPTIONS \
52+
return nullptr; \
5253
} \
5354
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
5455
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \

include/pybind11/numpy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1638,7 +1638,7 @@ struct npy_format_descriptor<
16381638
enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value
16391639
|| ((std::is_same<T, handle>::value || std::is_same<T, object>::value)
16401640
&& sizeof(T) == sizeof(PyObject *))>> {
1641-
static constexpr auto name = const_name("object");
1641+
static constexpr auto name = const_name("numpy.object_");
16421642

16431643
static constexpr int value = npy_api::NPY_OBJECT_;
16441644

include/pybind11/pybind11.h

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
112112
size_t &arg_index) {
113113
std::string signature;
114114
bool is_starred = false;
115-
bool is_annotation = func_rec == nullptr;
116115
// `is_return_value.top()` is true if we are currently inside the return type of the
117116
// signature. Using `@^`/`@$` we can force types to be arg/return types while `@!` pops
118117
// back to the previous state.
@@ -199,9 +198,7 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
199198
// For named arguments (py::arg()) with noconvert set, return value type is used.
200199
++pc;
201200
if (!is_return_value.top()
202-
&& (is_annotation
203-
|| !(arg_index < func_rec->args.size()
204-
&& !func_rec->args[arg_index].convert))) {
201+
&& (!(arg_index < func_rec->args.size() && !func_rec->args[arg_index].convert))) {
205202
while (*pc != '\0' && *pc != '@') {
206203
signature += *pc++;
207204
}
@@ -232,6 +229,19 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
232229
return signature;
233230
}
234231

232+
template <typename T>
233+
inline std::string generate_type_signature() {
234+
static constexpr auto caster_name_field = make_caster<T>::name;
235+
PYBIND11_DESCR_CONSTEXPR auto descr_types = decltype(caster_name_field)::types();
236+
// Create a default function_record to ensure the function signature has the proper
237+
// configuration e.g. no_convert.
238+
auto func_rec = function_record();
239+
size_t type_index = 0;
240+
size_t arg_index = 0;
241+
return generate_function_signature(
242+
caster_name_field.text, &func_rec, descr_types.data(), type_index, arg_index);
243+
}
244+
235245
#if defined(_MSC_VER)
236246
# define PYBIND11_COMPAT_STRDUP _strdup
237247
#else
@@ -1255,6 +1265,22 @@ class mod_gil_not_used {
12551265
bool flag_;
12561266
};
12571267

1268+
PYBIND11_NAMESPACE_BEGIN(detail)
1269+
1270+
inline bool gil_not_used_option() { return false; }
1271+
template <typename F, typename... O>
1272+
bool gil_not_used_option(F &&, O &&...o);
1273+
template <typename... O>
1274+
inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) {
1275+
return f.flag() || gil_not_used_option(o...);
1276+
}
1277+
template <typename F, typename... O>
1278+
inline bool gil_not_used_option(F &&, O &&...o) {
1279+
return gil_not_used_option(o...);
1280+
}
1281+
1282+
PYBIND11_NAMESPACE_END(detail)
1283+
12581284
/// Wrapper for Python extension modules
12591285
class module_ : public object {
12601286
public:
@@ -1362,16 +1388,15 @@ class module_ : public object {
13621388
= mod_gil_not_used(false)) {
13631389
// module_def is PyModuleDef
13641390
// Placement new (not an allocation).
1365-
def = new (def)
1366-
PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1367-
/* m_name */ name,
1368-
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
1369-
/* m_size */ -1,
1370-
/* m_methods */ nullptr,
1371-
/* m_slots */ nullptr,
1372-
/* m_traverse */ nullptr,
1373-
/* m_clear */ nullptr,
1374-
/* m_free */ nullptr};
1391+
new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1392+
/* m_name */ name,
1393+
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
1394+
/* m_size */ -1,
1395+
/* m_methods */ nullptr,
1396+
/* m_slots */ nullptr,
1397+
/* m_traverse */ nullptr,
1398+
/* m_clear */ nullptr,
1399+
/* m_free */ nullptr};
13751400
auto *m = PyModule_Create(def);
13761401
if (m == nullptr) {
13771402
if (PyErr_Occurred()) {
@@ -1389,6 +1414,68 @@ class module_ : public object {
13891414
// For Python 2, reinterpret_borrow was correct.
13901415
return reinterpret_borrow<module_>(m);
13911416
}
1417+
1418+
/// Must be a POD type, and must hold enough entries for all of the possible slots PLUS ONE for
1419+
/// the sentinel (0) end slot.
1420+
using slots_array = std::array<PyModuleDef_Slot, 3>;
1421+
1422+
/** \rst
1423+
Initialized a module def for use with multi-phase module initialization.
1424+
1425+
``def`` should point to a statically allocated module_def.
1426+
``slots`` must already contain a Py_mod_exec or Py_mod_create slot and will be filled with
1427+
additional slots from the supplied options (and the empty sentinel slot).
1428+
\endrst */
1429+
template <typename... Options>
1430+
static object initialize_multiphase_module_def(const char *name,
1431+
const char *doc,
1432+
module_def *def,
1433+
slots_array &slots,
1434+
Options &&...options) {
1435+
size_t next_slot = 0;
1436+
size_t term_slot = slots.size() - 1;
1437+
1438+
// find the end of the supplied slots
1439+
while (next_slot < term_slot && slots[next_slot].slot != 0) {
1440+
++next_slot;
1441+
}
1442+
1443+
bool nogil PYBIND11_MAYBE_UNUSED = detail::gil_not_used_option(options...);
1444+
if (nogil) {
1445+
#if defined(Py_mod_gil) && defined(Py_GIL_DISABLED)
1446+
if (next_slot >= term_slot) {
1447+
pybind11_fail("initialize_multiphase_module_def: not enough space in slots");
1448+
}
1449+
slots[next_slot++] = {Py_mod_gil, Py_MOD_GIL_NOT_USED};
1450+
#endif
1451+
}
1452+
1453+
// slots must have a zero end sentinel
1454+
if (next_slot > term_slot) {
1455+
pybind11_fail("initialize_multiphase_module_def: not enough space in slots");
1456+
}
1457+
slots[next_slot++] = {0, nullptr};
1458+
1459+
// module_def is PyModuleDef
1460+
// Placement new (not an allocation).
1461+
new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1462+
/* m_name */ name,
1463+
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
1464+
/* m_size */ 0,
1465+
/* m_methods */ nullptr,
1466+
/* m_slots */ &slots[0],
1467+
/* m_traverse */ nullptr,
1468+
/* m_clear */ nullptr,
1469+
/* m_free */ nullptr};
1470+
auto *m = PyModuleDef_Init(def);
1471+
if (m == nullptr) {
1472+
if (PyErr_Occurred()) {
1473+
throw error_already_set();
1474+
}
1475+
pybind11_fail("Internal error in module_::initialize_multiphase_module_def()");
1476+
}
1477+
return reinterpret_borrow<object>(m);
1478+
}
13921479
};
13931480

13941481
PYBIND11_NAMESPACE_BEGIN(detail)

tests/test_exceptions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def test_cross_module_exceptions(msg):
7979
"env.MACOS and env.PYPY",
8080
raises=RuntimeError,
8181
reason="See Issue #2847, PR #2999, PR #4324",
82+
strict=not env.PYPY, # PR 5569
8283
)
8384
def test_cross_module_exception_translator():
8485
with pytest.raises(KeyError):

tests/test_numpy_array.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ def test_constructors():
302302
assert results["array_t<double>"].dtype == np.float64
303303

304304

305+
def test_array_object_type(doc):
306+
assert (
307+
doc(m.pass_array_object_return_as_list)
308+
== "pass_array_object_return_as_list(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.object_]) -> list"
309+
)
310+
311+
305312
def test_overload_resolution(msg):
306313
# Exact overload matches:
307314
assert m.overloaded(np.array([1], dtype="float64")) == "double"

tests/test_pytypes.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,21 @@ TEST_SUBMODULE(pytypes, m) {
10581058
// Exercises py::handle overload:
10591059
m.attr_with_type_hint<py::typing::Set<py::str>>(py::str("set_str")) = py::set();
10601060

1061+
struct foo_t {};
1062+
struct foo2 {};
1063+
struct foo3 {};
1064+
1065+
pybind11::class_<foo_t>(m, "foo");
1066+
pybind11::class_<foo2>(m, "foo2");
1067+
pybind11::class_<foo3>(m, "foo3");
1068+
m.attr_with_type_hint<foo_t>("foo") = foo_t{};
1069+
1070+
m.attr_with_type_hint<py::typing::Union<foo_t, foo2, foo3>>("foo_union") = foo_t{};
1071+
1072+
// Include to ensure this does not crash
1073+
struct foo4 {};
1074+
m.attr_with_type_hint<foo4>("foo4") = 3;
1075+
10611076
struct Empty {};
10621077
py::class_<Empty>(m, "EmptyAnnotationClass");
10631078

tests/test_pytypes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@ def test_module_attribute_types() -> None:
11571157

11581158
assert module_annotations["list_int"] == "list[typing.SupportsInt]"
11591159
assert module_annotations["set_str"] == "set[str]"
1160+
assert module_annotations["foo"] == "pybind11_tests.pytypes.foo"
1161+
assert (
1162+
module_annotations["foo_union"]
1163+
== "Union[pybind11_tests.pytypes.foo, pybind11_tests.pytypes.foo2, pybind11_tests.pytypes.foo3]"
1164+
)
11601165

11611166

11621167
@pytest.mark.skipif(

0 commit comments

Comments
 (0)