Skip to content

Commit 7421420

Browse files
committed
Check scope's __dict__ instead of using hasattr when registering classes and exceptions, to allow registering the same name in a derived class scope
1 parent e8ad33b commit 7421420

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

include/pybind11/pybind11.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ class generic_type : public object {
10001000
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
10011001
protected:
10021002
void initialize(const type_record &rec) {
1003-
if (rec.scope && hasattr(rec.scope, rec.name))
1003+
if (rec.scope && hasattr(rec.scope, "__dict__") && rec.scope.attr("__dict__").contains(rec.name))
10041004
pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) +
10051005
"\": an object with that name is already defined");
10061006

@@ -1918,7 +1918,7 @@ class exception : public object {
19181918
std::string full_name = scope.attr("__name__").cast<std::string>() +
19191919
std::string(".") + name;
19201920
m_ptr = PyErr_NewException(const_cast<char *>(full_name.c_str()), base.ptr(), NULL);
1921-
if (hasattr(scope, name))
1921+
if (hasattr(scope, "__dict__") && scope.attr("__dict__").contains(name))
19221922
pybind11_fail("Error during initialization: multiple incompatible "
19231923
"definitions with name \"" + std::string(name) + "\"");
19241924
scope.attr(name) = *this;

tests/test_class.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ TEST_SUBMODULE(class_, m) {
410410
struct IsNonFinalFinal {};
411411
py::class_<IsNonFinalFinal>(m, "IsNonFinalFinal", py::is_final());
412412

413+
// test_exception_rvalue_abort
413414
struct PyPrintDestructor {
414415
PyPrintDestructor() = default;
415416
~PyPrintDestructor() {
@@ -421,6 +422,7 @@ TEST_SUBMODULE(class_, m) {
421422
.def(py::init<>())
422423
.def("throw_something", &PyPrintDestructor::throw_something);
423424

425+
// test_multiple_instances_with_same_pointer
424426
struct SamePointer {};
425427
static SamePointer samePointer;
426428
py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer")
@@ -430,6 +432,20 @@ TEST_SUBMODULE(class_, m) {
430432
struct Empty {};
431433
py::class_<Empty>(m, "Empty")
432434
.def(py::init<>());
435+
436+
// test_base_and_derived_nested_scope
437+
struct BaseWithNested {
438+
struct Nested {};
439+
};
440+
441+
struct DerivedWithNested : BaseWithNested {
442+
struct Nested {};
443+
};
444+
445+
py::class_<BaseWithNested> baseWithNested_class(m, "BaseWithNested");
446+
py::class_<DerivedWithNested, BaseWithNested> derivedWithNested_class(m, "DerivedWithNested");
447+
py::class_<BaseWithNested::Nested>(baseWithNested_class, "Nested");
448+
py::class_<DerivedWithNested::Nested>(derivedWithNested_class, "Nested");
433449
}
434450

435451
template <int N> class BreaksBase { public:

tests/test_class.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,9 @@ def test_multiple_instances_with_same_pointer(capture):
383383
# No assert: if this does not trigger the error
384384
# pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
385385
# and just completes without crashing, we're good.
386+
387+
388+
# https://github.com/pybind/pybind11/issues/1624
389+
def test_base_and_derived_nested_scope():
390+
assert issubclass(m.DerivedWithNested, m.BaseWithNested)
391+
assert m.DerivedWithNested.Nested != m.BaseWithNested.Nested

0 commit comments

Comments
 (0)