Last active
November 14, 2023 22:16
-
-
Save kurtbrose/fc488adb8011aa9a23f9e75d28f5409c to your computer and use it in GitHub Desktop.
class decorator to inherit annotations from base-classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def inherit_annotations(cls): | |
""" | |
Inherit annotations from all base classes according to method-resolution-order. | |
This is the same way that type checkers will interpret annotations. | |
This allows for other class decorators such as attr.define() or dataclasses.dataclass() | |
to see the inherited annotations from plain-vanilla python classes. This, in turn, | |
allows base classes defining common fields to be shared among different class-decorator-annotation | |
libraries. | |
""" | |
annotations = {} | |
for base in cls.__mro__[::-1]: # go in reverse order so "closer" updates come later and override | |
if not hasattr(base, "__dict__"): | |
continue | |
if "__annotations__" not in base.__dict__: | |
continue # in python 3.9 and earlier we might unintentionally get parent classes annotations | |
annotations.update(base.__annotations__) | |
if "__annotations__" not in cls.__dict__: | |
cls.__annotations__ = {} # ensure __annotations__ exists for 3.9 and earlier | |
cls.__annotations__.update(annotations) | |
return cls | |
def test(): | |
from dataclasses import dataclass | |
class Base: | |
a: int | |
b: int = 3 | |
@dataclass | |
@inherit_annotations | |
class Derived(Base): | |
pass | |
assert Derived.__annotations__ == Base.__annotations__ | |
assert Derived(a=1).b == 3 | |
class A: | |
a: int | |
class B(A): | |
a: str | |
assert B.__annotations__['a'] is str | |
if __name__ == "__main__": | |
test() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment