cpython: 9e65bc305a24 (original) (raw)
--- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -378,6 +378,16 @@ class CallableTests(BaseTestCase): with self.assertRaises(TypeError): type(c)()
- def test_callable_wrong_forms(self):
with self.assertRaises(TypeError):[](#l1.8)
Callable[[...], int][](#l1.9)
with self.assertRaises(TypeError):[](#l1.10)
Callable[(), int][](#l1.11)
with self.assertRaises(TypeError):[](#l1.12)
Callable[[()], int][](#l1.13)
with self.assertRaises(TypeError):[](#l1.14)
Callable[[int, 1], 2][](#l1.15)
+ def test_callable_instance_works(self): def f(): pass @@ -1296,9 +1306,10 @@ PY36 = sys.version_info[:2] >= (3, 6) PY36_TESTS = """ from test import ann_module, ann_module2, ann_module3 -from collections import ChainMap -class B: +class A:
+class B(A): x: ClassVar[Optional['B']] = None y: int class CSub(B): @@ -1317,6 +1328,15 @@ if PY36: gth = get_type_hints class GetTypeHintTests(BaseTestCase):
- def test_get_type_hints_from_various_objects(self):
# For invalid objects should fail with TypeError (not AttributeError etc).[](#l1.38)
with self.assertRaises(TypeError):[](#l1.39)
gth(123)[](#l1.40)
with self.assertRaises(TypeError):[](#l1.41)
gth('abc')[](#l1.42)
with self.assertRaises(TypeError):[](#l1.43)
gth(None)[](#l1.44)
+ @skipUnless(PY36, 'Python 3.6 required') def test_get_type_hints_modules(self): self.assertEqual(gth(ann_module), {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str}) @@ -1326,18 +1346,15 @@ class GetTypeHintTests(BaseTestCase): @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]}, {}))[](#l1.53)
self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})')[](#l1.54)
self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type},[](#l1.55)
{}, {}))[](#l1.56)
{'y': Optional[ann_module.C]})[](#l1.57)
self.assertIsInstance(gth(ann_module.j_class), dict)[](#l1.58)
self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type})[](#l1.59) self.assertEqual(gth(ann_module.D),[](#l1.60)
ChainMap({'j': str, 'k': str,[](#l1.61)
'y': Optional[ann_module.C]}, {}))[](#l1.62)
self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {}))[](#l1.63)
{'j': str, 'k': str, 'y': Optional[ann_module.C]})[](#l1.64)
self.assertEqual(gth(ann_module.Y), {'z': int})[](#l1.65) self.assertEqual(gth(ann_module.h_class),[](#l1.66)
ChainMap({}, {'y': Optional[ann_module.C]}, {}))[](#l1.67)
self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str},[](#l1.68)
{}))[](#l1.69)
{'y': Optional[ann_module.C]})[](#l1.70)
self.assertEqual(gth(ann_module.S), {'x': str, 'y': str})[](#l1.71) self.assertEqual(gth(ann_module.foo), {'x': int})[](#l1.72)
@skipUnless(PY36, 'Python 3.6 required') @@ -1355,20 +1372,34 @@ class GetTypeHintTests(BaseTestCase): class Der(ABase): ... self.assertEqual(gth(ABase.meth), {'x': int})
- def test_get_type_hints_for_builins(self):
# Should not fail for built-in classes and functions.[](#l1.80)
self.assertEqual(gth(int), {})[](#l1.81)
self.assertEqual(gth(type), {})[](#l1.82)
self.assertEqual(gth(dir), {})[](#l1.83)
self.assertEqual(gth(len), {})[](#l1.84)
def test_previous_behavior(self): def testf(x, y): ... testf.annotations['x'] = 'int' self.assertEqual(gth(testf), {'x': int})
- def test_get_type_hints_for_object_with_annotations(self):
class A: ...[](#l1.92)
class B: ...[](#l1.93)
b = B()[](#l1.94)
b.__annotations__ = {'x': 'A'}[](#l1.95)
self.assertEqual(gth(b, locals()), {'x': A})[](#l1.96)
+ @skipUnless(PY36, 'Python 3.6 required') def test_get_type_hints_ClassVar(self):
self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__),[](#l1.100)
{'var': typing.ClassVar[ann_module2.CV]})[](#l1.101) self.assertEqual(gth(B, globals()),[](#l1.102)
ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {}))[](#l1.103)
{'y': int, 'x': ClassVar[Optional[B]]})[](#l1.104) self.assertEqual(gth(CSub, globals()),[](#l1.105)
ChainMap({'z': ClassVar[CSub]},[](#l1.106)
{'y': int, 'x': ClassVar[Optional[B]]}, {}))[](#l1.107)
self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{}))[](#l1.108)
{'z': ClassVar[CSub], 'y': int, 'x': ClassVar[Optional[B]]})[](#l1.109)
self.assertEqual(gth(G), {'lst': ClassVar[List[T]]})[](#l1.110)
class CollectionsAbcTests(BaseTestCase):
--- a/Lib/typing.py +++ b/Lib/typing.py @@ -10,8 +10,6 @@ try: import collections.abc as collections_abc except ImportError: import collections as collections_abc # Fallback for PY3.2. -if sys.version_info[:2] >= (3, 3):
Please keep all alphabetized within each category.
@@ -1194,14 +1192,12 @@ class CallableMeta(GenericMeta): # super()._tree_repr() for nice formatting. arg_list = [] for arg in tree[1:]:
if arg == ():[](#l2.16)
arg_list.append('[]')[](#l2.17)
elif not isinstance(arg, tuple):[](#l2.18)
if not isinstance(arg, tuple):[](#l2.19) arg_list.append(_type_repr(arg))[](#l2.20) else:[](#l2.21) arg_list.append(arg[0]._tree_repr(arg))[](#l2.22)
if len(arg_list) == 2:[](#l2.23)
return repr(tree[0]) + '[%s]' % ', '.join(arg_list)[](#l2.24)
if arg_list[0] == '...':[](#l2.25)
return repr(tree[0]) + '[..., %s]' % arg_list[1][](#l2.26) return (repr(tree[0]) +[](#l2.27) '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1]))[](#l2.28)
@@ -1216,26 +1212,22 @@ class CallableMeta(GenericMeta): raise TypeError("Callable must be used as " "Callable[[arg, ...], result].") args, result = parameters
if args is ...:[](#l2.34)
parameters = (..., result)[](#l2.35)
elif args == []:[](#l2.36)
parameters = ((), result)[](#l2.37)
if args is Ellipsis:[](#l2.38)
parameters = (Ellipsis, result)[](#l2.39) else:[](#l2.40) if not isinstance(args, list):[](#l2.41) raise TypeError("Callable[args, result]: args must be a list."[](#l2.42) " Got %.100r." % (args,))[](#l2.43)
parameters = tuple(args) + (result,)[](#l2.44)
parameters = (tuple(args), result)[](#l2.45) return self.__getitem_inner__(parameters)[](#l2.46)
@_tp_cache def getitem_inner(self, parameters):
*args, result = parameters[](#l2.50)
args, result = parameters[](#l2.51) msg = "Callable[args, result]: result must be a type."[](#l2.52) result = _type_check(result, msg)[](#l2.53)
if args == [...,]:[](#l2.54)
if args is Ellipsis:[](#l2.55) return super().__getitem__((_TypingEllipsis, result))[](#l2.56)
if args == [(),]:[](#l2.57)
return super().__getitem__((_TypingEmpty, result))[](#l2.58) msg = "Callable[[arg, ...], result]: each arg must be a type."[](#l2.59) args = tuple(_type_check(arg, msg) for arg in args)[](#l2.60) parameters = args + (result,)[](#l2.61)
@@ -1332,7 +1324,11 @@ def cast(typ, val): def _get_defaults(func): """Internal helper to extract the default arguments, by name."""
- try:
code = func.__code__[](#l2.68)
- except AttributeError:
# Some built-in functions don't have __code__, __defaults__, etc.[](#l2.70)
pos_count = code.co_argcount arg_names = code.co_varnames arg_names = arg_names[:pos_count] @@ -1346,138 +1342,80 @@ def _get_defaults(func): return res -if sys.version_info[:2] >= (3, 3):return {}[](#l2.71)
This is often the same as obj.__annotations__, but it handles[](#l2.83)
forward references encoded as string literals, and if necessary[](#l2.84)
adds Optional[t] if a default value equal to None is set.[](#l2.85)
The argument may be a module, class, method, or function. The annotations[](#l2.87)
are returned as a dictionary, or in the case of a class, a ChainMap of[](#l2.88)
dictionaries.[](#l2.89)
TypeError is raised if the argument is not of a type that can contain[](#l2.91)
annotations, and an empty dictionary is returned if no annotations are[](#l2.92)
present.[](#l2.93)
BEWARE -- the behavior of globalns and localns is counterintuitive[](#l2.95)
(unless you are familiar with how eval() and exec() work). The[](#l2.96)
search order is locals first, then globals.[](#l2.97)
- If no dict arguments are passed, an attempt is made to use the[](#l2.99)
globals from obj, and these are also used as the locals. If the[](#l2.100)
object does not appear to have globals, an exception is raised.[](#l2.101)
- If one dict argument is passed, it is used for both globals and[](#l2.103)
locals.[](#l2.104)
- If two dict arguments are passed, they specify globals and[](#l2.106)
locals, respectively.[](#l2.107)
"""[](#l2.108)
if getattr(obj, '__no_type_check__', None):[](#l2.110)
return {}[](#l2.111)
if globalns is None:[](#l2.112)
globalns = getattr(obj, '__globals__', {})[](#l2.113)
if localns is None:[](#l2.114)
localns = globalns[](#l2.115)
elif localns is None:[](#l2.116)
+def get_type_hints(obj, globalns=None, localns=None):
- 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.
- The argument may be a module, class, method, or function. The annotations
- are returned as a dictionary. For classes, annotations include also
- inherited members.
- TypeError is raised if the argument is not of a type that can contain
- annotations, and an empty dictionary is returned if no annotations are
- present.
- 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.
globals from obj, and these are also used as the locals. If the[](#l2.137)
object does not appear to have globals, an exception is raised.[](#l2.138)
- if getattr(obj, 'no_type_check', None):
return {}[](#l2.148)
- if globalns is None:
globalns = getattr(obj, '__globals__', {})[](#l2.150)
if localns is None:[](#l2.151) localns = globalns[](#l2.152)
if (isinstance(obj, types.FunctionType) or[](#l2.154)
isinstance(obj, types.BuiltinFunctionType) or[](#l2.155)
isinstance(obj, types.MethodType)):[](#l2.156)
defaults = _get_defaults(obj)[](#l2.157)
hints = obj.__annotations__[](#l2.158)
for name, value in hints.items():[](#l2.159)
if value is None:[](#l2.160)
value = type(None)[](#l2.161)
if isinstance(value, str):[](#l2.162)
value = _ForwardRef(value)[](#l2.163)
value = _eval_type(value, globalns, localns)[](#l2.164)
if name in defaults and defaults[name] is None:[](#l2.165)
value = Optional[value][](#l2.166)
hints[name] = value[](#l2.167)
return hints[](#l2.168)
if isinstance(obj, types.ModuleType):[](#l2.170)
try:[](#l2.171)
hints = obj.__annotations__[](#l2.172)
except AttributeError:[](#l2.173)
return {}[](#l2.174)
for name, value in hints.items():[](#l2.175)
- elif localns is None:
localns = globalns[](#l2.177)
Classes require a special treatment.
- if isinstance(obj, type):
hints = {}[](#l2.180)
for base in reversed(obj.__mro__):[](#l2.181)
ann = base.__dict__.get('__annotations__', {})[](#l2.182)
for name, value in ann.items():[](#l2.183) if value is None:[](#l2.184) value = type(None)[](#l2.185) if isinstance(value, str):[](#l2.186) value = _ForwardRef(value)[](#l2.187) value = _eval_type(value, globalns, localns)[](#l2.188) hints[name] = value[](#l2.189)
return hints[](#l2.190)
if isinstance(object, type):[](#l2.192)
cmap = None[](#l2.193)
for base in reversed(obj.__mro__):[](#l2.194)
new_map = collections.ChainMap if cmap is None else cmap.new_child[](#l2.195)
try:[](#l2.196)
hints = base.__dict__['__annotations__'][](#l2.197)
except KeyError:[](#l2.198)
cmap = new_map()[](#l2.199)
else:[](#l2.200)
for name, value in hints.items():[](#l2.201)
if value is None:[](#l2.202)
value = type(None)[](#l2.203)
if isinstance(value, str):[](#l2.204)
value = _ForwardRef(value)[](#l2.205)
value = _eval_type(value, globalns, localns)[](#l2.206)
hints[name] = value[](#l2.207)
cmap = new_map(hints)[](#l2.208)
return cmap[](#l2.209)
raise TypeError('{!r} is not a module, class, method, '[](#l2.211)
'or function.'.format(obj))[](#l2.212)
- def get_type_hints(obj, globalns=None, localns=None):
"""Return type hints for a function or method object.[](#l2.216)
This is often the same as obj.__annotations__, but it handles[](#l2.218)
forward references encoded as string literals, and if necessary[](#l2.219)
adds Optional[t] if a default value equal to None is set.[](#l2.220)
BEWARE -- the behavior of globalns and localns is counterintuitive[](#l2.222)
(unless you are familiar with how eval() and exec() work). The[](#l2.223)
search order is locals first, then globals.[](#l2.224)
- If no dict arguments are passed, an attempt is made to use the[](#l2.226)
globals from obj, and these are also used as the locals. If the[](#l2.227)
object does not appear to have globals, an exception is raised.[](#l2.228)
- If one dict argument is passed, it is used for both globals and[](#l2.230)
locals.[](#l2.231)
- If two dict arguments are passed, they specify globals and[](#l2.233)
locals, respectively.[](#l2.234)
"""[](#l2.235)
if getattr(obj, '__no_type_check__', None):[](#l2.236)
return hints[](#l2.237)
- hints = getattr(obj, 'annotations', None)
- if hints is None:
# Return empty annotations for something that _could_ have them.[](#l2.240)
if (isinstance(obj, types.FunctionType) or[](#l2.241)
isinstance(obj, types.BuiltinFunctionType) or[](#l2.242)
isinstance(obj, types.MethodType) or[](#l2.243)
isinstance(obj, types.ModuleType)):[](#l2.244) return {}[](#l2.245)
if globalns is None:[](#l2.246)
globalns = getattr(obj, '__globals__', {})[](#l2.247)
if localns is None:[](#l2.248)
localns = globalns[](#l2.249)
elif localns is None:[](#l2.250)
localns = globalns[](#l2.251)
defaults = _get_defaults(obj)[](#l2.252)
hints = dict(obj.__annotations__)[](#l2.253)
for name, value in hints.items():[](#l2.254)
if isinstance(value, str):[](#l2.255)
value = _ForwardRef(value)[](#l2.256)
value = _eval_type(value, globalns, localns)[](#l2.257)
if name in defaults and defaults[name] is None:[](#l2.258)
value = Optional[value][](#l2.259)
hints[name] = value[](#l2.260)
return hints[](#l2.261)
else:[](#l2.262)
raise TypeError('{!r} is not a module, class, method, '[](#l2.263)
'or function.'.format(obj))[](#l2.264)
- defaults = _get_defaults(obj)
- hints = dict(hints)
- for name, value in hints.items():
if value is None:[](#l2.268)
value = type(None)[](#l2.269)
if isinstance(value, str):[](#l2.270)
value = _ForwardRef(value)[](#l2.271)
value = _eval_type(value, globalns, localns)[](#l2.272)
if name in defaults and defaults[name] is None:[](#l2.273)
value = Optional[value][](#l2.274)
hints[name] = value[](#l2.275)
- return hints
def no_type_check(arg): @@ -2160,7 +2098,7 @@ class TextIO(IO[str]): pass @abstractproperty