classGeneric(metaclass=GenericMeta): """Abstract base class for generic types. A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as:: class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc. This class can then be used as follows:: def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default """
__slots__ = ()
def__new__(cls, *args, **kwds): if cls._gorg isGeneric: raise TypeError("Type Generic cannot be instantiated; " "it can be used only as a base class") return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
def_generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ isNone: if (base_cls.__new__ isobject.__new__ and cls.__init__ isnotobject.__init__): return base_cls.__new__(cls) else: return base_cls.__new__(cls, *args, **kwds) else: origin = cls._gorg if (base_cls.__new__ isobject.__new__ and cls.__init__ isnotobject.__init__): obj = base_cls.__new__(origin) else: obj = base_cls.__new__(origin, *args, **kwds) try: obj.__orig_class__ = cls except AttributeError: pass obj.__init__(*args, **kwds) return obj
classGenericMeta(TypingMeta, abc.ABCMeta): # ... @_tp_cache def__getitem__(self, params): ifnotisinstance(params, tuple): params = (params,) ifnot params and self._gorg isnotTuple: raise TypeError( "Parameter list to %s[...] cannot be empty" % _qualname(self)) msg = "Parameters to generic types must be types." params = tuple(_type_check(p, msg) for p in params) if self isGeneric: # Generic can only be subscripted with unique type variables. ifnotall(isinstance(p, TypeVar) for p in params): raise TypeError( "Parameters to Generic[...] must all be type variables") iflen(set(params)) != len(params): raise TypeError( "Parameters to Generic[...] must all be unique") tvars = params args = params elif self in (Tuple, Callable): tvars = _type_vars(params) args = params elif self is _Protocol: # _Protocol is internal, don't check anything. tvars = params args = params elif self.__origin__ in (Generic, _Protocol): # Can't subscript Generic[...] or _Protocol[...]. raise TypeError("Cannot subscript already-subscripted %s" % repr(self)) else: # Subscripting a regular Generic subclass. _check_generic(self, params) tvars = _type_vars(params) args = params
def__new__(cls, name, bases, namespace, tvars=None, args=None, origin=None, extra=None, orig_bases=None): """Create a new generic class. GenericMeta.__new__ accepts keyword arguments that are used for internal bookkeeping, therefore an override should pass unused keyword arguments to super(). """ if tvars isnotNone: # Called from __getitem__() below. assert origin isnotNone assertall(isinstance(t, TypeVar) for t in tvars), tvars else: # Called from class statement. assert tvars isNone, tvars assert args isNone, args assert origin isNone, origin
# Get the full set of tvars from the bases. tvars = _type_vars(bases) # Look for Generic[T1, ..., Tn]. # If found, tvars must be a subset of it. # If not found, tvars is it. # Also check for and reject plain Generic, # and reject multiple Generic[...]. gvars = None for base in bases: if base isGeneric: raise TypeError("Cannot inherit from plain Generic") if (isinstance(base, GenericMeta) and base.__origin__ isGeneric): if gvars isnotNone: raise TypeError( "Cannot inherit from Generic[...] multiple types.") gvars = base.__parameters__ if gvars isNone: gvars = tvars else: tvarset = set(tvars) gvarset = set(gvars) ifnot tvarset <= gvarset: raise TypeError( "Some type variables (%s) " "are not listed in Generic[%s]" % (", ".join(str(t) for t in tvars if t notin gvarset), ", ".join(str(g) for g in gvars))) tvars = gvars
initial_bases = bases if extra isnotNoneandtype(extra) is abc.ABCMeta and extra notin bases: bases = (extra,) + bases bases = tuple(b._gorg ifisinstance(b, GenericMeta) else b for b in bases)
# remove bare Generic from bases if there are other generic bases ifany(isinstance(b, GenericMeta) and b isnotGenericfor b in bases): bases = tuple(b for b in bases if b isnotGeneric) namespace.update({'__origin__': origin, '__extra__': extra, '_gorg': Noneifnot origin else origin._gorg}) self = super().__new__(cls, name, bases, namespace, _root=True) super(GenericMeta, self).__setattr__('_gorg', self ifnot origin else origin._gorg) self.__parameters__ = tvars # Be prepared that GenericMeta will be subclassed by TupleMeta # and CallableMeta, those two allow ..., (), or [] in __args___. self.__args__ = tuple(... if a is _TypingEllipsis else () if a is _TypingEmpty else a for a in args) if args elseNone # Speed hack (https://github.com/python/typing/issues/196). self.__next_in_mro__ = _next_in_mro(self) # Preserve base classes on subclassing (__bases__ are type erased now). if orig_bases isNone: self.__orig_bases__ = initial_bases
# This allows unparameterized generic collections to be used # with issubclass() and isinstance() in the same way as their # collections.abc counterparts (e.g., isinstance([], Iterable)). if ( '__subclasshook__'notin namespace and extra or # allow overriding getattr(self.__subclasshook__, '__name__', '') == '__extrahook__' ): self.__subclasshook__ = _make_subclasshook(self) ifisinstance(extra, abc.ABCMeta): self._abc_registry = extra._abc_registry self._abc_cache = extra._abc_cache elif origin isnotNone: self._abc_registry = origin._abc_registry self._abc_cache = origin._abc_cache
if origin andhasattr(origin, '__qualname__'): # Fix for Python 3.2. self.__qualname__ = origin.__qualname__ self.__tree_hash__ = (hash(self._subs_tree()) if origin else super(GenericMeta, self).__hash__()) return self
def_next_in_mro(cls): """Helper for Generic.__new__. Returns the class after the last occurrence of Generic or Generic[...] in cls.__mro__. """ next_in_mro = object # Look for the last occurrence of Generic or Generic[...]. for i, c inenumerate(cls.__mro__[:-1]): ifisinstance(c, GenericMeta) and c._gorg isGeneric: next_in_mro = cls.__mro__[i + 1] return next_in_mro
def__getitem__(self, key: _KT) -> _VT: # get value value = ... value_type = self.__orig_class__.__args__[1] ifnotisinstance(value, value_type): raise ValueType("value type is inconsistent with dict declaration")
def_type_check(arg, msg): """Check that the argument is a type, and return it (internal helper). As a special case, accept None and return type(None) instead. Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable. The msg argument is a human-readable error message, e.g. "Union[arg, ...]: arg should be a type." We append the repr() of the actual value (truncated to 100 chars). """ if arg isNone: returntype(None) ifisinstance(arg, str): arg = _ForwardRef(arg) if ( isinstance(arg, _TypingBase) andtype(arg).__name__ == '_ClassVar'or notisinstance(arg, (type, _TypingBase)) andnotcallable(arg) ): raise TypeError(msg + " Got %.100r." % (arg,)) # Bare Union etc. are not valid as type arguments if ( type(arg).__name__ in ('_Union', '_Optional') and notgetattr(arg, '__origin__', None) or isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol) ): raise TypeError("Plain %s is not valid as type argument" % arg) return arg