Skip to content

Commit

Permalink
Fix bindings of classes derived from a trampoline
Browse files Browse the repository at this point in the history
Binding of deeper (>= 2 levels) class hierarchies that inherit from
a trampoline class fail in Python 3.12+. For example, in the added test
suite class ``SiameseCat``, this produces the following error message:

Critical nanobind error: nanobind::detail::nb_type_new("SiameseCat"): type construction failed: TypeError: tp_basicsize for type 'test_classes_ext.SiameseCat' (56) is too small for base 'test_classes_ext.Cat' (88)!

The trick to make Python happy is to carefully walk through the parent
classes to look for trampolines. Previously, only the direct parent was
checked.
  • Loading branch information
wjakob committed Jan 27, 2025
1 parent d5b05b7 commit 92d9cb3
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 5 deletions.
27 changes: 22 additions & 5 deletions src/nb_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1206,11 +1206,28 @@ PyObject *nb_type_new(const type_init_data *t) noexcept {

/* Handle a corner case (base class larger than derived class)
which can arise when extending trampoline base classes */
size_t base_basicsize = sizeof(nb_inst) + tb->size;
if (tb->align > ptr_size)
base_basicsize += tb->align - ptr_size;
if (base_basicsize > basicsize)
basicsize = base_basicsize;

PyTypeObject *base_2 = (PyTypeObject *) base;
type_data *tb_2 = tb;

do {
size_t base_basicsize = sizeof(nb_inst) + tb_2->size;
if (tb_2->align > ptr_size)
base_basicsize += tb_2->align - ptr_size;
if (base_basicsize > basicsize)
basicsize = base_basicsize;

#if !defined(Py_LIMITED_API)
base_2 = base_2->tp_base;
#else
base_2 = (PyTypeObject *) PyType_GetSlot(base_2, Py_tp_base);
#endif

if (!base_2 || !nb_type_check((PyObject *) base_2))
break;

tb_2 = nb_type_data(base_2);
} while (true);
}

bool base_intrusive_ptr =
Expand Down
5 changes: 5 additions & 0 deletions tests/test_classes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ NB_MODULE(test_classes_ext, m) {
std::string s;
};

struct SiameseCat : Cat { };

struct Foo { };

auto animal = nb::class_<Animal, PyAnimal>(m, "Animal")
Expand All @@ -274,6 +276,9 @@ NB_MODULE(test_classes_ext, m) {
nb::class_<Cat>(m, "Cat", animal)
.def(nb::init<const std::string &>());

nb::class_<SiameseCat, Cat> sc(m, "SiameseCat");
(void) sc;

m.def("go", [](Animal *a) {
return a->name() + " says " + a->what();
});
Expand Down
3 changes: 3 additions & 0 deletions tests/test_classes_ext.pyi.ref
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ class PolymorphicBase:
class PolymorphicSubclass:
pass

class SiameseCat(Cat):
pass

class StaticProperties:
value: int = ...
"""Static property docstring"""
Expand Down

0 comments on commit 92d9cb3

Please sign in to comment.