generics
Utilities for generic type introspection and resolution.
This module wraps the Python typing machinery (and some Pydantic internals) to make it easy to:
inspect the parameters declared on a generic class or alias,
examine how those parameters are specialised on subclasses and aliases,
follow argument bindings through entire inheritance hierarchies, and
expose reusable descriptors that resolve type arguments on demand.
These helper methods raise GenericsError
when the caller provides
inputs that make resolution impossible (for example, asking for a parameter
that was never declared, or querying a non-generic class).
Repeated lookups are cached so the helpers remain inexpensive even when used repeatedly.
Examples
1. Inspect concrete types of generic type parameters
Parameters declared by a generic class or alias can be inspected using
get_parameter_infos()
:>>> class Parent[T]: ... pass >>> class Child(Parent[list[int]]): ... pass >>> from app.util.helpers import generics >>> generics.get_concrete_parent_argument(Child, Parent, "T") list[int]If you rely on forward references, note that this library might not be able to resolve them:
>>> class SomeClass(Parent["AnotherClass"]): ... >>> generics.get_concrete_parent_argument(SomeClass, Parent, "T", bound=False) Traceback (most recent call last): ... GenericsError: Could not resolve ForwardRef('AnotherClass') to a concrete type. Consider using `register_forwardref_type`.In order to automatically resolve forward references, you may register types with the library:
>>> def another_scope(): ... class AnotherClass: ... ... ... generics.register_type(AnotherClass)Now the library can resolve the forward reference:
>>> another_scope() >>> generics.get_concrete_parent_argument(SomeClass, Parent, "T") <class '...AnotherClass'>
2. Class descriptors for generic type parameter introspection
Classes can expose reusable introspection class methods by using
GenericIntrospectionMethod
:>>> from app.util.helpers import generics >>> class Repository[T]: ... item_type = generics.GenericIntrospectionMethod[T]() ... item_type_origin = generics.GenericIntrospectionMethod[T](origin=True) >>> class StrRepository(Repository[str]): ... pass >>> StrRepository.item_type() <class 'str'> >>> class IntListRepository(Repository[list[int]]): ... pass >>> IntListRepository.item_type() list[int] >>> IntListRepository.item_type_origin() <class 'list'>Instances can also invoke the descriptor directly:
>>> IntListRepository().item_type() list[int]The descriptor enforces type argument bounds if present:
>>> class Animal: ... >>> class Dog(Animal): ... >>> class AnimalRepository[T: Animal](Repository[T]): ... animal_type = generics.GenericIntrospectionMethod[T]() >>> AnimalRepository.animal_type() <class 'app.util.helpers.generics.Animal'> >>> class IntRepository(AnimalRepository[int]): ... >>> IntRepository.animal_type() Traceback (most recent call last): ... GenericsError: IntRepository.T type argument <class 'int'> is not a subclass of <class '__main__.Animal'>This can be turned off by passing bound=False to the descriptor instantiation or the method call:
>>> IntRepository.animal_type(bound=False) <class 'int'>You can also pass a
source
alias to cover cases wherecls
has lost its generic specialisation:>>> alias = Repository[int] >>> Repository.item_type(source=alias) <class 'int'>
See test/util/helpers/test_generic_introspection_method.py
for more involved
scenarios.
Functions
|
Return a human-friendly representation of bound. |
|
|
|
Return the raw type argument bound to param for cls. |
|
Return the |
|
Return the |
|
Return a cached mapping of parameter names to |
|
Return the inheritance chain between cls and parent, inclusive. |
|
Return the inheritance chain between cls and parent, inclusive. |
|
Return the concrete origin type bound to param for cls. |
|
Return the concrete argument resolved for param from parent within cls. |
|
Return the origin type for the concrete argument resolved from parent. |
|
Return the generic base class for cls. |
Return the generic base class for cls if one exists. |
|
|
Return the typing origin for cls or |
|
Return the typing origin for cls or |
|
Return the tuple of original bases for cls, resolving aliases. |
|
Resolve name_or_typevar to a |
|
Return a specific |
|
Return a mapping of parameter names to |
|
Return the value or bound resolved for param on parent within cls. |
|
Return the resolved |
|
Return the final |
|
Return the cached sequence of argument bindings from cls to parent. |
|
Return the value or bound resolved for param on parent within cls if any. |
|
Return |
|
Return whether cls is a generic class. |
|
Return whether cls resolves to a Pydantic |
|
Yield |
|
Yield |
|
Yield argument bindings for param across the inheritance chain to parent. |
|
Add a named class to the generics types namespace. |
|
Remove a named class from the generics types namespace. |
Classes
Metadata describing the argument bound to a |
|
Descriptor that resolves generic parent arguments on demand. |
|
Typed keyword arguments accepted by |
|
Typed keyword arguments accepted by |
|
Metadata describing a generic type parameter for a class or alias. |
Exceptions
Signals incorrect generic usage outside the helpers' control. |