Resolves #894
After adding support for subclassing StructMeta in #890, users could define metaclasses like:
class IntegerStructMeta(StructMeta, ABCMeta): ...
and then use them for abstract Struct bases:
class IntegerStructBase(Struct, metaclass=IntegerStructMeta): @abstractmethod def to_integer(self) -> int: ...
At runtime this exposed two related problems:
-
Original failure (user issue):
issubclass(ConcreteIntStruct, IntegerStructBase)andisinstance(obj, IntegerStructBase)crashed with:AttributeError: type object 'IntegerStructBase' has no attribute '_abc_impl'This comes from the
__instancecheck__/__subclasscheck__methods ofABCMeta, which assume the ABC machinery has been initialized (_abc_init/update_abstractmethodshave run and_abc_implis present). BecauseStructMeta_new_innerbuilt the type viaPyType_Type.tp_newwithout calling any ABC helper, the ABC state was never initialized, so the cache object_abc_implwas missing. -
Follow-up failure when trying to just call
__new__:
An attempt to delegate to the "next" metaclass in the MRO (a C equivalent ofsuper(StructMeta, mcls).__new__(...)) resulted in:TypeError: type.__new__(IntegerStructMeta) is not safe, use IntegerStructMeta.__new__()This comes from CPython's new safety check, which rejects calling a base type's
__new__when the static base has a differenttp_new. This has been deprecated since 3.12 but is now officially unsupported starting in 3.14.
So mixing StructMeta and ABCMeta gave you either a crash in ABC’s internals or a TypeError from the metaclass construction path.
Taking inspiration from that new safety check, I think it's infeasible to support mixing with arbitrary metaclasses and now we have special logic for ABCMeta.