mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #28079: Update typing and test typing from python/typing repo.
Ivan Levkivskyi (3.5 version)
This commit is contained in:
parent
0e0cfd7135
commit
0a6976da10
2 changed files with 409 additions and 65 deletions
|
@ -9,9 +9,9 @@ from typing import Any
|
||||||
from typing import TypeVar, AnyStr
|
from typing import TypeVar, AnyStr
|
||||||
from typing import T, KT, VT # Not in __all__.
|
from typing import T, KT, VT # Not in __all__.
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
from typing import Tuple
|
from typing import Tuple, List
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Generic
|
from typing import Generic, ClassVar
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from typing import get_type_hints
|
from typing import get_type_hints
|
||||||
from typing import no_type_check, no_type_check_decorator
|
from typing import no_type_check, no_type_check_decorator
|
||||||
|
@ -827,6 +827,43 @@ class GenericTests(BaseTestCase):
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception):
|
||||||
D[T]
|
D[T]
|
||||||
|
|
||||||
|
class ClassVarTests(BaseTestCase):
|
||||||
|
|
||||||
|
def test_basics(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ClassVar[1]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ClassVar[int, str]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ClassVar[int][str]
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
self.assertEqual(repr(ClassVar), 'typing.ClassVar')
|
||||||
|
cv = ClassVar[int]
|
||||||
|
self.assertEqual(repr(cv), 'typing.ClassVar[int]')
|
||||||
|
cv = ClassVar[Employee]
|
||||||
|
self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__)
|
||||||
|
|
||||||
|
def test_cannot_subclass(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(type(ClassVar)):
|
||||||
|
pass
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
class C(type(ClassVar[int])):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_cannot_init(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
type(ClassVar)()
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
type(ClassVar[Optional[int]])()
|
||||||
|
|
||||||
|
def test_no_isinstance(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
isinstance(1, ClassVar[int])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
issubclass(int, ClassVar)
|
||||||
|
|
||||||
|
|
||||||
class VarianceTests(BaseTestCase):
|
class VarianceTests(BaseTestCase):
|
||||||
|
|
||||||
|
@ -1119,6 +1156,84 @@ class AsyncIteratorWrapper(typing.AsyncIterator[T_a]):
|
||||||
if PY35:
|
if PY35:
|
||||||
exec(PY35_TESTS)
|
exec(PY35_TESTS)
|
||||||
|
|
||||||
|
PY36 = sys.version_info[:2] >= (3, 6)
|
||||||
|
|
||||||
|
PY36_TESTS = """
|
||||||
|
from test import ann_module, ann_module2, ann_module3
|
||||||
|
from collections import ChainMap
|
||||||
|
|
||||||
|
class B:
|
||||||
|
x: ClassVar[Optional['B']] = None
|
||||||
|
y: int
|
||||||
|
class CSub(B):
|
||||||
|
z: ClassVar['CSub'] = B()
|
||||||
|
class G(Generic[T]):
|
||||||
|
lst: ClassVar[List[T]] = []
|
||||||
|
|
||||||
|
class CoolEmployee(NamedTuple):
|
||||||
|
name: str
|
||||||
|
cool: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PY36:
|
||||||
|
exec(PY36_TESTS)
|
||||||
|
|
||||||
|
gth = get_type_hints
|
||||||
|
|
||||||
|
class GetTypeHintTests(BaseTestCase):
|
||||||
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
|
def test_get_type_hints_modules(self):
|
||||||
|
self.assertEqual(gth(ann_module), {'x': int, 'y': str})
|
||||||
|
self.assertEqual(gth(ann_module2), {})
|
||||||
|
self.assertEqual(gth(ann_module3), {})
|
||||||
|
|
||||||
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
|
def test_get_type_hints_classes(self):
|
||||||
|
self.assertEqual(gth(ann_module.C, ann_module.__dict__),
|
||||||
|
ChainMap({'y': Optional[ann_module.C]}, {}))
|
||||||
|
self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})')
|
||||||
|
self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type},
|
||||||
|
{}, {}))
|
||||||
|
self.assertEqual(gth(ann_module.D),
|
||||||
|
ChainMap({'j': str, 'k': str,
|
||||||
|
'y': Optional[ann_module.C]}, {}))
|
||||||
|
self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {}))
|
||||||
|
self.assertEqual(gth(ann_module.h_class),
|
||||||
|
ChainMap({}, {'y': Optional[ann_module.C]}, {}))
|
||||||
|
self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str},
|
||||||
|
{}))
|
||||||
|
self.assertEqual(gth(ann_module.foo), {'x': int})
|
||||||
|
|
||||||
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
|
def test_respect_no_type_check(self):
|
||||||
|
@no_type_check
|
||||||
|
class NoTpCheck:
|
||||||
|
class Inn:
|
||||||
|
def __init__(self, x: 'not a type'): ...
|
||||||
|
self.assertTrue(NoTpCheck.__no_type_check__)
|
||||||
|
self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__)
|
||||||
|
self.assertEqual(gth(ann_module2.NTC.meth), {})
|
||||||
|
class ABase(Generic[T]):
|
||||||
|
def meth(x: int): ...
|
||||||
|
@no_type_check
|
||||||
|
class Der(ABase): ...
|
||||||
|
self.assertEqual(gth(ABase.meth), {'x': int})
|
||||||
|
|
||||||
|
|
||||||
|
def test_previous_behavior(self):
|
||||||
|
def testf(x, y): ...
|
||||||
|
testf.__annotations__['x'] = 'int'
|
||||||
|
self.assertEqual(gth(testf), {'x': int})
|
||||||
|
|
||||||
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
|
def test_get_type_hints_ClassVar(self):
|
||||||
|
self.assertEqual(gth(B, globals()),
|
||||||
|
ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {}))
|
||||||
|
self.assertEqual(gth(CSub, globals()),
|
||||||
|
ChainMap({'z': ClassVar[CSub]},
|
||||||
|
{'y': int, 'x': ClassVar[Optional[B]]}, {}))
|
||||||
|
self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{}))
|
||||||
|
|
||||||
|
|
||||||
class CollectionsAbcTests(BaseTestCase):
|
class CollectionsAbcTests(BaseTestCase):
|
||||||
|
|
||||||
|
@ -1426,6 +1541,18 @@ class TypeTests(BaseTestCase):
|
||||||
|
|
||||||
joe = new_user(BasicUser)
|
joe = new_user(BasicUser)
|
||||||
|
|
||||||
|
def test_type_optional(self):
|
||||||
|
A = Optional[Type[BaseException]]
|
||||||
|
|
||||||
|
def foo(a: A) -> Optional[BaseException]:
|
||||||
|
if a is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return a()
|
||||||
|
|
||||||
|
assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt)
|
||||||
|
assert foo(None) is None
|
||||||
|
|
||||||
|
|
||||||
class NewTypeTests(BaseTestCase):
|
class NewTypeTests(BaseTestCase):
|
||||||
|
|
||||||
|
@ -1463,6 +1590,17 @@ class NamedTupleTests(BaseTestCase):
|
||||||
self.assertEqual(Emp._fields, ('name', 'id'))
|
self.assertEqual(Emp._fields, ('name', 'id'))
|
||||||
self.assertEqual(Emp._field_types, dict(name=str, id=int))
|
self.assertEqual(Emp._field_types, dict(name=str, id=int))
|
||||||
|
|
||||||
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
|
def test_annotation_usage(self):
|
||||||
|
tim = CoolEmployee('Tim', 9000)
|
||||||
|
self.assertIsInstance(tim, CoolEmployee)
|
||||||
|
self.assertIsInstance(tim, tuple)
|
||||||
|
self.assertEqual(tim.name, 'Tim')
|
||||||
|
self.assertEqual(tim.cool, 9000)
|
||||||
|
self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
|
||||||
|
self.assertEqual(CoolEmployee._fields, ('name', 'cool'))
|
||||||
|
self.assertEqual(CoolEmployee._field_types, dict(name=str, cool=int))
|
||||||
|
|
||||||
def test_pickle(self):
|
def test_pickle(self):
|
||||||
global Emp # pickle wants to reference the class by name
|
global Emp # pickle wants to reference the class by name
|
||||||
Emp = NamedTuple('Emp', [('name', str), ('id', int)])
|
Emp = NamedTuple('Emp', [('name', str), ('id', int)])
|
||||||
|
|
332
Lib/typing.py
332
Lib/typing.py
|
@ -10,6 +10,8 @@ try:
|
||||||
import collections.abc as collections_abc
|
import collections.abc as collections_abc
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import collections as collections_abc # Fallback for PY3.2.
|
import collections as collections_abc # Fallback for PY3.2.
|
||||||
|
if sys.version_info[:2] >= (3, 3):
|
||||||
|
from collections import ChainMap
|
||||||
|
|
||||||
|
|
||||||
# Please keep __all__ alphabetized within each category.
|
# Please keep __all__ alphabetized within each category.
|
||||||
|
@ -17,6 +19,7 @@ __all__ = [
|
||||||
# Super-special typing primitives.
|
# Super-special typing primitives.
|
||||||
'Any',
|
'Any',
|
||||||
'Callable',
|
'Callable',
|
||||||
|
'ClassVar',
|
||||||
'Generic',
|
'Generic',
|
||||||
'Optional',
|
'Optional',
|
||||||
'Tuple',
|
'Tuple',
|
||||||
|
@ -270,7 +273,7 @@ class _TypeAlias:
|
||||||
|
|
||||||
def _get_type_vars(types, tvars):
|
def _get_type_vars(types, tvars):
|
||||||
for t in types:
|
for t in types:
|
||||||
if isinstance(t, TypingMeta):
|
if isinstance(t, TypingMeta) or isinstance(t, _ClassVar):
|
||||||
t._get_type_vars(tvars)
|
t._get_type_vars(tvars)
|
||||||
|
|
||||||
|
|
||||||
|
@ -281,7 +284,7 @@ def _type_vars(types):
|
||||||
|
|
||||||
|
|
||||||
def _eval_type(t, globalns, localns):
|
def _eval_type(t, globalns, localns):
|
||||||
if isinstance(t, TypingMeta):
|
if isinstance(t, TypingMeta) or isinstance(t, _ClassVar):
|
||||||
return t._eval_type(globalns, localns)
|
return t._eval_type(globalns, localns)
|
||||||
else:
|
else:
|
||||||
return t
|
return t
|
||||||
|
@ -1114,6 +1117,67 @@ class Generic(metaclass=GenericMeta):
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
class _ClassVar(metaclass=TypingMeta, _root=True):
|
||||||
|
"""Special type construct to mark class variables.
|
||||||
|
|
||||||
|
An annotation wrapped in ClassVar indicates that a given
|
||||||
|
attribute is intended to be used as a class variable and
|
||||||
|
should not be set on instances of that class. Usage::
|
||||||
|
|
||||||
|
class Starship:
|
||||||
|
stats: ClassVar[Dict[str, int]] = {} # class variable
|
||||||
|
damage: int = 10 # instance variable
|
||||||
|
|
||||||
|
ClassVar accepts only types and cannot be further subscribed.
|
||||||
|
|
||||||
|
Note that ClassVar is not a class itself, and should not
|
||||||
|
be used with isinstance() or issubclass().
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tp=None, _root=False):
|
||||||
|
cls = type(self)
|
||||||
|
if _root:
|
||||||
|
self.__type__ = tp
|
||||||
|
else:
|
||||||
|
raise TypeError('Cannot initialize {}'.format(cls.__name__[1:]))
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
cls = type(self)
|
||||||
|
if self.__type__ is None:
|
||||||
|
return cls(_type_check(item,
|
||||||
|
'{} accepts only types.'.format(cls.__name__[1:])),
|
||||||
|
_root=True)
|
||||||
|
raise TypeError('{} cannot be further subscripted'
|
||||||
|
.format(cls.__name__[1:]))
|
||||||
|
|
||||||
|
def _eval_type(self, globalns, localns):
|
||||||
|
return type(self)(_eval_type(self.__type__, globalns, localns),
|
||||||
|
_root=True)
|
||||||
|
|
||||||
|
def _get_type_vars(self, tvars):
|
||||||
|
if self.__type__:
|
||||||
|
_get_type_vars(self.__type__, tvars)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
cls = type(self)
|
||||||
|
if not self.__type__:
|
||||||
|
return '{}.{}'.format(cls.__module__, cls.__name__[1:])
|
||||||
|
return '{}.{}[{}]'.format(cls.__module__, cls.__name__[1:],
|
||||||
|
_type_repr(self.__type__))
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((type(self).__name__, self.__type__))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, _ClassVar):
|
||||||
|
return NotImplemented
|
||||||
|
if self.__type__ is not None:
|
||||||
|
return self.__type__ == other.__type__
|
||||||
|
return self is other
|
||||||
|
|
||||||
|
ClassVar = _ClassVar(_root=True)
|
||||||
|
|
||||||
|
|
||||||
def cast(typ, val):
|
def cast(typ, val):
|
||||||
"""Cast a value to a type.
|
"""Cast a value to a type.
|
||||||
|
|
||||||
|
@ -1141,62 +1205,167 @@ def _get_defaults(func):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def get_type_hints(obj, globalns=None, localns=None):
|
if sys.version_info[:2] >= (3, 3):
|
||||||
"""Return type hints for a function or method object.
|
def get_type_hints(obj, globalns=None, localns=None):
|
||||||
|
"""Return type hints for an object.
|
||||||
|
|
||||||
This is often the same as obj.__annotations__, but it handles
|
This is often the same as obj.__annotations__, but it handles
|
||||||
forward references encoded as string literals, and if necessary
|
forward references encoded as string literals, and if necessary
|
||||||
adds Optional[t] if a default value equal to None is set.
|
adds Optional[t] if a default value equal to None is set.
|
||||||
|
|
||||||
BEWARE -- the behavior of globalns and localns is counterintuitive
|
The argument may be a module, class, method, or function. The annotations
|
||||||
(unless you are familiar with how eval() and exec() work). The
|
are returned as a dictionary, or in the case of a class, a ChainMap of
|
||||||
search order is locals first, then globals.
|
dictionaries.
|
||||||
|
|
||||||
- If no dict arguments are passed, an attempt is made to use the
|
TypeError is raised if the argument is not of a type that can contain
|
||||||
globals from obj, and these are also used as the locals. If the
|
annotations, and an empty dictionary is returned if no annotations are
|
||||||
object does not appear to have globals, an exception is raised.
|
present.
|
||||||
|
|
||||||
- If one dict argument is passed, it is used for both globals and
|
BEWARE -- the behavior of globalns and localns is counterintuitive
|
||||||
locals.
|
(unless you are familiar with how eval() and exec() work). The
|
||||||
|
search order is locals first, then globals.
|
||||||
|
|
||||||
- If two dict arguments are passed, they specify globals and
|
- If no dict arguments are passed, an attempt is made to use the
|
||||||
locals, respectively.
|
globals from obj, and these are also used as the locals. If the
|
||||||
"""
|
object does not appear to have globals, an exception is raised.
|
||||||
if getattr(obj, '__no_type_check__', None):
|
|
||||||
return {}
|
- If one dict argument is passed, it is used for both globals and
|
||||||
if globalns is None:
|
locals.
|
||||||
globalns = getattr(obj, '__globals__', {})
|
|
||||||
if localns is None:
|
- If two dict arguments are passed, they specify globals and
|
||||||
|
locals, respectively.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if getattr(obj, '__no_type_check__', None):
|
||||||
|
return {}
|
||||||
|
if globalns is None:
|
||||||
|
globalns = getattr(obj, '__globals__', {})
|
||||||
|
if localns is None:
|
||||||
|
localns = globalns
|
||||||
|
elif localns is None:
|
||||||
localns = globalns
|
localns = globalns
|
||||||
elif localns is None:
|
|
||||||
localns = globalns
|
if (isinstance(obj, types.FunctionType) or
|
||||||
defaults = _get_defaults(obj)
|
isinstance(obj, types.BuiltinFunctionType) or
|
||||||
hints = dict(obj.__annotations__)
|
isinstance(obj, types.MethodType)):
|
||||||
for name, value in hints.items():
|
defaults = _get_defaults(obj)
|
||||||
if isinstance(value, str):
|
hints = obj.__annotations__
|
||||||
value = _ForwardRef(value)
|
for name, value in hints.items():
|
||||||
value = _eval_type(value, globalns, localns)
|
if value is None:
|
||||||
if name in defaults and defaults[name] is None:
|
value = type(None)
|
||||||
value = Optional[value]
|
if isinstance(value, str):
|
||||||
hints[name] = value
|
value = _ForwardRef(value)
|
||||||
return hints
|
value = _eval_type(value, globalns, localns)
|
||||||
|
if name in defaults and defaults[name] is None:
|
||||||
|
value = Optional[value]
|
||||||
|
hints[name] = value
|
||||||
|
return hints
|
||||||
|
|
||||||
|
if isinstance(obj, types.ModuleType):
|
||||||
|
try:
|
||||||
|
hints = obj.__annotations__
|
||||||
|
except AttributeError:
|
||||||
|
return {}
|
||||||
|
# we keep only those annotations that can be accessed on module
|
||||||
|
members = obj.__dict__
|
||||||
|
hints = {name: value for name, value in hints.items()
|
||||||
|
if name in members}
|
||||||
|
for name, value in hints.items():
|
||||||
|
if value is None:
|
||||||
|
value = type(None)
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = _ForwardRef(value)
|
||||||
|
value = _eval_type(value, globalns, localns)
|
||||||
|
hints[name] = value
|
||||||
|
return hints
|
||||||
|
|
||||||
|
if isinstance(object, type):
|
||||||
|
cmap = None
|
||||||
|
for base in reversed(obj.__mro__):
|
||||||
|
new_map = collections.ChainMap if cmap is None else cmap.new_child
|
||||||
|
try:
|
||||||
|
hints = base.__dict__['__annotations__']
|
||||||
|
except KeyError:
|
||||||
|
cmap = new_map()
|
||||||
|
else:
|
||||||
|
for name, value in hints.items():
|
||||||
|
if value is None:
|
||||||
|
value = type(None)
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = _ForwardRef(value)
|
||||||
|
value = _eval_type(value, globalns, localns)
|
||||||
|
hints[name] = value
|
||||||
|
cmap = new_map(hints)
|
||||||
|
return cmap
|
||||||
|
|
||||||
|
raise TypeError('{!r} is not a module, class, method, '
|
||||||
|
'or function.'.format(obj))
|
||||||
|
|
||||||
|
else:
|
||||||
|
def get_type_hints(obj, globalns=None, localns=None):
|
||||||
|
"""Return type hints for a function or method object.
|
||||||
|
|
||||||
|
This is often the same as obj.__annotations__, but it handles
|
||||||
|
forward references encoded as string literals, and if necessary
|
||||||
|
adds Optional[t] if a default value equal to None is set.
|
||||||
|
|
||||||
|
BEWARE -- the behavior of globalns and localns is counterintuitive
|
||||||
|
(unless you are familiar with how eval() and exec() work). The
|
||||||
|
search order is locals first, then globals.
|
||||||
|
|
||||||
|
- If no dict arguments are passed, an attempt is made to use the
|
||||||
|
globals from obj, and these are also used as the locals. If the
|
||||||
|
object does not appear to have globals, an exception is raised.
|
||||||
|
|
||||||
|
- If one dict argument is passed, it is used for both globals and
|
||||||
|
locals.
|
||||||
|
|
||||||
|
- If two dict arguments are passed, they specify globals and
|
||||||
|
locals, respectively.
|
||||||
|
"""
|
||||||
|
if getattr(obj, '__no_type_check__', None):
|
||||||
|
return {}
|
||||||
|
if globalns is None:
|
||||||
|
globalns = getattr(obj, '__globals__', {})
|
||||||
|
if localns is None:
|
||||||
|
localns = globalns
|
||||||
|
elif localns is None:
|
||||||
|
localns = globalns
|
||||||
|
defaults = _get_defaults(obj)
|
||||||
|
hints = dict(obj.__annotations__)
|
||||||
|
for name, value in hints.items():
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = _ForwardRef(value)
|
||||||
|
value = _eval_type(value, globalns, localns)
|
||||||
|
if name in defaults and defaults[name] is None:
|
||||||
|
value = Optional[value]
|
||||||
|
hints[name] = value
|
||||||
|
return hints
|
||||||
|
|
||||||
|
|
||||||
def no_type_check(arg):
|
def no_type_check(arg):
|
||||||
"""Decorator to indicate that annotations are not type hints.
|
"""Decorator to indicate that annotations are not type hints.
|
||||||
|
|
||||||
The argument must be a class or function; if it is a class, it
|
The argument must be a class or function; if it is a class, it
|
||||||
applies recursively to all methods defined in that class (but not
|
applies recursively to all methods and classes defined in that class
|
||||||
to methods defined in its superclasses or subclasses).
|
(but not to methods defined in its superclasses or subclasses).
|
||||||
|
|
||||||
This mutates the function(s) in place.
|
This mutates the function(s) or class(es) in place.
|
||||||
"""
|
"""
|
||||||
if isinstance(arg, type):
|
if isinstance(arg, type):
|
||||||
for obj in arg.__dict__.values():
|
arg_attrs = arg.__dict__.copy()
|
||||||
|
for attr, val in arg.__dict__.items():
|
||||||
|
if val in arg.__bases__:
|
||||||
|
arg_attrs.pop(attr)
|
||||||
|
for obj in arg_attrs.values():
|
||||||
if isinstance(obj, types.FunctionType):
|
if isinstance(obj, types.FunctionType):
|
||||||
obj.__no_type_check__ = True
|
obj.__no_type_check__ = True
|
||||||
else:
|
if isinstance(obj, type):
|
||||||
|
no_type_check(obj)
|
||||||
|
try:
|
||||||
arg.__no_type_check__ = True
|
arg.__no_type_check__ = True
|
||||||
|
except TypeError: # built-in classes
|
||||||
|
pass
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
@ -1300,6 +1469,8 @@ class _ProtocolMeta(GenericMeta):
|
||||||
else:
|
else:
|
||||||
if (not attr.startswith('_abc_') and
|
if (not attr.startswith('_abc_') and
|
||||||
attr != '__abstractmethods__' and
|
attr != '__abstractmethods__' and
|
||||||
|
attr != '__annotations__' and
|
||||||
|
attr != '__weakref__' and
|
||||||
attr != '_is_protocol' and
|
attr != '_is_protocol' and
|
||||||
attr != '__dict__' and
|
attr != '__dict__' and
|
||||||
attr != '__args__' and
|
attr != '__args__' and
|
||||||
|
@ -1605,7 +1776,7 @@ CT_co = TypeVar('CT_co', covariant=True, bound=type)
|
||||||
|
|
||||||
|
|
||||||
# This is not a real generic class. Don't use outside annotations.
|
# This is not a real generic class. Don't use outside annotations.
|
||||||
class Type(type, Generic[CT_co], extra=type):
|
class Type(Generic[CT_co], extra=type):
|
||||||
"""A special construct usable to annotate class objects.
|
"""A special construct usable to annotate class objects.
|
||||||
|
|
||||||
For example, suppose we have the following classes::
|
For example, suppose we have the following classes::
|
||||||
|
@ -1630,31 +1801,66 @@ class Type(type, Generic[CT_co], extra=type):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def NamedTuple(typename, fields):
|
def _make_nmtuple(name, types):
|
||||||
"""Typed version of namedtuple.
|
nm_tpl = collections.namedtuple(name, [n for n, t in types])
|
||||||
|
nm_tpl._field_types = dict(types)
|
||||||
Usage::
|
|
||||||
|
|
||||||
Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)])
|
|
||||||
|
|
||||||
This is equivalent to::
|
|
||||||
|
|
||||||
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
|
||||||
|
|
||||||
The resulting class has one extra attribute: _field_types,
|
|
||||||
giving a dict mapping field names to types. (The field names
|
|
||||||
are in the _fields attribute, which is part of the namedtuple
|
|
||||||
API.)
|
|
||||||
"""
|
|
||||||
fields = [(n, t) for n, t in fields]
|
|
||||||
cls = collections.namedtuple(typename, [n for n, t in fields])
|
|
||||||
cls._field_types = dict(fields)
|
|
||||||
# Set the module to the caller's module (otherwise it'd be 'typing').
|
|
||||||
try:
|
try:
|
||||||
cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__')
|
nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
pass
|
pass
|
||||||
return cls
|
return nm_tpl
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info[:2] >= (3, 6):
|
||||||
|
class NamedTupleMeta(type):
|
||||||
|
|
||||||
|
def __new__(cls, typename, bases, ns, *, _root=False):
|
||||||
|
if _root:
|
||||||
|
return super().__new__(cls, typename, bases, ns)
|
||||||
|
types = ns.get('__annotations__', {})
|
||||||
|
return _make_nmtuple(typename, types.items())
|
||||||
|
|
||||||
|
class NamedTuple(metaclass=NamedTupleMeta, _root=True):
|
||||||
|
"""Typed version of namedtuple.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
class Employee(NamedTuple):
|
||||||
|
name: str
|
||||||
|
id: int
|
||||||
|
|
||||||
|
This is equivalent to::
|
||||||
|
|
||||||
|
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||||
|
|
||||||
|
The resulting class has one extra attribute: _field_types,
|
||||||
|
giving a dict mapping field names to types. (The field names
|
||||||
|
are in the _fields attribute, which is part of the namedtuple
|
||||||
|
API.) Backward-compatible usage::
|
||||||
|
|
||||||
|
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __new__(self, typename, fields):
|
||||||
|
return _make_nmtuple(typename, fields)
|
||||||
|
else:
|
||||||
|
def NamedTuple(typename, fields):
|
||||||
|
"""Typed version of namedtuple.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)])
|
||||||
|
|
||||||
|
This is equivalent to::
|
||||||
|
|
||||||
|
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||||
|
|
||||||
|
The resulting class has one extra attribute: _field_types,
|
||||||
|
giving a dict mapping field names to types. (The field names
|
||||||
|
are in the _fields attribute, which is part of the namedtuple
|
||||||
|
API.)
|
||||||
|
"""
|
||||||
|
return _make_nmtuple(typename, fields)
|
||||||
|
|
||||||
|
|
||||||
def NewType(name, tp):
|
def NewType(name, tp):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue