Module pedantic.mixins.generic_mixin
+Classes
+-
+
+class GenericMixin +
+-
+++
+Expand source code +
+
+class GenericMixin: + """ + A mixin that provides easy access to given type variables. + + Example: + >>> from typing import Generic, TypeVar + >>> T = TypeVar('T') + >>> U = TypeVar('U') + >>> class Foo(Generic[T, U], GenericMixin): + ... values: list[T] + ... value: U + >>> f = Foo[str, int]() + >>> f.type_vars + {~T: <class 'str'>, ~U: <class 'int'>} + """ + + @property + def type_var(self) -> Type: + """ + Get the type variable for this class. + Use this for convenience if your class has only one type parameter. + + DO NOT call this inside __init__()! + + Example: + >>> from typing import Generic, TypeVar + >>> T = TypeVar('T') + >>> class Foo(Generic[T], GenericMixin): + ... value: T + >>> f = Foo[float]() + >>> f.type_var + <class 'float'> + """ + + types = self._get_resolved_typevars() + assert len(types) == 1, f'You have multiple type parameters. Please use "type_vars" instead of "type_var".' + return list(types.values())[0] # type: ignore + + @property + def type_vars(self) -> Dict[TypeVar, Type]: + """ + Returns the mapping of type variables to types. + + DO NOT call this inside __init__()! + + Example: + >>> from typing import Generic, TypeVar + >>> T = TypeVar('T') + >>> U = TypeVar('U') + >>> class Foo(Generic[T, U], GenericMixin): + ... values: list[T] + ... value: U + >>> f = Foo[str, int]() + >>> f.type_vars + {~T: <class 'str'>, ~U: <class 'int'>} + """ + + return self._get_resolved_typevars() + + def _get_resolved_typevars(self) -> Dict[TypeVar, Type]: + """ + Do not call this inside the __init__() method, because at that point the relevant information are not present. + See also https://github.com/python/cpython/issues/90899' + """ + + mapping: dict[TypeVar, type] = {} + + if not hasattr(self, '__orig_bases__'): + raise AssertionError( + f'{self.class_name} is not a generic class. To make it generic, declare it like: ' + f'class {self.class_name}(Generic[T], GenericMixin):...' + ) + + def collect(base, substitutions: dict[TypeVar, type]): + """Recursively collect type var mappings from a generic base.""" + origin = get_origin(base) or base + args = get_args(base) + + params = getattr(origin, '__parameters__', ()) + # copy substitutions so each recursion has its own view + resolved = substitutions.copy() + + for param, arg in zip(params, args): + if isinstance(arg, TypeVar): + arg = substitutions.get(arg, arg) + mapping[param] = arg + resolved[param] = arg + + # Recurse into base classes, applying current substitutions + for super_base in getattr(origin, '__orig_bases__', []): + super_origin = get_origin(super_base) or super_base + super_args = get_args(super_base) + + if super_args: + # Substitute any TypeVars in the super_base's args using resolved + substituted_args = tuple( + resolved.get(a, a) if isinstance(a, TypeVar) else a + for a in super_args + ) + # Build a new parametrized base so get_args() inside collect sees substituted_args + try: + substituted_base = super_origin[substituted_args] # type: ignore[index] + except TypeError: + # Some origins won't accept subscription; fall back to passing the origin and trusting resolved + substituted_base = super_base + collect(base=substituted_base, substitutions=resolved) + else: + collect(base=super_base, substitutions=resolved) + + # Start from __orig_class__ if present, else walk the declared MRO bases + cls = getattr(self, '__orig_class__', None) + if cls is not None: + collect(base=cls, substitutions={}) + else: + # Walk the full MRO to catch indirect generic ancestors + for c in self.__class__.__mro__: + for base in getattr(c, '__orig_bases__', []): + collect(base=base, substitutions=mapping) + + # Ensure no unresolved TypeVars remain + all_params = set() + for c in self.__class__.__mro__: + all_params.update(getattr(c, '__parameters__', ())) + + unresolved = {p for p in all_params if p not in mapping or isinstance(mapping[p], TypeVar)} + if unresolved: + raise AssertionError( + f'You need to instantiate this class with type parameters! Example: {self.class_name}[int]()\n' + f'Also make sure that you do not call this in the __init__() method of your class!\n' + f'Unresolved type variables: {unresolved}\n' + f'See also https://github.com/python/cpython/issues/90899' + ) + + return mapping + @property + def class_name(self) -> str: + """ Get the name of the class of this instance. """ + + return type(self).__name__+A mixin that provides easy access to given type variables.
+Example
+>>> from typing import Generic, TypeVar +>>> T = TypeVar('T') +>>> U = TypeVar('U') +>>> class Foo(Generic[T, U], GenericMixin): +... values: list[T] +... value: U +>>> f = Foo[str, int]() +>>> f.type_vars +{~T: <class 'str'>, ~U: <class 'int'>} +Subclasses
+ +Instance variables
+-
+
prop class_name : str
+-
+++
+Expand source code +
+
+@property +def class_name(self) -> str: + """ Get the name of the class of this instance. """ + + return type(self).__name__+Get the name of the class of this instance.
+ prop type_var : Type
+-
+++
+Expand source code +
+
+@property +def type_var(self) -> Type: + """ + Get the type variable for this class. + Use this for convenience if your class has only one type parameter. + + DO NOT call this inside __init__()! + + Example: + >>> from typing import Generic, TypeVar + >>> T = TypeVar('T') + >>> class Foo(Generic[T], GenericMixin): + ... value: T + >>> f = Foo[float]() + >>> f.type_var + <class 'float'> + """ + + types = self._get_resolved_typevars() + assert len(types) == 1, f'You have multiple type parameters. Please use "type_vars" instead of "type_var".' + return list(types.values())[0] # type: ignore+Get the type variable for this class. +Use this for convenience if your class has only one type parameter.
+DO NOT call this inside init()!
+Example
+>>> from typing import Generic, TypeVar +>>> T = TypeVar('T') +>>> class Foo(Generic[T], GenericMixin): +... value: T +>>> f = Foo[float]() +>>> f.type_var +<class 'float'> +
+ prop type_vars : Dict[TypeVar, Type]
+-
+++
+Expand source code +
+
+@property +def type_vars(self) -> Dict[TypeVar, Type]: + """ + Returns the mapping of type variables to types. + + DO NOT call this inside __init__()! + + Example: + >>> from typing import Generic, TypeVar + >>> T = TypeVar('T') + >>> U = TypeVar('U') + >>> class Foo(Generic[T, U], GenericMixin): + ... values: list[T] + ... value: U + >>> f = Foo[str, int]() + >>> f.type_vars + {~T: <class 'str'>, ~U: <class 'int'>} + """ + + return self._get_resolved_typevars()+Returns the mapping of type variables to types.
+DO NOT call this inside init()!
+Example
+>>> from typing import Generic, TypeVar +>>> T = TypeVar('T') +>>> U = TypeVar('U') +>>> class Foo(Generic[T, U], GenericMixin): +... values: list[T] +... value: U +>>> f = Foo[str, int]() +>>> f.type_vars +{~T: <class 'str'>, ~U: <class 'int'>} +
+
+