Skip to content

Instantly share code, notes, and snippets.

@kurtbrose
Last active November 14, 2023 22:16
Show Gist options
  • Save kurtbrose/fc488adb8011aa9a23f9e75d28f5409c to your computer and use it in GitHub Desktop.
Save kurtbrose/fc488adb8011aa9a23f9e75d28f5409c to your computer and use it in GitHub Desktop.
class decorator to inherit annotations from base-classes
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