Skip to content

Cannot register a class with an abstract method. #1359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Fung-Chai opened this issue Apr 15, 2018 · 3 comments
Open

Cannot register a class with an abstract method. #1359

Fung-Chai opened this issue Apr 15, 2018 · 3 comments

Comments

@Fung-Chai
Copy link

Hi,

I am trying to register a class that has an abstract method. The idea is for clients to define the method in their subclasses. However, the registration fails.

Before I forget, I am running on Ubuntu 16.04, using pybind11 version 2.2.2, python 2.7.12, g++ version 5.4.0, and -std=c++11.

Here is what I am trying to do:

py::module abc = py::module::import("abc");
py::object abc_meta = abc.attr("ABCMeta");
py::object abstract = abc.attr("abstractmethod");

py::module mod{"example"};
py::class_<Base> base(mod, "Base", py::metaclass(abc_meta));
base.def("draw", &Base::draw, "Abstract method that subclasses must define");
base.attr("draw") = abstract(base.attr("draw"));
base.def("save", &Base::save, "Save into file");

Here, I have a class that offers the save() function. But I require clients to create a subclass and define the draw() function.

When I compile and run the code, I get the following error:

terminate called after throwing an instance of 'pybind11::error_already_set'
  what():  AttributeError: type object 'example.Base' has no attribute '_abc_cache'

At:
  /usr/lib/python2.7/abc.py(151): __subclasscheck__

Since ABCMeta is trying to add _abc_cache and other attributes to my base class, I thought adding dynamic_attr() might help. I get the same error message with the following:

py::class_<Base> base(mod, "Base", py::metaclass(abc_meta), py::dynamic_attr());

I would appreciate any help on this. Thank you.

In the attached file, I am embedding the python interpreter so that I do not need a python script to import my example module.
main.cpp.txt

@oremanj
Copy link
Contributor

oremanj commented May 1, 2018

It looks like pybind only calls the metaclass tp_alloc slot when creating a new type, not tp_new, so in your example ABCMeta.__new__ is never called and never gets a chance to create its tracking attributes. I'm not super familiar with the Python internals involved, but type_new does a lot and it's unlikely it would be remotely straightforward to just make pybind call it. And ABCMeta.__new__ calls type_new via super(), so we can't just initialize the ABC parts.

I'm not sure there's a good solution here, unfortunately, not without some substantial hacking at least.

@jnjaeschke
Copy link

+1 for any type of workaround for this.
The code in our project relies heavily on interface classes that use ABCMeta. These classes should be moved to C++ (in order to allow derived classes both in C++ and Python) while preserving the functionality of ABCMeta (especially raising a TypeError if an abstract method is not implemented). Is there any way of achieving this behavior?

@rwgk
Copy link
Collaborator

rwgk commented Feb 1, 2024

Does the info under #5015 help? (I'm honestly not sure, mainly because I don't understand what exactly the original problem was/is.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants