Skip to content

Commit c72708a

Browse files
Moving tp_class access, and consistent fully-qualified naming for PyPy, to detail::get_tp_name (#2520)
* Moving tp_class access, and consistent fully-qualified naming for PyPy, to detail::get_tp_name * Change get_tp_name to get_fully_qualified_tp_name
1 parent 3232e59 commit c72708a

File tree

5 files changed

+25
-23
lines changed

5 files changed

+25
-23
lines changed

include/pybind11/cast.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
4040
PYBIND11_NAMESPACE_BEGIN(detail)
4141

42+
// Forward-declaration; see detail/class.h
43+
std::string get_fully_qualified_tp_name(PyTypeObject*);
44+
4245
/// A life support system for temporary objects created by `type_caster::load()`.
4346
/// Adding a patient will keep it alive up until the enclosing function returns.
4447
class loader_life_support {
@@ -342,8 +345,8 @@ PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const t
342345
"(compile in debug mode for type details)");
343346
#else
344347
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" +
345-
std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" +
346-
std::string(Py_TYPE(this)->tp_name) + "' instance");
348+
get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" +
349+
get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
347350
#endif
348351
}
349352

include/pybind11/detail/class.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ PYBIND11_NAMESPACE_BEGIN(detail)
2424
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj)
2525
#endif
2626

27+
inline std::string get_fully_qualified_tp_name(PyTypeObject *type) {
28+
#if !defined(PYPY_VERSION)
29+
return type->tp_name;
30+
#else
31+
return handle((PyObject *) type).attr("__module__").cast<std::string>() + "." + type->tp_name;
32+
#endif
33+
}
34+
2735
inline PyTypeObject *type_incref(PyTypeObject *type) {
2836
Py_INCREF(type);
2937
return type;
@@ -172,7 +180,7 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
172180
for (const auto &vh : values_and_holders(instance)) {
173181
if (!vh.holder_constructed()) {
174182
PyErr_Format(PyExc_TypeError, "%.200s.__init__() must be called when overriding __init__",
175-
vh.type->type->tp_name);
183+
get_fully_qualified_tp_name(vh.type->type).c_str());
176184
Py_DECREF(self);
177185
return nullptr;
178186
}
@@ -304,12 +312,7 @@ extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *,
304312
/// following default function will be used which simply throws an exception.
305313
extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) {
306314
PyTypeObject *type = Py_TYPE(self);
307-
std::string msg;
308-
#if defined(PYPY_VERSION)
309-
msg += handle((PyObject *) type).attr("__module__").cast<std::string>() + ".";
310-
#endif
311-
msg += type->tp_name;
312-
msg += ": No constructor defined!";
315+
std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!";
313316
PyErr_SetString(PyExc_TypeError, msg.c_str());
314317
return -1;
315318
}
@@ -448,7 +451,7 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
448451
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
449452
if (!PyDict_Check(new_dict)) {
450453
PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'",
451-
Py_TYPE(new_dict)->tp_name);
454+
get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
452455
return -1;
453456
}
454457
PyObject *&dict = *_PyObject_GetDictPtr(self);
@@ -476,9 +479,8 @@ extern "C" inline int pybind11_clear(PyObject *self) {
476479
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
477480
auto type = &heap_type->ht_type;
478481
#if defined(PYPY_VERSION) && (PYPY_VERSION_NUM < 0x06000000)
479-
pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are "
480-
"currently not supported in "
481-
"conjunction with PyPy!");
482+
pybind11_fail(get_fully_qualified_tp_name(type) + ": dynamic attributes are currently not "
483+
"supported in conjunction with PyPy!");
482484
#endif
483485
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
484486
type->tp_dictoffset = type->tp_basicsize; // place dict at the end

include/pybind11/pybind11.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ class cpp_function : public function {
242242

243243
#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
244244
if (rec->is_constructor && !rec->is_new_style_constructor) {
245-
const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name);
245+
const auto class_name = detail::get_fully_qualified_tp_name((PyTypeObject *) rec->scope.ptr());
246246
const auto func_name = std::string(rec->name);
247247
PyErr_WarnEx(
248248
PyExc_FutureWarning,
@@ -1039,7 +1039,7 @@ class generic_type : public object {
10391039
if (!type->ht_type.tp_as_buffer)
10401040
pybind11_fail(
10411041
"To be able to register buffer protocol support for the type '" +
1042-
std::string(tinfo->type->tp_name) +
1042+
get_fully_qualified_tp_name(tinfo->type) +
10431043
"' the associated class<>(..) invocation must "
10441044
"include the pybind11::buffer_protocol() annotation!");
10451045

tests/test_class.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,8 @@ def __init__(self):
152152
pass
153153
with pytest.raises(TypeError) as exc_info:
154154
Python()
155-
expected = ["m.class_.Pet.__init__() must be called when overriding __init__",
156-
"Pet.__init__() must be called when overriding __init__"] # PyPy?
157-
# TODO: fix PyPy error message wrt. tp_name/__qualname__?
158-
assert msg(exc_info.value) in expected
155+
expected = "m.class_.Pet.__init__() must be called when overriding __init__"
156+
assert msg(exc_info.value) == expected
159157

160158
# Multiple bases
161159
class RabbitHamster(m.Rabbit, m.Hamster):
@@ -164,9 +162,8 @@ def __init__(self):
164162

165163
with pytest.raises(TypeError) as exc_info:
166164
RabbitHamster()
167-
expected = ["m.class_.Hamster.__init__() must be called when overriding __init__",
168-
"Hamster.__init__() must be called when overriding __init__"] # PyPy
169-
assert msg(exc_info.value) in expected
165+
expected = "m.class_.Hamster.__init__() must be called when overriding __init__"
166+
assert msg(exc_info.value) == expected
170167

171168

172169
def test_automatic_upcasting():

tests/test_local_bindings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def test_internal_locals_differ():
155155
assert m.local_cpp_types_addr() != cm.local_cpp_types_addr()
156156

157157

158-
@pytest.mark.xfail("env.PYPY")
158+
@pytest.mark.xfail("env.PYPY and sys.pypy_version_info < (7, 3, 2)")
159159
def test_stl_caster_vs_stl_bind(msg):
160160
"""One module uses a generic vector caster from `<pybind11/stl.h>` while the other
161161
exports `std::vector<int>` via `py:bind_vector` and `py::module_local`"""

0 commit comments

Comments
 (0)