Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/nb_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,33 @@ static int nb_type_init(PyObject *self, PyObject *args, PyObject *kwds) {
return 0;
}

/// metaclass `__call__` function that is used to create all nanobind objects
static PyObject *nb_type_call(PyObject *type, PyObject *args, PyObject *kwargs) {

// use the default metaclass call to create/initialize the object
#if defined(Py_LIMITED_API)
PyObject *self = ((ternaryfunc)PyType_GetSlot(&PyType_Type, Py_tp_call))(type, args, kwargs);
#else
PyObject *self = PyType_Type.tp_call(type, args, kwargs);
#endif
if (self == nullptr) {
return nullptr;
}

// This must be a nanobind instance
nb_inst *inst = (nb_inst *) self;
if (inst->state == nb_inst::state_uninitialized) {
const type_data *t = nb_type_data(Py_TYPE(self));
PyErr_Format(PyExc_TypeError,
"%.200s.__init__() must be called when overriding __init__",
t->name);
Py_DECREF(self);
return nullptr;
}

return self;
}

/// Special case to handle 'Class.property = value' assignments
int nb_type_setattro(PyObject* obj, PyObject* name, PyObject* value) {
nb_internals *int_p = internals;
Expand Down Expand Up @@ -861,6 +888,7 @@ static PyTypeObject *nb_type_tp(size_t supplement) noexcept {
{ Py_tp_dealloc, (void *) nb_type_dealloc },
{ Py_tp_setattro, (void *) nb_type_setattro },
{ Py_tp_init, (void *) nb_type_init },
{ Py_tp_call, (void *) nb_type_call },
#if defined(Py_LIMITED_API)
{ Py_tp_members, (void *) members },
#endif
Expand Down
10 changes: 3 additions & 7 deletions tests/test_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,9 @@ def name(self):
def what(self):
return "b"

with pytest.warns(
RuntimeWarning,
match="nanobind: attempted to access an uninitialized instance of type",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any other test that checks for this string, but the place where the documentation discusses it is talking about a unique_ptr ownership transfer, which isn't this?

):
with pytest.raises(TypeError) as excinfo:
t.go(Incomplete2())
assert "incompatible function arguments" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
Incomplete2()
assert "__init__() must be called when overriding __init__" in str(excinfo.value)


def test12_large_pointers():
Expand Down
Loading