pybind / pybind/pybind11

[QUESTION] Defining and using metaclasses with pybind11

Open
#2,696 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

I am aware of the pybind11::metaclass option that can be passed to pybind11::class_, but I can't find any documentation on how it is supposed to be used. There is a single test case here:

https://github.com/pybind/pybind11/blob/028812ae7eee307dca5f8f69d467af7b92cc41c8/tests/test_methods_and_attributes.cpp#L282

but it isn't clear what that case is showing, and I haven't been able to find a single other example of code that uses pybind11::metaclass.

What I'd like to accomplish is to make __class_getitem__ work on Python < 3.7, equivalent to the following pure python code:

class Parent(type):

    def __getitem__(self, key):
        return self.__class_getitem__(key)


class Child(metaclass=Parent):

    @classmethod
    def __class_getitem__(cls, key):
        return 'got key: %r' % (key,)

assert Child[1] == 'got key: 1'

Note that in Python >= 3.7, this example also works if we eliminate Parent and just use type as the metaclass of Child.

I'd like to accomplish this same thing, where both Parent and Child are defined using pybind11 rather than pure Python.

My initial attempt was:

  struct Parent {};
  struct Child {};

  py::class_<Parent> cls_parent(
      m, "Parent",
      py::handle(reinterpret_cast<PyObject*>(
          pybind11::detail::get_internals().default_metaclass));
  cls_parent.def("__getitem__", [](Parent& self, py::object key) {
    return py::cast(&self).attr("__class_getitem__")(key);
  });

  py::class_<Child> cls_child(m, "Child", py::metaclass(cls_parent));
  cls_child.def_static("__class_getitem__",
                       [](std::string key) { return "got key: " + key; });

but that crashes while creating cls_parent: due to the t_size >= b_size condition in the extra_ivars function in typeobject.c.

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 tests/test_methods_and_attributes.cpp around line 282 to understand the existing metaclass coverage, then inspect the reported failure in Python's typeobject.c at the extra_ivars t_size >= b_size condition. Reproduce the metaclass construction and determine whether the supported usage or the Python <3.7 class_getitem behavior should be documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python
Domain
documentation
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.