fromcollections.abcimportIteratorfromdataclassesimportdataclassfromtypingimportTypeVarfromapischemaimportdeserialize,deserializerfromapischema.conversionsimportConversionFoo_=TypeVar("Foo_",bound="Foo")# Use a dataclass in order to be easily testable with ==@dataclassclassFoo:value:int@classmethoddefdeserialize(cls:type[Foo_],value:int)->Foo_:returncls(value)def__init_subclass__(cls,**kwargs):super().__init_subclass__(**kwargs)# Register subclasses' conversion in __init_subclass__deserializer(Conversion(cls.deserialize,target=cls))# Register main conversion after the class definitiondeserializer(Conversion(Foo.deserialize,target=Foo))classBar(Foo):passassertdeserialize(Foo,0)==Foo(0)assertdeserialize(Bar,0)==Bar(0)# For external types (defines in imported library)@dataclassclassForeignType:value:intclassForeignSubtype(ForeignType):passT=TypeVar("T")# Recursive implementation of type.__subclasses__defrec_subclasses(cls:type[T])->Iterator[type[T]]:forsub_clsincls.__subclasses__():yieldsub_clsyield fromrec_subclasses(sub_cls)# Register deserializers for all subclassesforclsin(ForeignType,*rec_subclasses(ForeignType)):# cls=cls is an lambda idiom to capture variable by value inside loopdeserializer(Conversion(lambdavalue,cls=cls:cls(value),source=int,target=cls))assertdeserialize(ForeignType,0)==ForeignType(0)assertdeserialize(ForeignSubtype,0)==ForeignSubtype(0)