summaryrefslogtreecommitdiff
path: root/python/testData/typing/typing.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/testData/typing/typing.py')
-rw-r--r--python/testData/typing/typing.py582
1 files changed, 582 insertions, 0 deletions
diff --git a/python/testData/typing/typing.py b/python/testData/typing/typing.py
new file mode 100644
index 000000000000..1fbd5fc8081b
--- /dev/null
+++ b/python/testData/typing/typing.py
@@ -0,0 +1,582 @@
+"""Static type checking helpers"""
+
+from abc import ABCMeta, abstractmethod, abstractproperty
+import inspect
+import sys
+import re
+
+
+__all__ = [
+ # Type system related
+ 'AbstractGeneric',
+ 'AbstractGenericMeta',
+ 'Any',
+ 'AnyStr',
+ 'Dict',
+ 'Function',
+ 'Generic',
+ 'GenericMeta',
+ 'IO',
+ 'List',
+ 'Match',
+ 'Pattern',
+ 'Protocol',
+ 'Set',
+ 'Tuple',
+ 'Undefined',
+ 'Union',
+ 'cast',
+ 'forwardref',
+ 'overload',
+ 'typevar',
+ # Protocols and abstract base classes
+ 'Container',
+ 'Iterable',
+ 'Iterator',
+ 'Sequence',
+ 'Sized',
+ 'AbstractSet',
+ 'Mapping',
+ 'BinaryIO',
+ 'TextIO',
+]
+
+
+def builtinclass(cls):
+ """Mark a class as a built-in/extension class for type checking."""
+ return cls
+
+
+def ducktype(type):
+ """Return a duck type declaration decorator.
+
+ The decorator only affects type checking.
+ """
+ def decorator(cls):
+ return cls
+ return decorator
+
+
+def disjointclass(type):
+ """Return a disjoint class declaration decorator.
+
+ The decorator only affects type checking.
+ """
+ def decorator(cls):
+ return cls
+ return decorator
+
+
+class GenericMeta(type):
+ """Metaclass for generic classes that support indexing by types."""
+
+ def __getitem__(self, args):
+ # Just ignore args; they are for compile-time checks only.
+ return self
+
+
+class Generic(metaclass=GenericMeta):
+ """Base class for generic classes."""
+
+
+class AbstractGenericMeta(ABCMeta):
+ """Metaclass for abstract generic classes that support type indexing.
+
+ This is used for both protocols and ordinary abstract classes.
+ """
+
+ def __new__(mcls, name, bases, namespace):
+ cls = super().__new__(mcls, name, bases, namespace)
+ # 'Protocol' must be an explicit base class in order for a class to
+ # be a protocol.
+ cls._is_protocol = name == 'Protocol' or Protocol in bases
+ return cls
+
+ def __getitem__(self, args):
+ # Just ignore args; they are for compile-time checks only.
+ return self
+
+
+class Protocol(metaclass=AbstractGenericMeta):
+ """Base class for protocol classes."""
+
+ @classmethod
+ def __subclasshook__(cls, c):
+ if not cls._is_protocol:
+ # No structural checks since this isn't a protocol.
+ return NotImplemented
+
+ if cls is Protocol:
+ # Every class is a subclass of the empty protocol.
+ return True
+
+ # Find all attributes defined in the protocol.
+ attrs = cls._get_protocol_attrs()
+
+ for attr in attrs:
+ if not any(attr in d.__dict__ for d in c.__mro__):
+ return NotImplemented
+ return True
+
+ @classmethod
+ def _get_protocol_attrs(cls):
+ # Get all Protocol base classes.
+ protocol_bases = []
+ for c in cls.__mro__:
+ if getattr(c, '_is_protocol', False) and c.__name__ != 'Protocol':
+ protocol_bases.append(c)
+
+ # Get attributes included in protocol.
+ attrs = set()
+ for base in protocol_bases:
+ for attr in base.__dict__.keys():
+ # Include attributes not defined in any non-protocol bases.
+ for c in cls.__mro__:
+ if (c is not base and attr in c.__dict__ and
+ not getattr(c, '_is_protocol', False)):
+ break
+ else:
+ if (not attr.startswith('_abc_') and
+ attr != '__abstractmethods__' and
+ attr != '_is_protocol' and
+ attr != '__dict__' and
+ attr != '_get_protocol_attrs' and
+ attr != '__module__'):
+ attrs.add(attr)
+
+ return attrs
+
+
+class AbstractGeneric(metaclass=AbstractGenericMeta):
+ """Base class for abstract generic classes."""
+
+
+class TypeAlias:
+ """Class for defining generic aliases for library types."""
+
+ def __init__(self, target_type):
+ self.target_type = target_type
+
+ def __getitem__(self, typeargs):
+ return self.target_type
+
+
+Traceback = object() # TODO proper type object
+
+
+# Define aliases for built-in types that support indexing.
+List = TypeAlias(list)
+Dict = TypeAlias(dict)
+Set = TypeAlias(set)
+Tuple = TypeAlias(tuple)
+Function = TypeAlias(callable)
+Pattern = TypeAlias(type(re.compile('')))
+Match = TypeAlias(type(re.match('', '')))
+
+def union(x): return x
+
+Union = TypeAlias(union)
+
+class typevar:
+ def __init__(self, name, *, values=None):
+ self.name = name
+ self.values = values
+
+
+# Predefined type variables.
+AnyStr = typevar('AnyStr', values=(str, bytes))
+
+
+class forwardref:
+ def __init__(self, name):
+ self.name = name
+
+
+def Any(x):
+ """The Any type; can also be used to cast a value to type Any."""
+ return x
+
+def cast(type, object):
+ """Cast a value to a type.
+
+ This only affects static checking; simply return object at runtime.
+ """
+ return object
+
+
+def overload(func):
+ """Function decorator for defining overloaded functions."""
+ frame = sys._getframe(1)
+ locals = frame.f_locals
+ # See if there is a previous overload variant available. Also verify
+ # that the existing function really is overloaded: otherwise, replace
+ # the definition. The latter is actually important if we want to reload
+ # a library module such as genericpath with a custom one that uses
+ # overloading in the implementation.
+ if func.__name__ in locals and hasattr(locals[func.__name__], 'dispatch'):
+ orig_func = locals[func.__name__]
+
+ def wrapper(*args, **kwargs):
+ ret, ok = orig_func.dispatch(*args, **kwargs)
+ if ok:
+ return ret
+ return func(*args, **kwargs)
+ wrapper.isoverload = True
+ wrapper.dispatch = make_dispatcher(func, orig_func.dispatch)
+ wrapper.next = orig_func
+ wrapper.__name__ = func.__name__
+ if hasattr(func, '__isabstractmethod__'):
+ # Note that we can't reliably check that abstractmethod is
+ # used consistently across overload variants, so we let a
+ # static checker do it.
+ wrapper.__isabstractmethod__ = func.__isabstractmethod__
+ return wrapper
+ else:
+ # Return the initial overload variant.
+ func.isoverload = True
+ func.dispatch = make_dispatcher(func)
+ func.next = None
+ return func
+
+
+def is_erased_type(t):
+ return t is Any or isinstance(t, typevar)
+
+
+def make_dispatcher(func, previous=None):
+ """Create argument dispatcher for an overloaded function.
+
+ Also handle chaining of multiple overload variants.
+ """
+ (args, varargs, varkw, defaults,
+ kwonlyargs, kwonlydefaults, annotations) = inspect.getfullargspec(func)
+
+ argtypes = []
+ for arg in args:
+ ann = annotations.get(arg)
+ if isinstance(ann, forwardref):
+ ann = ann.name
+ if is_erased_type(ann):
+ ann = None
+ elif isinstance(ann, str):
+ # The annotation is a string => evaluate it lazily when the
+ # overloaded function is first called.
+ frame = sys._getframe(2)
+ t = [None]
+ ann_str = ann
+ def check(x):
+ if not t[0]:
+ # Evaluate string in the context of the overload caller.
+ t[0] = eval(ann_str, frame.f_globals, frame.f_locals)
+ if is_erased_type(t[0]):
+ # Anything goes.
+ t[0] = object
+ if isinstance(t[0], type):
+ return isinstance(x, t[0])
+ else:
+ return t[0](x)
+ ann = check
+ argtypes.append(ann)
+
+ maxargs = len(argtypes)
+ minargs = maxargs
+ if defaults:
+ minargs = len(argtypes) - len(defaults)
+
+ def dispatch(*args, **kwargs):
+ if previous:
+ ret, ok = previous(*args, **kwargs)
+ if ok:
+ return ret, ok
+
+ nargs = len(args)
+ if nargs < minargs or nargs > maxargs:
+ # Invalid argument count.
+ return None, False
+
+ for i in range(nargs):
+ argtype = argtypes[i]
+ if argtype:
+ if isinstance(argtype, type):
+ if not isinstance(args[i], argtype):
+ break
+ else:
+ if not argtype(args[i]):
+ break
+ else:
+ return func(*args, **kwargs), True
+ return None, False
+ return dispatch
+
+
+class Undefined:
+ """Class that represents an undefined value with a specified type.
+
+ At runtime the name Undefined is bound to an instance of this
+ class. The intent is that any operation on an Undefined object
+ raises an exception, including use in a boolean context. Some
+ operations cannot be disallowed: Undefined can be used as an
+ operand of 'is', and it can be assigned to variables and stored in
+ containers.
+
+ 'Undefined' makes it possible to declare the static type of a
+ variable even if there is no useful default value to initialize it
+ with:
+
+ from typing import Undefined
+ x = Undefined(int)
+ y = Undefined # type: int
+
+ The latter form can be used if efficiency is of utmost importance,
+ since it saves a call operation and potentially additional
+ operations needed to evaluate a type expression. Undefined(x)
+ just evaluates to Undefined, ignoring the argument value.
+ """
+
+ def __repr__(self):
+ return '<typing.Undefined>'
+
+ def __setattr__(self, attr, value):
+ raise AttributeError("'Undefined' object has no attribute '%s'" % attr)
+
+ def __eq__(self, other):
+ raise TypeError("'Undefined' object cannot be compared")
+
+ def __call__(self, type):
+ return self
+
+ def __bool__(self):
+ raise TypeError("'Undefined' object is not valid as a boolean")
+
+
+Undefined = Undefined()
+
+
+# Abstract classes
+
+
+T = typevar('T')
+KT = typevar('KT')
+VT = typevar('VT')
+
+
+class SupportsInt(Protocol):
+ @abstractmethod
+ def __int__(self) -> int: pass
+
+
+class SupportsFloat(Protocol):
+ @abstractmethod
+ def __float__(self) -> float: pass
+
+
+class SupportsAbs(Protocol[T]):
+ @abstractmethod
+ def __abs__(self) -> T: pass
+
+
+class SupportsRound(Protocol[T]):
+ @abstractmethod
+ def __round__(self, ndigits: int = 0) -> T: pass
+
+
+class Reversible(Protocol[T]):
+ @abstractmethod
+ def __reversed__(self) -> 'Iterator[T]': pass
+
+
+class Sized(Protocol):
+ @abstractmethod
+ def __len__(self) -> int: pass
+
+
+class Container(Protocol[T]):
+ @abstractmethod
+ def __contains__(self, x) -> bool: pass
+
+
+class Iterable(Protocol[T]):
+ @abstractmethod
+ def __iter__(self) -> 'Iterator[T]': pass
+
+
+class Iterator(Iterable[T], Protocol[T]):
+ @abstractmethod
+ def __next__(self) -> T: pass
+
+
+class Sequence(Sized, Iterable[T], Container[T], AbstractGeneric[T]):
+ @abstractmethod
+ @overload
+ def __getitem__(self, i: int) -> T: pass
+
+ @abstractmethod
+ @overload
+ def __getitem__(self, s: slice) -> 'Sequence[T]': pass
+
+ @abstractmethod
+ def __reversed__(self, s: slice) -> Iterator[T]: pass
+
+ @abstractmethod
+ def index(self, x) -> int: pass
+
+ @abstractmethod
+ def count(self, x) -> int: pass
+
+
+for t in list, tuple, str, bytes, range:
+ Sequence.register(t)
+
+
+class AbstractSet(Sized, Iterable[T], AbstractGeneric[T]):
+ @abstractmethod
+ def __contains__(self, x: object) -> bool: pass
+ @abstractmethod
+ def __and__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass
+ @abstractmethod
+ def __or__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass
+ @abstractmethod
+ def __sub__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass
+ @abstractmethod
+ def __xor__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass
+ @abstractmethod
+ def isdisjoint(self, s: 'AbstractSet[T]') -> bool: pass
+
+
+for t in set, frozenset, type({}.keys()), type({}.items()):
+ AbstractSet.register(t)
+
+
+class Mapping(Sized, Iterable[KT], AbstractGeneric[KT, VT]):
+ @abstractmethod
+ def __getitem__(self, k: KT) -> VT: pass
+ @abstractmethod
+ def __setitem__(self, k: KT, v: VT) -> None: pass
+ @abstractmethod
+ def __delitem__(self, v: KT) -> None: pass
+ @abstractmethod
+ def __contains__(self, o: object) -> bool: pass
+
+ @abstractmethod
+ def clear(self) -> None: pass
+ @abstractmethod
+ def copy(self) -> 'Mapping[KT, VT]': pass
+ @overload
+ @abstractmethod
+ def get(self, k: KT) -> VT: pass
+ @overload
+ @abstractmethod
+ def get(self, k: KT, default: VT) -> VT: pass
+ @overload
+ @abstractmethod
+ def pop(self, k: KT) -> VT: pass
+ @overload
+ @abstractmethod
+ def pop(self, k: KT, default: VT) -> VT: pass
+ @abstractmethod
+ def popitem(self) -> Tuple[KT, VT]: pass
+ @overload
+ @abstractmethod
+ def setdefault(self, k: KT) -> VT: pass
+ @overload
+ @abstractmethod
+ def setdefault(self, k: KT, default: VT) -> VT: pass
+
+ @overload
+ @abstractmethod
+ def update(self, m: 'Mapping[KT, VT]') -> None: pass
+ @overload
+ @abstractmethod
+ def update(self, m: Iterable[Tuple[KT, VT]]) -> None: pass
+
+ @abstractmethod
+ def keys(self) -> AbstractSet[KT]: pass
+ @abstractmethod
+ def values(self) -> AbstractSet[VT]: pass
+ @abstractmethod
+ def items(self) -> AbstractSet[Tuple[KT, VT]]: pass
+
+
+# TODO Consider more types: os.environ, etc. However, these add dependencies.
+Mapping.register(dict)
+
+
+# Note that the BinaryIO and TextIO classes must be in sync with typing module
+# stubs.
+
+
+class IO(AbstractGeneric[AnyStr]):
+ @abstractproperty
+ def mode(self) -> str: pass
+ @abstractproperty
+ def name(self) -> str: pass
+ @abstractmethod
+ def close(self) -> None: pass
+ @abstractmethod
+ def closed(self) -> bool: pass
+ @abstractmethod
+ def fileno(self) -> int: pass
+ @abstractmethod
+ def flush(self) -> None: pass
+ @abstractmethod
+ def isatty(self) -> bool: pass
+ @abstractmethod
+ def read(self, n: int = -1) -> AnyStr: pass
+ @abstractmethod
+ def readable(self) -> bool: pass
+ @abstractmethod
+ def readline(self, limit: int = -1) -> AnyStr: pass
+ @abstractmethod
+ def readlines(self, hint: int = -1) -> List[AnyStr]: pass
+ @abstractmethod
+ def seek(self, offset: int, whence: int = 0) -> int: pass
+ @abstractmethod
+ def seekable(self) -> bool: pass
+ @abstractmethod
+ def tell(self) -> int: pass
+ @abstractmethod
+ def truncate(self, size: int = None) -> int: pass
+ @abstractmethod
+ def writable(self) -> bool: pass
+ @abstractmethod
+ def write(self, s: AnyStr) -> int: pass
+ @abstractmethod
+ def writelines(self, lines: List[AnyStr]) -> None: pass
+
+ @abstractmethod
+ def __enter__(self) -> 'IO[AnyStr]': pass
+ @abstractmethod
+ def __exit__(self, type, value, traceback) -> None: pass
+
+
+class BinaryIO(IO[bytes]):
+ @overload
+ @abstractmethod
+ def write(self, s: bytes) -> int: pass
+ @overload
+ @abstractmethod
+ def write(self, s: bytearray) -> int: pass
+
+ @abstractmethod
+ def __enter__(self) -> 'BinaryIO': pass
+
+
+class TextIO(IO[str]):
+ @abstractproperty
+ def buffer(self) -> BinaryIO: pass
+ @abstractproperty
+ def encoding(self) -> str: pass
+ @abstractproperty
+ def errors(self) -> str: pass
+ @abstractproperty
+ def line_buffering(self) -> bool: pass
+ @abstractproperty
+ def newlines(self) -> Any: pass
+ @abstractmethod
+ def __enter__(self) -> 'TextIO': pass
+
+
+# TODO Register IO/TextIO/BinaryIO as the base class of file-like types.
+
+
+del t