Many changes from the upstream repo (https://github.com/python/typing).
This syncs to rev 7b43ada77821d23e55e3a4b35f6055a59b9e1ad7 there. Summary: - Add typing.DefaultDict (as a generic variant of collections.defaultdict). - Use collections.Reversible if it exists (only relevant for Python 3.6). - Revamped generic class behavior to conform to updated PEP 484. - Improve speed of Generic.__new__. - Make sure __init__ is called for new Generic instances. Fix issue #26391. - Refactor async support to be compatible with 3.2, 3.3, 3.4. - Remove 'io' and 're' from __all__ (they still exist, just not included by "import *"). Fix issue #26234. - Change @overload -- you can now use it outside stubs (you still cannot call the decorated function though).
This commit is contained in:
parent
0f7673943a
commit
bd5b9a0742
|
@ -1,8 +1,7 @@
|
|||
import asyncio
|
||||
import pickle
|
||||
import re
|
||||
import sys
|
||||
from unittest import TestCase, main
|
||||
from unittest import TestCase, main, skipUnless
|
||||
|
||||
from typing import Any
|
||||
from typing import TypeVar, AnyStr
|
||||
|
@ -133,6 +132,7 @@ class TypeVarTests(TestCase):
|
|||
def test_constrained_error(self):
|
||||
with self.assertRaises(TypeError):
|
||||
X = TypeVar('X', int)
|
||||
X
|
||||
|
||||
def test_union_unique(self):
|
||||
X = TypeVar('X')
|
||||
|
@ -317,6 +317,7 @@ class UnionTests(TestCase):
|
|||
def test_union_str_pattern(self):
|
||||
# Shouldn't crash; see http://bugs.python.org/issue25390
|
||||
A = Union[str, Pattern]
|
||||
A
|
||||
|
||||
|
||||
class TypeVarUnionTests(TestCase):
|
||||
|
@ -487,7 +488,7 @@ class SimpleMapping(Generic[XK, XV]):
|
|||
...
|
||||
|
||||
|
||||
class MySimpleMapping(SimpleMapping):
|
||||
class MySimpleMapping(SimpleMapping[XK, XV]):
|
||||
|
||||
def __init__(self):
|
||||
self.store = {}
|
||||
|
@ -541,6 +542,7 @@ class ProtocolTests(TestCase):
|
|||
assert not issubclass(str, typing.SupportsAbs)
|
||||
|
||||
def test_supports_round(self):
|
||||
issubclass(float, typing.SupportsRound)
|
||||
assert issubclass(float, typing.SupportsRound)
|
||||
assert issubclass(int, typing.SupportsRound)
|
||||
assert not issubclass(str, typing.SupportsRound)
|
||||
|
@ -551,20 +553,23 @@ class ProtocolTests(TestCase):
|
|||
|
||||
def test_protocol_instance_type_error(self):
|
||||
with self.assertRaises(TypeError):
|
||||
isinstance([], typing.Reversible)
|
||||
isinstance(0, typing.SupportsAbs)
|
||||
|
||||
|
||||
class GenericTests(TestCase):
|
||||
|
||||
def test_basics(self):
|
||||
X = SimpleMapping[str, Any]
|
||||
Y = SimpleMapping[XK, str]
|
||||
assert X.__parameters__ == ()
|
||||
with self.assertRaises(TypeError):
|
||||
X[str]
|
||||
with self.assertRaises(TypeError):
|
||||
X[str, str]
|
||||
Y = SimpleMapping[XK, str]
|
||||
assert Y.__parameters__ == (XK,)
|
||||
Y[str]
|
||||
with self.assertRaises(TypeError):
|
||||
Y[str, str]
|
||||
with self.assertRaises(TypeError):
|
||||
X[int, str]
|
||||
with self.assertRaises(TypeError):
|
||||
Y[str, bytes]
|
||||
|
||||
def test_init(self):
|
||||
T = TypeVar('T')
|
||||
|
@ -576,30 +581,61 @@ class GenericTests(TestCase):
|
|||
|
||||
def test_repr(self):
|
||||
self.assertEqual(repr(SimpleMapping),
|
||||
__name__ + '.' + 'SimpleMapping[~XK, ~XV]')
|
||||
__name__ + '.' + 'SimpleMapping<~XK, ~XV>')
|
||||
self.assertEqual(repr(MySimpleMapping),
|
||||
__name__ + '.' + 'MySimpleMapping[~XK, ~XV]')
|
||||
__name__ + '.' + 'MySimpleMapping<~XK, ~XV>')
|
||||
|
||||
def test_chain_repr(self):
|
||||
T = TypeVar('T')
|
||||
S = TypeVar('S')
|
||||
|
||||
class C(Generic[T]):
|
||||
pass
|
||||
|
||||
X = C[Tuple[S, T]]
|
||||
assert X == C[Tuple[S, T]]
|
||||
assert X != C[Tuple[T, S]]
|
||||
|
||||
Y = X[T, int]
|
||||
assert Y == X[T, int]
|
||||
assert Y != X[S, int]
|
||||
assert Y != X[T, str]
|
||||
|
||||
Z = Y[str]
|
||||
assert Z == Y[str]
|
||||
assert Z != Y[int]
|
||||
assert Z != Y[T]
|
||||
|
||||
assert str(Z).endswith(
|
||||
'.C<~T>[typing.Tuple[~S, ~T]]<~S, ~T>[~T, int]<~T>[str]')
|
||||
|
||||
def test_dict(self):
|
||||
T = TypeVar('T')
|
||||
|
||||
class B(Generic[T]):
|
||||
pass
|
||||
|
||||
b = B()
|
||||
b.foo = 42
|
||||
self.assertEqual(b.__dict__, {'foo': 42})
|
||||
|
||||
class C(B[int]):
|
||||
pass
|
||||
|
||||
c = C()
|
||||
c.bar = 'abc'
|
||||
self.assertEqual(c.__dict__, {'bar': 'abc'})
|
||||
|
||||
def test_pickle(self):
|
||||
global C # pickle wants to reference the class by name
|
||||
T = TypeVar('T')
|
||||
|
||||
class B(Generic[T]):
|
||||
pass
|
||||
global C # pickle wants to reference the class by name
|
||||
|
||||
class C(B[int]):
|
||||
pass
|
||||
|
||||
c = C()
|
||||
c.foo = 42
|
||||
c.bar = 'abc'
|
||||
|
@ -626,12 +662,12 @@ class GenericTests(TestCase):
|
|||
assert C.__module__ == __name__
|
||||
if not PY32:
|
||||
assert C.__qualname__ == 'GenericTests.test_repr_2.<locals>.C'
|
||||
assert repr(C).split('.')[-1] == 'C[~T]'
|
||||
assert repr(C).split('.')[-1] == 'C<~T>'
|
||||
X = C[int]
|
||||
assert X.__module__ == __name__
|
||||
if not PY32:
|
||||
assert X.__qualname__ == 'C'
|
||||
assert repr(X).split('.')[-1] == 'C[int]'
|
||||
assert repr(X).split('.')[-1] == 'C<~T>[int]'
|
||||
|
||||
class Y(C[int]):
|
||||
pass
|
||||
|
@ -639,7 +675,7 @@ class GenericTests(TestCase):
|
|||
assert Y.__module__ == __name__
|
||||
if not PY32:
|
||||
assert Y.__qualname__ == 'GenericTests.test_repr_2.<locals>.Y'
|
||||
assert repr(Y).split('.')[-1] == 'Y[int]'
|
||||
assert repr(Y).split('.')[-1] == 'Y'
|
||||
|
||||
def test_eq_1(self):
|
||||
assert Generic == Generic
|
||||
|
@ -667,15 +703,14 @@ class GenericTests(TestCase):
|
|||
class B(Generic[KT, T]):
|
||||
pass
|
||||
|
||||
class C(A, Generic[KT, VT], B):
|
||||
class C(A[T, VT], Generic[VT, T, KT], B[KT, T]):
|
||||
pass
|
||||
|
||||
assert C.__parameters__ == (T, VT, KT)
|
||||
assert C.__parameters__ == (VT, T, KT)
|
||||
|
||||
def test_nested(self):
|
||||
|
||||
class G(Generic):
|
||||
pass
|
||||
G = Generic
|
||||
|
||||
class Visitor(G[T]):
|
||||
|
||||
|
@ -721,9 +756,30 @@ class GenericTests(TestCase):
|
|||
assert type(a) is Node
|
||||
assert type(b) is Node
|
||||
assert type(c) is Node
|
||||
assert a.label == x
|
||||
assert b.label == x
|
||||
assert c.label == x
|
||||
|
||||
foo(42)
|
||||
|
||||
def test_implicit_any(self):
|
||||
T = TypeVar('T')
|
||||
|
||||
class C(Generic[T]):
|
||||
pass
|
||||
|
||||
class D(C):
|
||||
pass
|
||||
|
||||
assert D.__parameters__ == ()
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
D[int]
|
||||
with self.assertRaises(Exception):
|
||||
D[Any]
|
||||
with self.assertRaises(Exception):
|
||||
D[T]
|
||||
|
||||
|
||||
class VarianceTests(TestCase):
|
||||
|
||||
|
@ -956,14 +1012,33 @@ class OverloadTests(TestCase):
|
|||
from typing import overload
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
|
||||
@overload
|
||||
def blah():
|
||||
pass
|
||||
|
||||
blah()
|
||||
|
||||
def test_overload_succeeds(self):
|
||||
from typing import overload
|
||||
|
||||
@overload
|
||||
def blah():
|
||||
pass
|
||||
|
||||
def blah():
|
||||
pass
|
||||
|
||||
blah()
|
||||
|
||||
|
||||
PY35 = sys.version_info[:2] >= (3, 5)
|
||||
|
||||
PY35_TESTS = """
|
||||
import asyncio
|
||||
|
||||
T_a = TypeVar('T')
|
||||
|
||||
|
||||
class AwaitableWrapper(typing.Awaitable[T_a]):
|
||||
|
||||
def __init__(self, value):
|
||||
|
@ -973,7 +1048,6 @@ class AwaitableWrapper(typing.Awaitable[T_a]):
|
|||
yield
|
||||
return self.value
|
||||
|
||||
|
||||
class AsyncIteratorWrapper(typing.AsyncIterator[T_a]):
|
||||
|
||||
def __init__(self, value: typing.Iterable[T_a]):
|
||||
|
@ -989,6 +1063,10 @@ class AsyncIteratorWrapper(typing.AsyncIterator[T_a]):
|
|||
return data
|
||||
else:
|
||||
raise StopAsyncIteration
|
||||
"""
|
||||
|
||||
if PY35:
|
||||
exec(PY35_TESTS)
|
||||
|
||||
|
||||
class CollectionsAbcTests(TestCase):
|
||||
|
@ -1015,9 +1093,14 @@ class CollectionsAbcTests(TestCase):
|
|||
assert isinstance(it, typing.Iterator[int])
|
||||
assert not isinstance(42, typing.Iterator)
|
||||
|
||||
@skipUnless(PY35, 'Python 3.5 required')
|
||||
def test_awaitable(self):
|
||||
async def foo() -> typing.Awaitable[int]:
|
||||
return await AwaitableWrapper(42)
|
||||
ns = {}
|
||||
exec(
|
||||
"async def foo() -> typing.Awaitable[int]:\n"
|
||||
" return await AwaitableWrapper(42)\n",
|
||||
globals(), ns)
|
||||
foo = ns['foo']
|
||||
g = foo()
|
||||
assert issubclass(type(g), typing.Awaitable[int])
|
||||
assert isinstance(g, typing.Awaitable)
|
||||
|
@ -1028,6 +1111,7 @@ class CollectionsAbcTests(TestCase):
|
|||
typing.Awaitable[Manager])
|
||||
g.send(None) # Run foo() till completion, to avoid warning.
|
||||
|
||||
@skipUnless(PY35, 'Python 3.5 required')
|
||||
def test_async_iterable(self):
|
||||
base_it = range(10) # type: Iterator[int]
|
||||
it = AsyncIteratorWrapper(base_it)
|
||||
|
@ -1037,6 +1121,7 @@ class CollectionsAbcTests(TestCase):
|
|||
typing.AsyncIterable[Employee])
|
||||
assert not isinstance(42, typing.AsyncIterable)
|
||||
|
||||
@skipUnless(PY35, 'Python 3.5 required')
|
||||
def test_async_iterator(self):
|
||||
base_it = range(10) # type: Iterator[int]
|
||||
it = AsyncIteratorWrapper(base_it)
|
||||
|
@ -1127,6 +1212,22 @@ class CollectionsAbcTests(TestCase):
|
|||
d = MyDict()
|
||||
assert isinstance(d, MyDict)
|
||||
|
||||
def test_no_defaultdict_instantiation(self):
|
||||
with self.assertRaises(TypeError):
|
||||
typing.DefaultDict()
|
||||
with self.assertRaises(TypeError):
|
||||
typing.DefaultDict[KT, VT]()
|
||||
with self.assertRaises(TypeError):
|
||||
typing.DefaultDict[str, int]()
|
||||
|
||||
def test_defaultdict_subclass_instantiation(self):
|
||||
|
||||
class MyDefDict(typing.DefaultDict[str, int]):
|
||||
pass
|
||||
|
||||
dd = MyDefDict()
|
||||
assert isinstance(dd, MyDefDict)
|
||||
|
||||
def test_no_set_instantiation(self):
|
||||
with self.assertRaises(TypeError):
|
||||
typing.Set()
|
||||
|
@ -1251,7 +1352,7 @@ class IOTests(TestCase):
|
|||
return a.readline()
|
||||
|
||||
a = stuff.__annotations__['a']
|
||||
assert a.__parameters__ == (str,)
|
||||
assert a.__parameters__ == ()
|
||||
|
||||
def test_binaryio(self):
|
||||
|
||||
|
@ -1259,7 +1360,7 @@ class IOTests(TestCase):
|
|||
return a.readline()
|
||||
|
||||
a = stuff.__annotations__['a']
|
||||
assert a.__parameters__ == (bytes,)
|
||||
assert a.__parameters__ == ()
|
||||
|
||||
def test_io_submodule(self):
|
||||
from typing.io import IO, TextIO, BinaryIO, __all__, __name__
|
||||
|
@ -1346,8 +1447,9 @@ class AllTests(TestCase):
|
|||
assert 'ValuesView' in a
|
||||
assert 'cast' in a
|
||||
assert 'overload' in a
|
||||
assert 'io' in a
|
||||
assert 're' in a
|
||||
# Check that io and re are not exported.
|
||||
assert 'io' not in a
|
||||
assert 're' not in a
|
||||
# Spot-check that stdlib modules aren't exported.
|
||||
assert 'os' not in a
|
||||
assert 'sys' not in a
|
||||
|
|
360
Lib/typing.py
360
Lib/typing.py
|
@ -1,7 +1,3 @@
|
|||
# TODO nits:
|
||||
# Get rid of asserts that are the caller's fault.
|
||||
# Docstrings (e.g. ABCs).
|
||||
|
||||
import abc
|
||||
from abc import abstractmethod, abstractproperty
|
||||
import collections
|
||||
|
@ -56,6 +52,7 @@ __all__ = [
|
|||
|
||||
# Concrete collection types.
|
||||
'Dict',
|
||||
'DefaultDict',
|
||||
'List',
|
||||
'Set',
|
||||
'NamedTuple', # Not really a type.
|
||||
|
@ -68,12 +65,12 @@ __all__ = [
|
|||
'no_type_check',
|
||||
'no_type_check_decorator',
|
||||
'overload',
|
||||
|
||||
# Submodules.
|
||||
'io',
|
||||
're',
|
||||
]
|
||||
|
||||
# The pseudo-submodules 're' and 'io' are part of the public
|
||||
# namespace, but excluded from __all__ because they might stomp on
|
||||
# legitimate imports of those modules.
|
||||
|
||||
|
||||
def _qualname(x):
|
||||
if sys.version_info[:2] >= (3, 3):
|
||||
|
@ -117,8 +114,8 @@ class TypingMeta(type):
|
|||
"""
|
||||
return self
|
||||
|
||||
def _has_type_var(self):
|
||||
return False
|
||||
def _get_type_vars(self, tvars):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return '%s.%s' % (self.__module__, _qualname(self))
|
||||
|
@ -271,8 +268,16 @@ class _TypeAlias:
|
|||
return issubclass(cls, self.impl_type)
|
||||
|
||||
|
||||
def _has_type_var(t):
|
||||
return t is not None and isinstance(t, TypingMeta) and t._has_type_var()
|
||||
def _get_type_vars(types, tvars):
|
||||
for t in types:
|
||||
if isinstance(t, TypingMeta):
|
||||
t._get_type_vars(tvars)
|
||||
|
||||
|
||||
def _type_vars(types):
|
||||
tvars = []
|
||||
_get_type_vars(types, tvars)
|
||||
return tuple(tvars)
|
||||
|
||||
|
||||
def _eval_type(t, globalns, localns):
|
||||
|
@ -376,7 +381,7 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True):
|
|||
At runtime, isinstance(x, T) will raise TypeError. However,
|
||||
issubclass(C, T) is true for any class C, and issubclass(str, A)
|
||||
and issubclass(bytes, A) are true, and issubclass(int, A) is
|
||||
false.
|
||||
false. (TODO: Why is this needed? This may change. See #136.)
|
||||
|
||||
Type variables may be marked covariant or contravariant by passing
|
||||
covariant=True or contravariant=True. See PEP 484 for more
|
||||
|
@ -410,8 +415,9 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True):
|
|||
self.__bound__ = None
|
||||
return self
|
||||
|
||||
def _has_type_var(self):
|
||||
return True
|
||||
def _get_type_vars(self, tvars):
|
||||
if self not in tvars:
|
||||
tvars.append(self)
|
||||
|
||||
def __repr__(self):
|
||||
if self.__covariant__:
|
||||
|
@ -448,7 +454,6 @@ VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers.
|
|||
T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant.
|
||||
|
||||
# A useful type variable with constraints. This represents string types.
|
||||
# TODO: What about bytearray, memoryview?
|
||||
AnyStr = TypeVar('AnyStr', bytes, str)
|
||||
|
||||
|
||||
|
@ -514,12 +519,9 @@ class UnionMeta(TypingMeta):
|
|||
return self.__class__(self.__name__, self.__bases__, {},
|
||||
p, _root=True)
|
||||
|
||||
def _has_type_var(self):
|
||||
def _get_type_vars(self, tvars):
|
||||
if self.__union_params__:
|
||||
for t in self.__union_params__:
|
||||
if _has_type_var(t):
|
||||
return True
|
||||
return False
|
||||
_get_type_vars(self.__union_params__, tvars)
|
||||
|
||||
def __repr__(self):
|
||||
r = super().__repr__()
|
||||
|
@ -656,12 +658,9 @@ class TupleMeta(TypingMeta):
|
|||
self.__tuple_use_ellipsis__ = use_ellipsis
|
||||
return self
|
||||
|
||||
def _has_type_var(self):
|
||||
def _get_type_vars(self, tvars):
|
||||
if self.__tuple_params__:
|
||||
for t in self.__tuple_params__:
|
||||
if _has_type_var(t):
|
||||
return True
|
||||
return False
|
||||
_get_type_vars(self.__tuple_params__, tvars)
|
||||
|
||||
def _eval_type(self, globalns, localns):
|
||||
tp = self.__tuple_params__
|
||||
|
@ -769,12 +768,9 @@ class CallableMeta(TypingMeta):
|
|||
self.__result__ = result
|
||||
return self
|
||||
|
||||
def _has_type_var(self):
|
||||
def _get_type_vars(self, tvars):
|
||||
if self.__args__:
|
||||
for t in self.__args__:
|
||||
if _has_type_var(t):
|
||||
return True
|
||||
return _has_type_var(self.__result__)
|
||||
_get_type_vars(self.__args__, tvars)
|
||||
|
||||
def _eval_type(self, globalns, localns):
|
||||
if self.__args__ is None and self.__result__ is None:
|
||||
|
@ -878,76 +874,106 @@ def _geqv(a, b):
|
|||
return _gorg(a) is _gorg(b)
|
||||
|
||||
|
||||
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 in enumerate(cls.__mro__[:-1]):
|
||||
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
|
||||
next_in_mro = cls.__mro__[i+1]
|
||||
return next_in_mro
|
||||
|
||||
|
||||
class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||
"""Metaclass for generic types."""
|
||||
|
||||
# TODO: Constrain more how Generic is used; only a few
|
||||
# standard patterns should be allowed.
|
||||
|
||||
# TODO: Use a more precise rule than matching __name__ to decide
|
||||
# whether two classes are the same. Also, save the formal
|
||||
# parameters. (These things are related! A solution lies in
|
||||
# using origin.)
|
||||
|
||||
__extra__ = None
|
||||
|
||||
def __new__(cls, name, bases, namespace,
|
||||
parameters=None, origin=None, extra=None):
|
||||
if parameters is None:
|
||||
# Extract parameters from direct base classes. Only
|
||||
# direct bases are considered and only those that are
|
||||
# themselves generic, and parameterized with type
|
||||
# variables. Don't use bases like Any, Union, Tuple,
|
||||
# Callable or type variables.
|
||||
params = None
|
||||
for base in bases:
|
||||
if isinstance(base, TypingMeta):
|
||||
if not isinstance(base, GenericMeta):
|
||||
raise TypeError(
|
||||
"You cannot inherit from magic class %s" %
|
||||
repr(base))
|
||||
if base.__parameters__ is None:
|
||||
continue # The base is unparameterized.
|
||||
for bp in base.__parameters__:
|
||||
if _has_type_var(bp) and not isinstance(bp, TypeVar):
|
||||
raise TypeError(
|
||||
"Cannot inherit from a generic class "
|
||||
"parameterized with "
|
||||
"non-type-variable %s" % bp)
|
||||
if params is None:
|
||||
params = []
|
||||
if bp not in params:
|
||||
params.append(bp)
|
||||
if params is not None:
|
||||
parameters = tuple(params)
|
||||
tvars=None, args=None, origin=None, extra=None):
|
||||
self = super().__new__(cls, name, bases, namespace, _root=True)
|
||||
self.__parameters__ = parameters
|
||||
|
||||
if tvars is not None:
|
||||
# Called from __getitem__() below.
|
||||
assert origin is not None
|
||||
assert all(isinstance(t, TypeVar) for t in tvars), tvars
|
||||
else:
|
||||
# Called from class statement.
|
||||
assert tvars is None, tvars
|
||||
assert args is None, args
|
||||
assert origin is None, 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 is Generic:
|
||||
raise TypeError("Cannot inherit from plain Generic")
|
||||
if (isinstance(base, GenericMeta) and
|
||||
base.__origin__ is Generic):
|
||||
if gvars is not None:
|
||||
raise TypeError(
|
||||
"Cannot inherit from Generic[...] multiple types.")
|
||||
gvars = base.__parameters__
|
||||
if gvars is None:
|
||||
gvars = tvars
|
||||
else:
|
||||
tvarset = set(tvars)
|
||||
gvarset = set(gvars)
|
||||
if not tvarset <= gvarset:
|
||||
raise TypeError(
|
||||
"Some type variables (%s) "
|
||||
"are not listed in Generic[%s]" %
|
||||
(", ".join(str(t) for t in tvars if t not in gvarset),
|
||||
", ".join(str(g) for g in gvars)))
|
||||
tvars = gvars
|
||||
|
||||
self.__parameters__ = tvars
|
||||
self.__args__ = args
|
||||
self.__origin__ = origin
|
||||
if extra is not None:
|
||||
self.__extra__ = extra
|
||||
# Else __extra__ is inherited, eventually from the
|
||||
# (meta-)class default above.
|
||||
self.__origin__ = origin
|
||||
# Speed hack (https://github.com/python/typing/issues/196).
|
||||
self.__next_in_mro__ = _next_in_mro(self)
|
||||
return self
|
||||
|
||||
def _has_type_var(self):
|
||||
if self.__parameters__:
|
||||
for t in self.__parameters__:
|
||||
if _has_type_var(t):
|
||||
return True
|
||||
return False
|
||||
def _get_type_vars(self, tvars):
|
||||
if self.__origin__ and self.__parameters__:
|
||||
_get_type_vars(self.__parameters__, tvars)
|
||||
|
||||
def __repr__(self):
|
||||
if self.__origin__ is not None:
|
||||
r = repr(self.__origin__)
|
||||
else:
|
||||
r = super().__repr__()
|
||||
if self.__parameters__ is not None:
|
||||
if self.__args__:
|
||||
r += '[%s]' % (
|
||||
', '.join(_type_repr(p) for p in self.__args__))
|
||||
if self.__parameters__:
|
||||
r += '<%s>' % (
|
||||
', '.join(_type_repr(p) for p in self.__parameters__))
|
||||
return r
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, GenericMeta):
|
||||
return NotImplemented
|
||||
return (_geqv(self, other) and
|
||||
if self.__origin__ is not None:
|
||||
return (self.__origin__ is other.__origin__ and
|
||||
self.__args__ == other.__args__ and
|
||||
self.__parameters__ == other.__parameters__)
|
||||
else:
|
||||
return self is other
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.__name__, self.__parameters__))
|
||||
|
@ -956,37 +982,45 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
|||
if not isinstance(params, tuple):
|
||||
params = (params,)
|
||||
if not params:
|
||||
raise TypeError("Cannot have empty parameter list")
|
||||
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.__parameters__ is None:
|
||||
for p in params:
|
||||
if not isinstance(p, TypeVar):
|
||||
raise TypeError("Initial parameters must be "
|
||||
"type variables; got %s" % p)
|
||||
if self is Generic:
|
||||
# Generic can only be subscripted with unique type variables.
|
||||
if not all(isinstance(p, TypeVar) for p in params):
|
||||
raise TypeError(
|
||||
"Parameters to Generic[...] must all be type variables")
|
||||
if len(set(params)) != len(params):
|
||||
raise TypeError(
|
||||
"All type variables in Generic[...] must be distinct.")
|
||||
"Parameters to Generic[...] must all be unique")
|
||||
tvars = params
|
||||
args = None
|
||||
elif self is _Protocol:
|
||||
# _Protocol is internal, don't check anything.
|
||||
tvars = params
|
||||
args = None
|
||||
elif self.__origin__ in (Generic, _Protocol):
|
||||
# Can't subscript Generic[...] or _Protocol[...].
|
||||
raise TypeError("Cannot subscript already-subscripted %s" %
|
||||
repr(self))
|
||||
else:
|
||||
if len(params) != len(self.__parameters__):
|
||||
raise TypeError("Cannot change parameter count from %d to %d" %
|
||||
(len(self.__parameters__), len(params)))
|
||||
for new, old in zip(params, self.__parameters__):
|
||||
if isinstance(old, TypeVar):
|
||||
if not old.__constraints__:
|
||||
# Substituting for an unconstrained TypeVar is OK.
|
||||
continue
|
||||
if issubclass(new, Union[old.__constraints__]):
|
||||
# Specializing a constrained type variable is OK.
|
||||
continue
|
||||
if not issubclass(new, old):
|
||||
# Subscripting a regular Generic subclass.
|
||||
if not self.__parameters__:
|
||||
raise TypeError("%s is not a generic class" % repr(self))
|
||||
alen = len(params)
|
||||
elen = len(self.__parameters__)
|
||||
if alen != elen:
|
||||
raise TypeError(
|
||||
"Cannot substitute %s for %s in %s" %
|
||||
(_type_repr(new), _type_repr(old), self))
|
||||
|
||||
return self.__class__(self.__name__, (self,) + self.__bases__,
|
||||
"Too %s parameters for %s; actual %s, expected %s" %
|
||||
("many" if alen > elen else "few", repr(self), alen, elen))
|
||||
tvars = _type_vars(params)
|
||||
args = params
|
||||
return self.__class__(self.__name__,
|
||||
(self,) + self.__bases__,
|
||||
dict(self.__dict__),
|
||||
parameters=params,
|
||||
tvars=tvars,
|
||||
args=args,
|
||||
origin=self,
|
||||
extra=self.__extra__)
|
||||
|
||||
|
@ -1006,10 +1040,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
|||
# C[X] is a subclass of C[Y] iff X is a subclass of Y.
|
||||
origin = self.__origin__
|
||||
if origin is not None and origin is cls.__origin__:
|
||||
assert len(self.__parameters__) == len(origin.__parameters__)
|
||||
assert len(cls.__parameters__) == len(origin.__parameters__)
|
||||
for p_self, p_cls, p_origin in zip(self.__parameters__,
|
||||
cls.__parameters__,
|
||||
assert len(self.__args__) == len(origin.__parameters__)
|
||||
assert len(cls.__args__) == len(origin.__parameters__)
|
||||
for p_self, p_cls, p_origin in zip(self.__args__,
|
||||
cls.__args__,
|
||||
origin.__parameters__):
|
||||
if isinstance(p_origin, TypeVar):
|
||||
if p_origin.__covariant__:
|
||||
|
@ -1039,6 +1073,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
|||
return issubclass(cls, self.__extra__)
|
||||
|
||||
|
||||
# Prevent checks for Generic to crash when defining Generic.
|
||||
Generic = None
|
||||
|
||||
|
||||
class Generic(metaclass=GenericMeta):
|
||||
"""Abstract base class for generic types.
|
||||
|
||||
|
@ -1053,29 +1091,23 @@ class Generic(metaclass=GenericMeta):
|
|||
|
||||
This class can then be used as follows::
|
||||
|
||||
def lookup_name(mapping: Mapping, key: KT, default: VT) -> VT:
|
||||
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
||||
try:
|
||||
return mapping[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
For clarity the type variables may be redefined, e.g.::
|
||||
|
||||
X = TypeVar('X')
|
||||
Y = TypeVar('Y')
|
||||
def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
|
||||
# Same body as above.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, *args, **kwds):
|
||||
next_in_mro = object
|
||||
# Look for the last occurrence of Generic or Generic[...].
|
||||
for i, c in enumerate(cls.__mro__[:-1]):
|
||||
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
|
||||
next_in_mro = cls.__mro__[i+1]
|
||||
return next_in_mro.__new__(_gorg(cls))
|
||||
if cls.__origin__ is None:
|
||||
return cls.__next_in_mro__.__new__(cls)
|
||||
else:
|
||||
origin = _gorg(cls)
|
||||
obj = cls.__next_in_mro__.__new__(origin)
|
||||
obj.__init__(*args, **kwds)
|
||||
return obj
|
||||
|
||||
|
||||
def cast(typ, val):
|
||||
|
@ -1093,9 +1125,7 @@ def _get_defaults(func):
|
|||
"""Internal helper to extract the default arguments, by name."""
|
||||
code = func.__code__
|
||||
pos_count = code.co_argcount
|
||||
kw_count = code.co_kwonlyargcount
|
||||
arg_names = code.co_varnames
|
||||
kwarg_names = arg_names[pos_count:pos_count + kw_count]
|
||||
arg_names = arg_names[:pos_count]
|
||||
defaults = func.__defaults__ or ()
|
||||
kwdefaults = func.__kwdefaults__
|
||||
|
@ -1148,7 +1178,6 @@ def get_type_hints(obj, globalns=None, localns=None):
|
|||
return hints
|
||||
|
||||
|
||||
# TODO: Also support this as a class decorator.
|
||||
def no_type_check(arg):
|
||||
"""Decorator to indicate that annotations are not type hints.
|
||||
|
||||
|
@ -1183,8 +1212,42 @@ def no_type_check_decorator(decorator):
|
|||
return wrapped_decorator
|
||||
|
||||
|
||||
def _overload_dummy(*args, **kwds):
|
||||
"""Helper for @overload to raise when called."""
|
||||
raise NotImplementedError(
|
||||
"You should not call an overloaded function. "
|
||||
"A series of @overload-decorated functions "
|
||||
"outside a stub module should always be followed "
|
||||
"by an implementation that is not @overload-ed.")
|
||||
|
||||
|
||||
def overload(func):
|
||||
raise RuntimeError("Overloading is only supported in library stubs")
|
||||
"""Decorator for overloaded functions/methods.
|
||||
|
||||
In a stub file, place two or more stub definitions for the same
|
||||
function in a row, each decorated with @overload. For example:
|
||||
|
||||
@overload
|
||||
def utf8(value: None) -> None: ...
|
||||
@overload
|
||||
def utf8(value: bytes) -> bytes: ...
|
||||
@overload
|
||||
def utf8(value: str) -> bytes: ...
|
||||
|
||||
In a non-stub file (i.e. a regular .py file), do the same but
|
||||
follow it with an implementation. The implementation should *not*
|
||||
be decorated with @overload. For example:
|
||||
|
||||
@overload
|
||||
def utf8(value: None) -> None: ...
|
||||
@overload
|
||||
def utf8(value: bytes) -> bytes: ...
|
||||
@overload
|
||||
def utf8(value: str) -> bytes: ...
|
||||
def utf8(value):
|
||||
# implementation goes here
|
||||
"""
|
||||
return _overload_dummy
|
||||
|
||||
|
||||
class _ProtocolMeta(GenericMeta):
|
||||
|
@ -1235,8 +1298,10 @@ class _ProtocolMeta(GenericMeta):
|
|||
attr != '__abstractmethods__' and
|
||||
attr != '_is_protocol' and
|
||||
attr != '__dict__' and
|
||||
attr != '__args__' and
|
||||
attr != '__slots__' and
|
||||
attr != '_get_protocol_attrs' and
|
||||
attr != '__next_in_mro__' and
|
||||
attr != '__parameters__' and
|
||||
attr != '__origin__' and
|
||||
attr != '__module__'):
|
||||
|
@ -1264,16 +1329,25 @@ class _Protocol(metaclass=_ProtocolMeta):
|
|||
Hashable = collections_abc.Hashable # Not generic.
|
||||
|
||||
|
||||
class Awaitable(Generic[T_co], extra=collections_abc.Awaitable):
|
||||
if hasattr(collections_abc, 'Awaitable'):
|
||||
class Awaitable(Generic[T_co], extra=collections_abc.Awaitable):
|
||||
__slots__ = ()
|
||||
else:
|
||||
Awaitable = None
|
||||
|
||||
|
||||
if hasattr(collections_abc, 'AsyncIterable'):
|
||||
|
||||
class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable):
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable):
|
||||
class AsyncIterator(AsyncIterable[T_co],
|
||||
extra=collections_abc.AsyncIterator):
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
class AsyncIterator(AsyncIterable[T_co], extra=collections_abc.AsyncIterator):
|
||||
__slots__ = ()
|
||||
else:
|
||||
AsyncIterable = None
|
||||
AsyncIterator = None
|
||||
|
||||
|
||||
class Iterable(Generic[T_co], extra=collections_abc.Iterable):
|
||||
|
@ -1332,7 +1406,11 @@ class SupportsRound(_Protocol[T_co]):
|
|||
pass
|
||||
|
||||
|
||||
class Reversible(_Protocol[T_co]):
|
||||
if hasattr(collections_abc, 'Reversible'):
|
||||
class Reversible(Iterable[T_co], extra=collections_abc.Reversible):
|
||||
__slots__ = ()
|
||||
else:
|
||||
class Reversible(_Protocol[T_co]):
|
||||
__slots__ = ()
|
||||
|
||||
@abstractmethod
|
||||
|
@ -1360,7 +1438,7 @@ class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet):
|
|||
|
||||
|
||||
# NOTE: Only the value type is covariant.
|
||||
class Mapping(Sized, Iterable[KT], Container[KT], Generic[VT_co],
|
||||
class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co],
|
||||
extra=collections_abc.Mapping):
|
||||
pass
|
||||
|
||||
|
@ -1368,8 +1446,12 @@ class Mapping(Sized, Iterable[KT], Container[KT], Generic[VT_co],
|
|||
class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping):
|
||||
pass
|
||||
|
||||
|
||||
class Sequence(Sized, Iterable[T_co], Container[T_co],
|
||||
if hasattr(collections_abc, 'Reversible'):
|
||||
class Sequence(Sized, Reversible[T_co], Container[T_co],
|
||||
extra=collections_abc.Sequence):
|
||||
pass
|
||||
else:
|
||||
class Sequence(Sized, Iterable[T_co], Container[T_co],
|
||||
extra=collections_abc.Sequence):
|
||||
pass
|
||||
|
||||
|
@ -1436,8 +1518,9 @@ class KeysView(MappingView[KT], AbstractSet[KT],
|
|||
pass
|
||||
|
||||
|
||||
# TODO: Enable Set[Tuple[KT, VT_co]] instead of Generic[KT, VT_co].
|
||||
class ItemsView(MappingView, Generic[KT, VT_co],
|
||||
class ItemsView(MappingView[Tuple[KT, VT_co]],
|
||||
Set[Tuple[KT, VT_co]],
|
||||
Generic[KT, VT_co],
|
||||
extra=collections_abc.ItemsView):
|
||||
pass
|
||||
|
||||
|
@ -1454,6 +1537,13 @@ class Dict(dict, MutableMapping[KT, VT]):
|
|||
"use dict() instead")
|
||||
return dict.__new__(cls, *args, **kwds)
|
||||
|
||||
class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]):
|
||||
|
||||
def __new__(cls, *args, **kwds):
|
||||
if _geqv(cls, DefaultDict):
|
||||
raise TypeError("Type DefaultDict cannot be instantiated; "
|
||||
"use collections.defaultdict() instead")
|
||||
return collections.defaultdict.__new__(cls, *args, **kwds)
|
||||
|
||||
# Determine what base class to use for Generator.
|
||||
if hasattr(collections_abc, 'Generator'):
|
||||
|
|
Loading…
Reference in New Issue