From d0f3c51f01271b71e6228a1d002bad347dc4aca1 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Mon, 20 Sep 2021 10:42:14 -0400 Subject: [PATCH] Enable defining custom __new__ (#3265) * Enable defining custom __new__ * See if xfail needed * Qualify auto self * Unconditionally defining PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING. Returning pointer from "__init__" instead of reference. * Use new style __init__ * Simplify __new__ creation * Reviewer suggestions * Match indentation Co-authored-by: Ralf W. Grosse-Kunstleve --- include/pybind11/pybind11.h | 3 ++- tests/test_class.cpp | 15 +++++++++++++++ tests/test_class.py | 8 ++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 07d2d0436..b8f5a6bae 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -416,7 +416,8 @@ protected: detail::function_record *chain = nullptr, *chain_start = rec; if (rec->sibling) { if (PyCFunction_Check(rec->sibling.ptr())) { - auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + auto *self = PyCFunction_GET_SELF(rec->sibling.ptr()); + capsule rec_capsule = isinstance(self) ? reinterpret_borrow(self) : capsule(self); chain = (detail::function_record *) rec_capsule; /* Never append a method to an overload chain of a parent class; instead, hide the parent's overloads in this case */ diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 246b6c1fe..0b998330d 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -47,10 +47,25 @@ TEST_SUBMODULE(class_, m) { } ~NoConstructor() { print_destroyed(this); } }; + struct NoConstructorNew { + NoConstructorNew() = default; + NoConstructorNew(const NoConstructorNew &) = default; + NoConstructorNew(NoConstructorNew &&) = default; + static NoConstructorNew *new_instance() { + auto *ptr = new NoConstructorNew(); + print_created(ptr, "via new_instance"); + return ptr; + } + ~NoConstructorNew() { print_destroyed(this); } + }; py::class_(m, "NoConstructor") .def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); + py::class_(m, "NoConstructorNew") + .def(py::init([](NoConstructorNew *self) { return self; })) // Need a NOOP __init__ + .def_static("__new__", [](const py::object *) { return NoConstructorNew::new_instance(); } ); + // test_inheritance class Pet { public: diff --git a/tests/test_class.py b/tests/test_class.py index 85d453199..caafe2068 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -25,6 +25,14 @@ def test_instance(msg): assert cstats.alive() == 0 +def test_instance_new(msg): + instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__) + cstats = ConstructorStats.get(m.NoConstructorNew) + assert cstats.alive() == 1 + del instance + assert cstats.alive() == 0 + + def test_type(): assert m.check_type(1) == m.DerivedClass1 with pytest.raises(RuntimeError) as execinfo: