I am trying to define a class that is supposed to simultaneously do two things:
- serve as the metaclass for a dataclass
- act like a mapping
i.e., it will need to be derived from both type and typing.Mapping.
Defining such a class itself works, but I have encountered two different (but probably related) problems when actually trying to use it:
- TypeError when using it for its intended purpose as the metaclass of any dataclass (see error 1 below for details)
- TypeError as soon as an unrelated other class tries to register itself as a virtual subclass of Mapping (see error 2 below for details)
Minimum non-working example:
import dataclasses, typing
class MyDataclassMeta(type, typing.Mapping):
# can imagine that the abstract methods of Mapping are implemented here (not relevant for the error)
pass
# # either uncomment this class to get error 1
# @dataclasses.dataclass
# class MyDataclass(metaclass=MyDataclassMeta):
# pass
# # or uncomment this class to get error 2
# @typing.Mapping.register
# class CompletelyUnrelatedClass:
# pass
if __name__ == "__main__":
pass
Traceback for error 1 (i.e., trying to use MyDataclassMeta as metaclass for any dataclass):
Traceback (most recent call last):
File "foo.py", line 10, in <module>
class MyDataclass(metaclass=MyDataclassMeta):
File "D:\Python38\lib\dataclasses.py", line 1019, in dataclass
return wrap(cls)
File "D:\Python38\lib\dataclasses.py", line 1011, in wrap
return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen)
File "D:\Python38\lib\dataclasses.py", line 991, in _process_class
str(inspect.signature(cls)).replace(' -> None', ''))
File "D:\Python38\lib\inspect.py", line 3105, in signature
return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
File "D:\Python38\lib\inspect.py", line 2854, in from_callable
return _signature_from_callable(obj, sigcls=cls,
File "D:\Python38\lib\inspect.py", line 2307, in _signature_from_callable
if _signature_is_builtin(obj):
File "D:\Python38\lib\inspect.py", line 1847, in _signature_is_builtin
obj in (type, object))
File "D:\Python38\lib\_collections_abc.py", line 685, in __eq__
if not isinstance(other, Mapping):
File "D:\Python38\lib\abc.py", line 98, in __instancecheck__
return _abc_instancecheck(cls, instance)
File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
TypeError: descriptor '__subclasses__' of 'type' object needs an argument
Traceback for error 2 (i.e., registering any class as virtual subclass of Mapping):
Traceback (most recent call last):
File "foo.py", line 15, in <module>
class CompletelyUnrelatedClass:
File "D:\Python38\lib\abc.py", line 94, in register
return _abc_register(cls, subclass)
File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
TypeError: descriptor '__subclasses__' of 'type' object needs an argument
Tried to define __subclasshook__ or __subclasscheck__, but I am not familiar enough with them to know how a proper implementation should look like, or whether that can even help with the problems.
As I stated above, the entry
typing.Mappingis a tool used for static type checking alone, and is of no use when the program is executed. So much that it is deprecated - sincecollections.abc.Mappingcan actually fit that role at the same time that it provides useful functionality for the actual execution of a program.That said,
collections.abc.Mappingis not designed to be used by a class that is itself a metaclass. It probably can be made to work, but not out of the box, and with knowledge of what is being done. just for starts, it would imply in the use of aabc.ABCMetaas a "meta meta class" - that is, it would police the instantiation of new classes themselves (because new classes are the instances of a metaclass) - I am not sure even if that would work.The fact, and easier thing, is that a class do not need to inherit from collections.abc.Mapping to work as a mapping - it just has to implement the relevant special methods so that it acts as desired.
You don't post an example of the actual usage you want to make of your special class - you just get the errors of trying to declare the class - so I can't help you with a concrete example of what you'd need.
You just need to implement in your class the
__getitem__,__setitem__,__delitem__and__len__methods, along with suitable implementations ofget,setdefault,keys,values,items,__iter__: these are all optional, and will depend of what use you intend to do of the mapping capabilities of your class. (It might even work with only__getitem__depending of what you want).Them if you need typecheking of the classes using your metaclass, you can use the register call of
collections.abc.Mappingitself: that will work out of the box:Otherwise, just post (possibly in other questions) what do you actually want to achieve - i.e.: how do you intend to use this class, so that some working code can be given as an example.