Extend PyType
to create metaclasses
#4621
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I am investigating adding support for metaclasses in PyO3 (#906), mainly as a way to implement proper enums or a close equivalent (#2887).
As a first step I created a metaclass in c. That example can be found in this comment. I then tried to recreate the example in PyO3. I found that the first issue is the missing ability to inherit from
type
.This PR allows metaclasses (classes that extend
type
) to be created from Pyo3 using#[pyclass(extends=PyType)]
and used in python (but not used with other Pyo3 classes yet).I'm not sure if
PyType::new_type
is required or the correct way to go about callingtype(name, bases, namespace)
from the PyO3 metaclass. There are probably also lifetime issues with the current implementation. guidance here would be appreciated.Background: Creating a metaclass in c
Initially I wasn't sure how to correctly inherit from
type
using the c api since there are no examples I could find online. By pattern matching with similar examples I ended up with:But this has a problem.
PyTypeObject
is aPyVarObject
and so cannot implement thePySizedLayout
trait necessary for using it as a base in Pyo3. After some searching I found PEP-0697 which was very useful because it explains that the actual base ofPyType_Type
isPyHeapTypeObject
meaning that the example should have been:The PEP also explains a mechanism introduced in python 3.12 for supporting extending opaque or variable sized base classes in extension modules. The one example of an extension module metaclass that I could find (the ctypes metaclass
_ctypes.CType_Type
defined in_ctypes.c
in the cpython source code) uses this mechanismFor this approach
basicsize
is set to a negative number equal to the size of the storage space required just for thederived class (
StfInfo
in the example above). A pointer to that memory is accessed usingPyObject_GetTypeData()
.If Pyo3 supported this then it would be possible to extend builtins (including
type
) using the limited API (for python >=3.12).Background: Why is inheriting type required
In python the following does not crash, but also does not work correctly as a metaclass:
And in the c example removing the
.tp_base = &PyType_Type,
line from the definition ofMyMetaclassType
results inbecause without that line the base becomes
object
.