pybind / pybind/pybind11

[BUG]: __getattr__ and base class order

Open
#3,804 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

docs
Dominant language
C++
Stars
18k
Forks
2.3k
Avg merge
5d 17h
Merged PRs (30d)
10

Description

Required prerequisites
Problem description

In the binding code below __getattr__ will only be found in the derived class if we do:
py::class_<Derived, Base, OtherBase>(m, "Derived")
if we instead do:
py::class_<Derived, OtherBase, Base>(m, "Derived")
note the order of the base classes switched,
the bound __getattr__ will never be called.

From what I understand from the documentation (https://pybind11.readthedocs.io/en/latest/advanced/classes.html#multiple-inheritance) the order of the base classes should not matter?

This is tested with the latest master as of writing.

Reproducible example code
// binding code
#include <pybind11/pybind11.h>

namespace py = pybind11;

struct Base {
    virtual ~Base() = default;
    int base() const { return 0; }
};
struct OtherBase {
    int other() const { return 3; }
};
struct Derived : Base, OtherBase{
    int id() const { return 2; }
};

PYBIND11_MODULE(bar, m) {
    py::class_<Base>(m, "Base")
        .def(py::init<>())
        .def("base", &Base::base)
        .def("__getattr__", [](Base&, std::string key) {
            return "Base GetAttr: " + key;
        });

    py::class_<OtherBase>(m, "OtherBase")
        .def("other", &OtherBase::other);

    py::class_<Derived, OtherBase, Base>(m, "Derived")
        .def(py::init<>())
        .def("id", &Derived::id);
}

// test code
import bar

d = bar.Derived()
print(f"d.base(): {d.base()}")
print(f"d.other(): {d.other()}")
print(f"d.id(): {d.id()}")
print(f"d.prop: {d.prop}") # this will fail (if Base is not the first base class)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided C++ binding and Python reproduction, and read the linked pybind11 multiple-inheritance documentation first. Confirm behavior for both base-class orders and trace the relevant class-binding and attribute-lookup entry point; done means getattr behaves consistently regardless of base order, with regression coverage for the reproduction.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.