Extend PyType to create metaclasses
#4621
Closed
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_typeis 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
typeusing 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.
PyTypeObjectis aPyVarObjectand so cannot implement thePySizedLayouttrait 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_TypeisPyHeapTypeObjectmeaning 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_Typedefined in_ctypes.cin the cpython source code) uses this mechanismFor this approach
basicsizeis set to a negative number equal to the size of the storage space required just for thederived class (
StfInfoin 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 ofMyMetaclassTyperesults inbecause without that line the base becomes
object.