mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	bpo-38605: Make 'from __future__ import annotations' the default (GH-20434)
The hard part was making all the tests pass; there are some subtle issues here, because apparently the future import wasn't tested very thoroughly in previous Python versions. For example, `inspect.signature()` returned type objects normally (except for forward references), but strings with the future import. We changed it to try and return type objects by calling `typing.get_type_hints()`, but fall back on returning strings if that function fails (which it may do if there are future references in the annotations that require passing in a specific namespace to resolve).
This commit is contained in:
		
							parent
							
								
									bef7d299eb
								
							
						
					
					
						commit
						044a1048ca
					
				
					 27 changed files with 403 additions and 299 deletions
				
			
		|  | @ -610,13 +610,9 @@ following the parameter name.  Any parameter may have an annotation, even those | |||
| ``*identifier`` or ``**identifier``.  Functions may have "return" annotation of | ||||
| the form "``-> expression``" after the parameter list.  These annotations can be | ||||
| any valid Python expression.  The presence of annotations does not change the | ||||
| semantics of a function.  The annotation values are available as values of | ||||
| a dictionary keyed by the parameters' names in the :attr:`__annotations__` | ||||
| attribute of the function object.  If the ``annotations`` import from | ||||
| :mod:`__future__` is used, annotations are preserved as strings at runtime which | ||||
| enables postponed evaluation.  Otherwise, they are evaluated when the function | ||||
| definition is executed.  In this case annotations may be evaluated in | ||||
| a different order than they appear in the source code. | ||||
| semantics of a function.  The annotation values are available as string values | ||||
| in a dictionary keyed by the parameters' names in the :attr:`__annotations__` | ||||
| attribute of the function object. | ||||
| 
 | ||||
| .. index:: pair: lambda; expression | ||||
| 
 | ||||
|  |  | |||
|  | @ -70,6 +70,23 @@ Summary -- Release highlights | |||
| New Features | ||||
| ============ | ||||
| 
 | ||||
| .. _whatsnew310-pep563: | ||||
| 
 | ||||
| PEP 563: Postponed Evaluation of Annotations Becomes Default | ||||
| ------------------------------------------------------------ | ||||
| 
 | ||||
| In Python 3.7, postponed evaluation of annotations was added, | ||||
| to be enabled with a ``from __future__ import annotations`` | ||||
| directive.  In 3.10 this became the default behavior, even | ||||
| without that future directive.  With this being default, all | ||||
| annotations stored in :attr:`__annotations__` will be strings. | ||||
| If needed, annotations can be resolved at runtime using | ||||
| :func:`typing.get_type_hints`.  See :pep:`563` for a full | ||||
| description.  Also, the :func:`inspect.signature` will try to | ||||
| resolve types from now on, and when it fails it will fall back to | ||||
| showing the string annotations.  (Contributed by Batuhan Taskaya | ||||
| in :issue:`38605`.) | ||||
| 
 | ||||
| * The :class:`int` type has a new method :meth:`int.bit_count`, returning the | ||||
|   number of ones in the binary expansion of a given integer, also known | ||||
|   as the population count. (Contributed by Niklas Fiekas in :issue:`29882`.) | ||||
|  |  | |||
|  | @ -399,8 +399,10 @@ def _create_fn(name, args, body, *, globals=None, locals=None, | |||
| 
 | ||||
|     ns = {} | ||||
|     exec(txt, globals, ns) | ||||
|     return ns['__create_fn__'](**locals) | ||||
| 
 | ||||
|     func = ns['__create_fn__'](**locals) | ||||
|     for arg, annotation in func.__annotations__.copy().items(): | ||||
|         func.__annotations__[arg] = locals[annotation] | ||||
|     return func | ||||
| 
 | ||||
| def _field_assign(frozen, name, value, self_name): | ||||
|     # If we're a frozen class, then assign to our fields in __init__ | ||||
|  | @ -651,6 +653,11 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): | |||
|     # a eval() penalty for every single field of every dataclass | ||||
|     # that's defined.  It was judged not worth it. | ||||
| 
 | ||||
|     # Strip away the extra quotes as a result of double-stringifying when the | ||||
|     # 'annotations' feature became default. | ||||
|     if annotation.startswith(("'", '"')) and annotation.endswith(("'", '"')): | ||||
|         annotation = annotation[1:-1] | ||||
| 
 | ||||
|     match = _MODULE_IDENTIFIER_RE.match(annotation) | ||||
|     if match: | ||||
|         ns = None | ||||
|  | @ -991,7 +998,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen): | |||
|     if not getattr(cls, '__doc__'): | ||||
|         # Create a class doc-string. | ||||
|         cls.__doc__ = (cls.__name__ + | ||||
|                        str(inspect.signature(cls)).replace(' -> None', '')) | ||||
|                        str(inspect.signature(cls)).replace(' -> NoneType', '')) | ||||
| 
 | ||||
|     abc.update_abstractmethods(cls) | ||||
| 
 | ||||
|  |  | |||
|  | @ -45,6 +45,7 @@ | |||
| import tokenize | ||||
| import token | ||||
| import types | ||||
| import typing | ||||
| import warnings | ||||
| import functools | ||||
| import builtins | ||||
|  | @ -1877,7 +1878,10 @@ def _signature_is_functionlike(obj): | |||
|     code = getattr(obj, '__code__', None) | ||||
|     defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... | ||||
|     kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here | ||||
|     annotations = getattr(obj, '__annotations__', None) | ||||
|     try: | ||||
|         annotations = _get_type_hints(obj) | ||||
|     except AttributeError: | ||||
|         annotations = None | ||||
| 
 | ||||
|     return (isinstance(code, types.CodeType) and | ||||
|             isinstance(name, str) and | ||||
|  | @ -2118,6 +2122,16 @@ def p(name_node, default_node, default=empty): | |||
| 
 | ||||
|     return cls(parameters, return_annotation=cls.empty) | ||||
| 
 | ||||
| def _get_type_hints(func): | ||||
|     try: | ||||
|         return typing.get_type_hints(func) | ||||
|     except Exception: | ||||
|         # First, try to use the get_type_hints to resolve | ||||
|         # annotations. But for keeping the behavior intact | ||||
|         # if there was a problem with that (like the namespace | ||||
|         # can't resolve some annotation) continue to use | ||||
|         # string annotations | ||||
|         return func.__annotations__ | ||||
| 
 | ||||
| def _signature_from_builtin(cls, func, skip_bound_arg=True): | ||||
|     """Private helper function to get signature for | ||||
|  | @ -2161,7 +2175,8 @@ def _signature_from_function(cls, func, skip_bound_arg=True): | |||
|     positional = arg_names[:pos_count] | ||||
|     keyword_only_count = func_code.co_kwonlyargcount | ||||
|     keyword_only = arg_names[pos_count:pos_count + keyword_only_count] | ||||
|     annotations = func.__annotations__ | ||||
|     annotations = _get_type_hints(func) | ||||
| 
 | ||||
|     defaults = func.__defaults__ | ||||
|     kwdefaults = func.__kwdefaults__ | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,9 +1,3 @@ | |||
| #from __future__ import annotations | ||||
| USING_STRINGS = False | ||||
| 
 | ||||
| # dataclass_module_1.py and dataclass_module_1_str.py are identical | ||||
| # except only the latter uses string annotations. | ||||
| 
 | ||||
| import dataclasses | ||||
| import typing | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,32 +0,0 @@ | |||
| from __future__ import annotations | ||||
| USING_STRINGS = True | ||||
| 
 | ||||
| # dataclass_module_1.py and dataclass_module_1_str.py are identical | ||||
| # except only the latter uses string annotations. | ||||
| 
 | ||||
| import dataclasses | ||||
| import typing | ||||
| 
 | ||||
| T_CV2 = typing.ClassVar[int] | ||||
| T_CV3 = typing.ClassVar | ||||
| 
 | ||||
| T_IV2 = dataclasses.InitVar[int] | ||||
| T_IV3 = dataclasses.InitVar | ||||
| 
 | ||||
| @dataclasses.dataclass | ||||
| class CV: | ||||
|     T_CV4 = typing.ClassVar | ||||
|     cv0: typing.ClassVar[int] = 20 | ||||
|     cv1: typing.ClassVar = 30 | ||||
|     cv2: T_CV2 | ||||
|     cv3: T_CV3 | ||||
|     not_cv4: T_CV4  # When using string annotations, this field is not recognized as a ClassVar. | ||||
| 
 | ||||
| @dataclasses.dataclass | ||||
| class IV: | ||||
|     T_IV4 = dataclasses.InitVar | ||||
|     iv0: dataclasses.InitVar[int] | ||||
|     iv1: dataclasses.InitVar | ||||
|     iv2: T_IV2 | ||||
|     iv3: T_IV3 | ||||
|     not_iv4: T_IV4  # When using string annotations, this field is not recognized as an InitVar. | ||||
|  | @ -1,9 +1,3 @@ | |||
| #from __future__ import annotations | ||||
| USING_STRINGS = False | ||||
| 
 | ||||
| # dataclass_module_2.py and dataclass_module_2_str.py are identical | ||||
| # except only the latter uses string annotations. | ||||
| 
 | ||||
| from dataclasses import dataclass, InitVar | ||||
| from typing import ClassVar | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,32 +0,0 @@ | |||
| from __future__ import annotations | ||||
| USING_STRINGS = True | ||||
| 
 | ||||
| # dataclass_module_2.py and dataclass_module_2_str.py are identical | ||||
| # except only the latter uses string annotations. | ||||
| 
 | ||||
| from dataclasses import dataclass, InitVar | ||||
| from typing import ClassVar | ||||
| 
 | ||||
| T_CV2 = ClassVar[int] | ||||
| T_CV3 = ClassVar | ||||
| 
 | ||||
| T_IV2 = InitVar[int] | ||||
| T_IV3 = InitVar | ||||
| 
 | ||||
| @dataclass | ||||
| class CV: | ||||
|     T_CV4 = ClassVar | ||||
|     cv0: ClassVar[int] = 20 | ||||
|     cv1: ClassVar = 30 | ||||
|     cv2: T_CV2 | ||||
|     cv3: T_CV3 | ||||
|     not_cv4: T_CV4  # When using string annotations, this field is not recognized as a ClassVar. | ||||
| 
 | ||||
| @dataclass | ||||
| class IV: | ||||
|     T_IV4 = InitVar | ||||
|     iv0: InitVar[int] | ||||
|     iv1: InitVar | ||||
|     iv2: T_IV2 | ||||
|     iv3: T_IV3 | ||||
|     not_iv4: T_IV4  # When using string annotations, this field is not recognized as an InitVar. | ||||
|  | @ -1,5 +1,3 @@ | |||
| from __future__ import annotations | ||||
| 
 | ||||
| import dataclasses | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										228
									
								
								Lib/test/test_annotations.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								Lib/test/test_annotations.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,228 @@ | |||
| import unittest | ||||
| import sys | ||||
| from textwrap import dedent | ||||
| 
 | ||||
| class PostponedAnnotationsTestCase(unittest.TestCase): | ||||
|     template = dedent( | ||||
|         """ | ||||
|         def f() -> {ann}: | ||||
|             ... | ||||
|         def g(arg: {ann}) -> None: | ||||
|             ... | ||||
|         async def f2() -> {ann}: | ||||
|             ... | ||||
|         async def g2(arg: {ann}) -> None: | ||||
|             ... | ||||
|         var: {ann} | ||||
|         var2: {ann} = None | ||||
|         """ | ||||
|     ) | ||||
| 
 | ||||
|     def getActual(self, annotation): | ||||
|         scope = {} | ||||
|         exec(self.template.format(ann=annotation), {}, scope) | ||||
|         func_ret_ann = scope['f'].__annotations__['return'] | ||||
|         func_arg_ann = scope['g'].__annotations__['arg'] | ||||
|         async_func_ret_ann = scope['f2'].__annotations__['return'] | ||||
|         async_func_arg_ann = scope['g2'].__annotations__['arg'] | ||||
|         var_ann1 = scope['__annotations__']['var'] | ||||
|         var_ann2 = scope['__annotations__']['var2'] | ||||
|         self.assertEqual(func_ret_ann, func_arg_ann) | ||||
|         self.assertEqual(func_ret_ann, async_func_ret_ann) | ||||
|         self.assertEqual(func_ret_ann, async_func_arg_ann) | ||||
|         self.assertEqual(func_ret_ann, var_ann1) | ||||
|         self.assertEqual(func_ret_ann, var_ann2) | ||||
|         return func_ret_ann | ||||
| 
 | ||||
|     def assertAnnotationEqual( | ||||
|         self, annotation, expected=None, drop_parens=False, is_tuple=False, | ||||
|     ): | ||||
|         actual = self.getActual(annotation) | ||||
|         if expected is None: | ||||
|             expected = annotation if not is_tuple else annotation[1:-1] | ||||
|         if drop_parens: | ||||
|             self.assertNotEqual(actual, expected) | ||||
|             actual = actual.replace("(", "").replace(")", "") | ||||
| 
 | ||||
|         self.assertEqual(actual, expected) | ||||
| 
 | ||||
|     def test_annotations(self): | ||||
|         eq = self.assertAnnotationEqual | ||||
|         eq('...') | ||||
|         eq("'some_string'") | ||||
|         eq("u'some_string'") | ||||
|         eq("b'\\xa3'") | ||||
|         eq('Name') | ||||
|         eq('None') | ||||
|         eq('True') | ||||
|         eq('False') | ||||
|         eq('1') | ||||
|         eq('1.0') | ||||
|         eq('1j') | ||||
|         eq('True or False') | ||||
|         eq('True or False or None') | ||||
|         eq('True and False') | ||||
|         eq('True and False and None') | ||||
|         eq('Name1 and Name2 or Name3') | ||||
|         eq('Name1 and (Name2 or Name3)') | ||||
|         eq('Name1 or Name2 and Name3') | ||||
|         eq('(Name1 or Name2) and Name3') | ||||
|         eq('Name1 and Name2 or Name3 and Name4') | ||||
|         eq('Name1 or Name2 and Name3 or Name4') | ||||
|         eq('a + b + (c + d)') | ||||
|         eq('a * b * (c * d)') | ||||
|         eq('(a ** b) ** c ** d') | ||||
|         eq('v1 << 2') | ||||
|         eq('1 >> v2') | ||||
|         eq('1 % finished') | ||||
|         eq('1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8') | ||||
|         eq('not great') | ||||
|         eq('not not great') | ||||
|         eq('~great') | ||||
|         eq('+value') | ||||
|         eq('++value') | ||||
|         eq('-1') | ||||
|         eq('~int and not v1 ^ 123 + v2 | True') | ||||
|         eq('a + (not b)') | ||||
|         eq('lambda: None') | ||||
|         eq('lambda arg: None') | ||||
|         eq('lambda a=True: a') | ||||
|         eq('lambda a, b, c=True: a') | ||||
|         eq("lambda a, b, c=True, *, d=1 << v2, e='str': a") | ||||
|         eq("lambda a, b, c=True, *vararg, d, e='str', **kwargs: a + b") | ||||
|         eq("lambda a, /, b, c=True, *vararg, d, e='str', **kwargs: a + b") | ||||
|         eq('lambda x, /: x') | ||||
|         eq('lambda x=1, /: x') | ||||
|         eq('lambda x, /, y: x + y') | ||||
|         eq('lambda x=1, /, y=2: x + y') | ||||
|         eq('lambda x, /, y=1: x + y') | ||||
|         eq('lambda x, /, y=1, *, z=3: x + y + z') | ||||
|         eq('lambda x=1, /, y=2, *, z=3: x + y + z') | ||||
|         eq('lambda x=1, /, y=2, *, z: x + y + z') | ||||
|         eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2: x + y + z + w + l + l2') | ||||
|         eq('lambda x=1, y=2, z=3, /, w=4, *, l, l2, **kwargs: x + y + z + w + l + l2') | ||||
|         eq('lambda x, /, y=1, *, z: x + y + z') | ||||
|         eq('lambda x: lambda y: x + y') | ||||
|         eq('1 if True else 2') | ||||
|         eq('str or None if int or True else str or bytes or None') | ||||
|         eq('str or None if (1 if True else 2) else str or bytes or None') | ||||
|         eq("0 if not x else 1 if x > 0 else -1") | ||||
|         eq("(1 if x > 0 else -1) if x else 0") | ||||
|         eq("{'2.7': dead, '3.7': long_live or die_hard}") | ||||
|         eq("{'2.7': dead, '3.7': long_live or die_hard, **{'3.6': verygood}}") | ||||
|         eq("{**a, **b, **c}") | ||||
|         eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}") | ||||
|         eq("{*a, *b, *c}") | ||||
|         eq("({'a': 'b'}, True or False, +value, 'string', b'bytes') or None") | ||||
|         eq("()") | ||||
|         eq("(a,)") | ||||
|         eq("(a, b)") | ||||
|         eq("(a, b, c)") | ||||
|         eq("(*a, *b, *c)") | ||||
|         eq("[]") | ||||
|         eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]") | ||||
|         eq("[*a, *b, *c]") | ||||
|         eq("{i for i in (1, 2, 3)}") | ||||
|         eq("{i ** 2 for i in (1, 2, 3)}") | ||||
|         eq("{i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}") | ||||
|         eq("{i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)}") | ||||
|         eq("[i for i in (1, 2, 3)]") | ||||
|         eq("[i ** 2 for i in (1, 2, 3)]") | ||||
|         eq("[i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]") | ||||
|         eq("[i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)]") | ||||
|         eq("(i for i in (1, 2, 3))") | ||||
|         eq("(i ** 2 for i in (1, 2, 3))") | ||||
|         eq("(i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))") | ||||
|         eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))") | ||||
|         eq("{i: 0 for i in (1, 2, 3)}") | ||||
|         eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}") | ||||
|         eq("[(x, y) for x, y in (a, b)]") | ||||
|         eq("[(x,) for x, in (a,)]") | ||||
|         eq("Python3 > Python2 > COBOL") | ||||
|         eq("Life is Life") | ||||
|         eq("call()") | ||||
|         eq("call(arg)") | ||||
|         eq("call(kwarg='hey')") | ||||
|         eq("call(arg, kwarg='hey')") | ||||
|         eq("call(arg, *args, another, kwarg='hey')") | ||||
|         eq("call(arg, another, kwarg='hey', **kwargs, kwarg2='ho')") | ||||
|         eq("lukasz.langa.pl") | ||||
|         eq("call.me(maybe)") | ||||
|         eq("1 .real") | ||||
|         eq("1.0.real") | ||||
|         eq("....__class__") | ||||
|         eq("list[str]") | ||||
|         eq("dict[str, int]") | ||||
|         eq("set[str,]") | ||||
|         eq("tuple[str, ...]") | ||||
|         eq("tuple[(str, *types)]") | ||||
|         eq("tuple[str, int, (str, int)]") | ||||
|         eq("tuple[(*int, str, str, (str, int))]") | ||||
|         eq("tuple[str, int, float, dict[str, int]]") | ||||
|         eq("slice[0]") | ||||
|         eq("slice[0:1]") | ||||
|         eq("slice[0:1:2]") | ||||
|         eq("slice[:]") | ||||
|         eq("slice[:-1]") | ||||
|         eq("slice[1:]") | ||||
|         eq("slice[::-1]") | ||||
|         eq("slice[:,]") | ||||
|         eq("slice[1:2,]") | ||||
|         eq("slice[1:2:3,]") | ||||
|         eq("slice[1:2, 1]") | ||||
|         eq("slice[1:2, 2, 3]") | ||||
|         eq("slice[()]") | ||||
|         eq("slice[a, b:c, d:e:f]") | ||||
|         eq("slice[(x for x in a)]") | ||||
|         eq('str or None if sys.version_info[0] > (3,) else str or bytes or None') | ||||
|         eq("f'f-string without formatted values is just a string'") | ||||
|         eq("f'{{NOT a formatted value}}'") | ||||
|         eq("f'some f-string with {a} {few():.2f} {formatted.values!r}'") | ||||
|         eq('''f"{f'{nested} inner'} outer"''') | ||||
|         eq("f'space between opening braces: { {a for a in (1, 2, 3)}}'") | ||||
|         eq("f'{(lambda x: x)}'") | ||||
|         eq("f'{(None if a else lambda x: x)}'") | ||||
|         eq("f'{x}'") | ||||
|         eq("f'{x!r}'") | ||||
|         eq("f'{x!a}'") | ||||
|         eq('(yield from outside_of_generator)') | ||||
|         eq('(yield)') | ||||
|         eq('(yield a + b)') | ||||
|         eq('await some.complicated[0].call(with_args=True or 1 is not 1)') | ||||
|         eq('[x for x in (a if b else c)]') | ||||
|         eq('[x for x in a if (b if c else d)]') | ||||
|         eq('f(x for x in a)') | ||||
|         eq('f(1, (x for x in a))') | ||||
|         eq('f((x for x in a), 2)') | ||||
|         eq('(((a)))', 'a') | ||||
|         eq('(((a, b)))', '(a, b)') | ||||
|         eq("(x := 10)") | ||||
|         eq("f'{(x := 10):=10}'") | ||||
|         eq("1 + 2") | ||||
|         eq("1 + 2 + 3") | ||||
| 
 | ||||
|     def test_fstring_debug_annotations(self): | ||||
|         # f-strings with '=' don't round trip very well, so set the expected | ||||
|         # result explicitely. | ||||
|         self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") | ||||
|         self.assertAnnotationEqual("f'{x=:}'", expected="f'x={x:}'") | ||||
|         self.assertAnnotationEqual("f'{x=:.2f}'", expected="f'x={x:.2f}'") | ||||
|         self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'") | ||||
|         self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'") | ||||
|         self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'") | ||||
| 
 | ||||
|     def test_infinity_numbers(self): | ||||
|         inf = "1e" + repr(sys.float_info.max_10_exp + 1) | ||||
|         infj = f"{inf}j" | ||||
|         self.assertAnnotationEqual("1e1000", expected=inf) | ||||
|         self.assertAnnotationEqual("1e1000j", expected=infj) | ||||
|         self.assertAnnotationEqual("-1e1000", expected=f"-{inf}") | ||||
|         self.assertAnnotationEqual("3+1e1000j", expected=f"3 + {infj}") | ||||
|         self.assertAnnotationEqual("(1e1000, 1e1000j)", expected=f"({inf}, {infj})") | ||||
|         self.assertAnnotationEqual("'inf'") | ||||
|         self.assertAnnotationEqual("('inf', 1e1000, 'infxxx', 1e1000j)", expected=f"('inf', {inf}, 'infxxx', {infj})") | ||||
|         self.assertAnnotationEqual("(1e1000, (1e1000j,))", expected=f"({inf}, ({infj},))") | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     unittest.main() | ||||
|  | @ -91,10 +91,6 @@ def test_badsyntax_1(self): | |||
|                 pass | ||||
|             """, | ||||
| 
 | ||||
|             """async def foo(a:await something()): | ||||
|                 pass | ||||
|             """, | ||||
| 
 | ||||
|             """async def foo(): | ||||
|                 def bar(): | ||||
|                  [i async for i in els] | ||||
|  | @ -299,10 +295,6 @@ def bar(): | |||
|                    pass | ||||
|             """, | ||||
| 
 | ||||
|             """async def foo(a:await b): | ||||
|                    pass | ||||
|             """, | ||||
| 
 | ||||
|             """def baz(): | ||||
|                    async def foo(a=await b): | ||||
|                        pass | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| import inspect | ||||
| import builtins | ||||
| import unittest | ||||
| from textwrap import dedent | ||||
| from unittest.mock import Mock | ||||
| from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional | ||||
| from typing import get_type_hints | ||||
|  | @ -562,17 +563,17 @@ class C: | |||
|         self.assertEqual(len(the_fields), 3) | ||||
| 
 | ||||
|         self.assertEqual(the_fields[0].name, 'x') | ||||
|         self.assertEqual(the_fields[0].type, int) | ||||
|         self.assertEqual(the_fields[0].type, 'int') | ||||
|         self.assertFalse(hasattr(C, 'x')) | ||||
|         self.assertTrue (the_fields[0].init) | ||||
|         self.assertTrue (the_fields[0].repr) | ||||
|         self.assertEqual(the_fields[1].name, 'y') | ||||
|         self.assertEqual(the_fields[1].type, str) | ||||
|         self.assertEqual(the_fields[1].type, 'str') | ||||
|         self.assertIsNone(getattr(C, 'y')) | ||||
|         self.assertFalse(the_fields[1].init) | ||||
|         self.assertTrue (the_fields[1].repr) | ||||
|         self.assertEqual(the_fields[2].name, 'z') | ||||
|         self.assertEqual(the_fields[2].type, str) | ||||
|         self.assertEqual(the_fields[2].type, 'str') | ||||
|         self.assertFalse(hasattr(C, 'z')) | ||||
|         self.assertTrue (the_fields[2].init) | ||||
|         self.assertFalse(the_fields[2].repr) | ||||
|  | @ -758,11 +759,11 @@ class F: | |||
|         def validate_class(cls): | ||||
|             # First, check __annotations__, even though they're not | ||||
|             #  function annotations. | ||||
|             self.assertEqual(cls.__annotations__['i'], int) | ||||
|             self.assertEqual(cls.__annotations__['j'], str) | ||||
|             self.assertEqual(cls.__annotations__['k'], F) | ||||
|             self.assertEqual(cls.__annotations__['l'], float) | ||||
|             self.assertEqual(cls.__annotations__['z'], complex) | ||||
|             self.assertEqual(cls.__annotations__['i'], 'int') | ||||
|             self.assertEqual(cls.__annotations__['j'], 'str') | ||||
|             self.assertEqual(cls.__annotations__['k'], 'F') | ||||
|             self.assertEqual(cls.__annotations__['l'], 'float') | ||||
|             self.assertEqual(cls.__annotations__['z'], 'complex') | ||||
| 
 | ||||
|             # Verify __init__. | ||||
| 
 | ||||
|  | @ -777,22 +778,22 @@ def validate_class(cls): | |||
|             self.assertEqual(param.name, 'self') | ||||
|             param = next(params) | ||||
|             self.assertEqual(param.name, 'i') | ||||
|             self.assertIs   (param.annotation, int) | ||||
|             self.assertIs   (param.annotation, 'int') | ||||
|             self.assertEqual(param.default, inspect.Parameter.empty) | ||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||
|             param = next(params) | ||||
|             self.assertEqual(param.name, 'j') | ||||
|             self.assertIs   (param.annotation, str) | ||||
|             self.assertIs   (param.annotation, 'str') | ||||
|             self.assertEqual(param.default, inspect.Parameter.empty) | ||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||
|             param = next(params) | ||||
|             self.assertEqual(param.name, 'k') | ||||
|             self.assertIs   (param.annotation, F) | ||||
|             self.assertIs   (param.annotation, 'F') | ||||
|             # Don't test for the default, since it's set to MISSING. | ||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||
|             param = next(params) | ||||
|             self.assertEqual(param.name, 'l') | ||||
|             self.assertIs   (param.annotation, float) | ||||
|             self.assertIs   (param.annotation, 'float') | ||||
|             # Don't test for the default, since it's set to MISSING. | ||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||
|             self.assertRaises(StopIteration, next, params) | ||||
|  | @ -2806,13 +2807,10 @@ class C: | |||
| 
 | ||||
| class TestStringAnnotations(unittest.TestCase): | ||||
|     def test_classvar(self): | ||||
|         # Some expressions recognized as ClassVar really aren't.  But | ||||
|         #  if you're using string annotations, it's not an exact | ||||
|         #  science. | ||||
|         # These tests assume that both "import typing" and "from | ||||
|         # typing import *" have been run in this file. | ||||
|         for typestr in ('ClassVar[int]', | ||||
|                         'ClassVar [int]' | ||||
|                         'ClassVar [int]', | ||||
|                         ' ClassVar [int]', | ||||
|                         'ClassVar', | ||||
|                         ' ClassVar ', | ||||
|  | @ -2823,17 +2821,15 @@ def test_classvar(self): | |||
|                         'typing. ClassVar[str]', | ||||
|                         'typing.ClassVar [str]', | ||||
|                         'typing.ClassVar [ str]', | ||||
| 
 | ||||
|                         # Double stringified | ||||
|                         '"typing.ClassVar[int]"', | ||||
|                         # Not syntactically valid, but these will | ||||
|                         #  be treated as ClassVars. | ||||
|                         # be treated as ClassVars. | ||||
|                         'typing.ClassVar.[int]', | ||||
|                         'typing.ClassVar+', | ||||
|                         ): | ||||
|             with self.subTest(typestr=typestr): | ||||
|                 @dataclass | ||||
|                 class C: | ||||
|                     x: typestr | ||||
| 
 | ||||
|                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||
|                 # x is a ClassVar, so C() takes no args. | ||||
|                 C() | ||||
| 
 | ||||
|  | @ -2854,9 +2850,7 @@ def test_isnt_classvar(self): | |||
|                         'typingxClassVar[str]', | ||||
|                         ): | ||||
|             with self.subTest(typestr=typestr): | ||||
|                 @dataclass | ||||
|                 class C: | ||||
|                     x: typestr | ||||
|                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||
| 
 | ||||
|                 # x is not a ClassVar, so C() takes one arg. | ||||
|                 self.assertEqual(C(10).x, 10) | ||||
|  | @ -2876,16 +2870,16 @@ def test_initvar(self): | |||
|                         'dataclasses. InitVar[str]', | ||||
|                         'dataclasses.InitVar [str]', | ||||
|                         'dataclasses.InitVar [ str]', | ||||
| 
 | ||||
|                         # Double stringified | ||||
|                         '"dataclasses.InitVar[int]"', | ||||
|                         # Not syntactically valid, but these will | ||||
|                         #  be treated as InitVars. | ||||
|                         'dataclasses.InitVar.[int]', | ||||
|                         'dataclasses.InitVar+', | ||||
|                         ): | ||||
|             with self.subTest(typestr=typestr): | ||||
|                 @dataclass | ||||
|                 class C: | ||||
|                     x: typestr | ||||
|                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||
| 
 | ||||
| 
 | ||||
|                 # x is an InitVar, so doesn't create a member. | ||||
|                 with self.assertRaisesRegex(AttributeError, | ||||
|  | @ -2899,30 +2893,22 @@ def test_isnt_initvar(self): | |||
|                         'typing.xInitVar[int]', | ||||
|                         ): | ||||
|             with self.subTest(typestr=typestr): | ||||
|                 @dataclass | ||||
|                 class C: | ||||
|                     x: typestr | ||||
|                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||
| 
 | ||||
|                 # x is not an InitVar, so there will be a member x. | ||||
|                 self.assertEqual(C(10).x, 10) | ||||
| 
 | ||||
|     def test_classvar_module_level_import(self): | ||||
|         from test import dataclass_module_1 | ||||
|         from test import dataclass_module_1_str | ||||
|         from test import dataclass_module_2 | ||||
|         from test import dataclass_module_2_str | ||||
| 
 | ||||
|         for m in (dataclass_module_1, dataclass_module_1_str, | ||||
|                   dataclass_module_2, dataclass_module_2_str, | ||||
|                   ): | ||||
|         for m in (dataclass_module_1, | ||||
|                   dataclass_module_2): | ||||
|             with self.subTest(m=m): | ||||
|                 # There's a difference in how the ClassVars are | ||||
|                 # interpreted when using string annotations or | ||||
|                 # not. See the imported modules for details. | ||||
|                 if m.USING_STRINGS: | ||||
|                     c = m.CV(10) | ||||
|                 else: | ||||
|                     c = m.CV() | ||||
|                 c = m.CV(10) | ||||
|                 self.assertEqual(c.cv0, 20) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -2938,14 +2924,9 @@ def test_classvar_module_level_import(self): | |||
|                             # not an instance field. | ||||
|                             getattr(c, field_name) | ||||
| 
 | ||||
|                 if m.USING_STRINGS: | ||||
|                     # iv4 is interpreted as a normal field. | ||||
|                     self.assertIn('not_iv4', c.__dict__) | ||||
|                     self.assertEqual(c.not_iv4, 4) | ||||
|                 else: | ||||
|                     # iv4 is interpreted as an InitVar, so it | ||||
|                     # won't exist on the instance. | ||||
|                     self.assertNotIn('not_iv4', c.__dict__) | ||||
|                 # iv4 is interpreted as a normal field. | ||||
|                 self.assertIn('not_iv4', c.__dict__) | ||||
|                 self.assertEqual(c.not_iv4, 4) | ||||
| 
 | ||||
|     def test_text_annotations(self): | ||||
|         from test import dataclass_textanno | ||||
|  |  | |||
|  | @ -227,28 +227,26 @@ def bug1333982(x=[]): | |||
|   2           0 SETUP_ANNOTATIONS | ||||
|               2 LOAD_CONST               0 (1) | ||||
|               4 STORE_NAME               0 (x) | ||||
|               6 LOAD_NAME                1 (int) | ||||
|               8 LOAD_NAME                2 (__annotations__) | ||||
|              10 LOAD_CONST               1 ('x') | ||||
|               6 LOAD_CONST               1 ('int') | ||||
|               8 LOAD_NAME                1 (__annotations__) | ||||
|              10 LOAD_CONST               2 ('x') | ||||
|              12 STORE_SUBSCR | ||||
| 
 | ||||
|   3          14 LOAD_NAME                3 (fun) | ||||
|              16 LOAD_CONST               0 (1) | ||||
|              18 CALL_FUNCTION            1 | ||||
|              20 LOAD_NAME                2 (__annotations__) | ||||
|              22 LOAD_CONST               2 ('y') | ||||
|              24 STORE_SUBSCR | ||||
|   3          14 LOAD_CONST               3 ('fun(1)') | ||||
|              16 LOAD_NAME                1 (__annotations__) | ||||
|              18 LOAD_CONST               4 ('y') | ||||
|              20 STORE_SUBSCR | ||||
| 
 | ||||
|   4          26 LOAD_CONST               0 (1) | ||||
|              28 LOAD_NAME                4 (lst) | ||||
|              30 LOAD_NAME                3 (fun) | ||||
|              32 LOAD_CONST               3 (0) | ||||
|              34 CALL_FUNCTION            1 | ||||
|              36 STORE_SUBSCR | ||||
|              38 LOAD_NAME                1 (int) | ||||
|              40 POP_TOP | ||||
|              42 LOAD_CONST               4 (None) | ||||
|              44 RETURN_VALUE | ||||
|   4          22 LOAD_CONST               0 (1) | ||||
|              24 LOAD_NAME                2 (lst) | ||||
|              26 LOAD_NAME                3 (fun) | ||||
|              28 LOAD_CONST               5 (0) | ||||
|              30 CALL_FUNCTION            1 | ||||
|              32 STORE_SUBSCR | ||||
|              34 LOAD_NAME                4 (int) | ||||
|              36 POP_TOP | ||||
|              38 LOAD_CONST               6 (None) | ||||
|              40 RETURN_VALUE | ||||
| """ | ||||
| 
 | ||||
| compound_stmt_str = """\ | ||||
|  |  | |||
|  | @ -618,7 +618,7 @@ def check_wrapper(self, wrapper, wrapped, | |||
| 
 | ||||
| 
 | ||||
|     def _default_update(self): | ||||
|         def f(a:'This is a new annotation'): | ||||
|         def f(a: int): | ||||
|             """This is a test""" | ||||
|             pass | ||||
|         f.attr = 'This is also a test' | ||||
|  | @ -635,7 +635,7 @@ def test_default_update(self): | |||
|         self.assertEqual(wrapper.__name__, 'f') | ||||
|         self.assertEqual(wrapper.__qualname__, f.__qualname__) | ||||
|         self.assertEqual(wrapper.attr, 'This is also a test') | ||||
|         self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation') | ||||
|         self.assertEqual(wrapper.__annotations__['a'], 'int') | ||||
|         self.assertNotIn('b', wrapper.__annotations__) | ||||
| 
 | ||||
|     @unittest.skipIf(sys.flags.optimize >= 2, | ||||
|  |  | |||
|  | @ -362,7 +362,7 @@ class C: | |||
|             z = 2 | ||||
|             def __init__(self, x): | ||||
|                 self.x: int = x | ||||
|         self.assertEqual(C.__annotations__, {'_C__foo': int, 's': str}) | ||||
|         self.assertEqual(C.__annotations__, {'_C__foo': 'int', 's': 'str'}) | ||||
|         with self.assertRaises(NameError): | ||||
|             class CBad: | ||||
|                 no_such_name_defined.attr: int = 0 | ||||
|  | @ -378,15 +378,15 @@ def __prepare__(metacls, name, bases, **kwds): | |||
|                 return {'__annotations__': CNS()} | ||||
|         class CC(metaclass=CMeta): | ||||
|             XX: 'ANNOT' | ||||
|         self.assertEqual(CC.__annotations__['xx'], 'ANNOT') | ||||
|         self.assertEqual(CC.__annotations__['xx'], repr('ANNOT')) | ||||
| 
 | ||||
|     def test_var_annot_module_semantics(self): | ||||
|         with self.assertRaises(AttributeError): | ||||
|             print(test.__annotations__) | ||||
|         self.assertEqual(ann_module.__annotations__, | ||||
|                      {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]}) | ||||
|                      {1: 2, 'x': 'int', 'y': 'str', 'f': 'Tuple[int, int]'}) | ||||
|         self.assertEqual(ann_module.M.__annotations__, | ||||
|                               {'123': 123, 'o': type}) | ||||
|                               {'123': 123, 'o': 'type'}) | ||||
|         self.assertEqual(ann_module2.__annotations__, {}) | ||||
| 
 | ||||
|     def test_var_annot_in_module(self): | ||||
|  | @ -405,7 +405,7 @@ def test_var_annot_simple_exec(self): | |||
|         exec("'docstring'\n" | ||||
|              "__annotations__[1] = 2\n" | ||||
|              "x: int = 5\n", gns, lns) | ||||
|         self.assertEqual(lns["__annotations__"], {1: 2, 'x': int}) | ||||
|         self.assertEqual(lns["__annotations__"], {1: 2, 'x': 'int'}) | ||||
|         with self.assertRaises(KeyError): | ||||
|             gns['__annotations__'] | ||||
| 
 | ||||
|  | @ -413,8 +413,8 @@ def test_var_annot_custom_maps(self): | |||
|         # tests with custom locals() and __annotations__ | ||||
|         ns = {'__annotations__': CNS()} | ||||
|         exec('X: int; Z: str = "Z"; (w): complex = 1j', ns) | ||||
|         self.assertEqual(ns['__annotations__']['x'], int) | ||||
|         self.assertEqual(ns['__annotations__']['z'], str) | ||||
|         self.assertEqual(ns['__annotations__']['x'], 'int') | ||||
|         self.assertEqual(ns['__annotations__']['z'], 'str') | ||||
|         with self.assertRaises(KeyError): | ||||
|             ns['__annotations__']['w'] | ||||
|         nonloc_ns = {} | ||||
|  | @ -428,7 +428,7 @@ def __setitem__(self, item, value): | |||
|             def __getitem__(self, item): | ||||
|                 return self._dct[item] | ||||
|         exec('x: int = 1', {}, CNS2()) | ||||
|         self.assertEqual(nonloc_ns['__annotations__']['x'], int) | ||||
|         self.assertEqual(nonloc_ns['__annotations__']['x'], 'int') | ||||
| 
 | ||||
|     def test_var_annot_refleak(self): | ||||
|         # complex case: custom locals plus custom __annotations__ | ||||
|  | @ -445,7 +445,7 @@ def __setitem__(self, item, value): | |||
|             def __getitem__(self, item): | ||||
|                 return self._dct[item] | ||||
|         exec('X: str', {}, CNS2()) | ||||
|         self.assertEqual(nonloc_ns['__annotations__']['x'], str) | ||||
|         self.assertEqual(nonloc_ns['__annotations__']['x'], 'str') | ||||
| 
 | ||||
|     def test_var_annot_rhs(self): | ||||
|         ns = {} | ||||
|  | @ -625,50 +625,46 @@ def f(*args, **kwargs): | |||
| 
 | ||||
|         # argument annotation tests | ||||
|         def f(x) -> list: pass | ||||
|         self.assertEqual(f.__annotations__, {'return': list}) | ||||
|         self.assertEqual(f.__annotations__, {'return': 'list'}) | ||||
|         def f(x: int): pass | ||||
|         self.assertEqual(f.__annotations__, {'x': int}) | ||||
|         self.assertEqual(f.__annotations__, {'x': 'int'}) | ||||
|         def f(x: int, /): pass | ||||
|         self.assertEqual(f.__annotations__, {'x': int}) | ||||
|         self.assertEqual(f.__annotations__, {'x': 'int'}) | ||||
|         def f(x: int = 34, /): pass | ||||
|         self.assertEqual(f.__annotations__, {'x': int}) | ||||
|         self.assertEqual(f.__annotations__, {'x': 'int'}) | ||||
|         def f(*x: str): pass | ||||
|         self.assertEqual(f.__annotations__, {'x': str}) | ||||
|         self.assertEqual(f.__annotations__, {'x': 'str'}) | ||||
|         def f(**x: float): pass | ||||
|         self.assertEqual(f.__annotations__, {'x': float}) | ||||
|         def f(x, y: 1+2): pass | ||||
|         self.assertEqual(f.__annotations__, {'y': 3}) | ||||
|         def f(x, y: 1+2, /): pass | ||||
|         self.assertEqual(f.__annotations__, {'y': 3}) | ||||
|         self.assertEqual(f.__annotations__, {'x': 'float'}) | ||||
|         def f(a, b: 1, c: 2, d): pass | ||||
|         self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) | ||||
|         self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'}) | ||||
|         def f(a, b: 1, /, c: 2, d): pass | ||||
|         self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) | ||||
|         self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'}) | ||||
|         def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6): pass | ||||
|         self.assertEqual(f.__annotations__, | ||||
|                          {'b': 1, 'c': 2, 'e': 3, 'g': 6}) | ||||
|                          {'b': '1', 'c': '2', 'e': '3', 'g': '6'}) | ||||
|         def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6, h: 7, i=8, j: 9 = 10, | ||||
|               **k: 11) -> 12: pass | ||||
|         self.assertEqual(f.__annotations__, | ||||
|                          {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, | ||||
|                           'k': 11, 'return': 12}) | ||||
|                          {'b': '1', 'c': '2', 'e': '3', 'g': '6', 'h': '7', 'j': '9', | ||||
|                           'k': '11', 'return': '12'}) | ||||
|         def f(a, b: 1, c: 2, d, e: 3 = 4, f: int = 5, /, *g: 6, h: 7, i=8, j: 9 = 10, | ||||
|               **k: 11) -> 12: pass | ||||
|         self.assertEqual(f.__annotations__, | ||||
|                           {'b': 1, 'c': 2, 'e': 3, 'f': int, 'g': 6, 'h': 7, 'j': 9, | ||||
|                            'k': 11, 'return': 12}) | ||||
|                           {'b': '1', 'c': '2', 'e': '3', 'f': 'int', 'g': '6', 'h': '7', 'j': '9', | ||||
|                            'k': '11', 'return': '12'}) | ||||
|         # Check for issue #20625 -- annotations mangling | ||||
|         class Spam: | ||||
|             def f(self, *, __kw: 1): | ||||
|                 pass | ||||
|         class Ham(Spam): pass | ||||
|         self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': 1}) | ||||
|         self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': 1}) | ||||
|         self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': '1'}) | ||||
|         self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': '1'}) | ||||
|         # Check for SF Bug #1697248 - mixing decorators and a return annotation | ||||
|         def null(x): return x | ||||
|         @null | ||||
|         def f(x) -> list: pass | ||||
|         self.assertEqual(f.__annotations__, {'return': list}) | ||||
|         self.assertEqual(f.__annotations__, {'return': 'list'}) | ||||
| 
 | ||||
|         # Test expressions as decorators (PEP 614): | ||||
|         @False or null | ||||
|  | @ -1116,8 +1112,6 @@ def g(): rest = 4, 5, 6; yield 1, 2, 3, *rest | |||
|         # Not allowed at class scope | ||||
|         check_syntax_error(self, "class foo:yield 1") | ||||
|         check_syntax_error(self, "class foo:yield from ()") | ||||
|         # Check annotation refleak on SyntaxError | ||||
|         check_syntax_error(self, "def g(a:(yield)): pass") | ||||
| 
 | ||||
|     def test_yield_in_comprehensions(self): | ||||
|         # Check yield in comprehensions | ||||
|  |  | |||
|  | @ -862,7 +862,7 @@ def test_getfullargspec(self): | |||
| 
 | ||||
|         self.assertFullArgSpecEquals(mod2.annotated, ['arg1'], | ||||
|                                      ann_e={'arg1' : list}, | ||||
|                                      formatted='(arg1: list)') | ||||
|                                      formatted="(arg1: list)") | ||||
|         self.assertFullArgSpecEquals(mod2.keyword_only_arg, [], | ||||
|                                      kwonlyargs_e=['arg'], | ||||
|                                      formatted='(*, arg)') | ||||
|  | @ -2211,8 +2211,8 @@ def test(a, b:'foo') -> 123: | |||
|             pass | ||||
|         self.assertEqual(self.signature(test), | ||||
|                          ((('a', ..., ..., "positional_or_keyword"), | ||||
|                            ('b', ..., 'foo', "positional_or_keyword")), | ||||
|                           123)) | ||||
|                            ('b', ..., repr('foo'), "positional_or_keyword")), | ||||
|                           '123')) | ||||
| 
 | ||||
|     def test_signature_on_wkwonly(self): | ||||
|         def test(*, a:float, b:str) -> int: | ||||
|  | @ -2227,11 +2227,11 @@ def test(a, b:'foo'=10, *args:'bar', spam:'baz', ham=123, **kwargs:int): | |||
|             pass | ||||
|         self.assertEqual(self.signature(test), | ||||
|                          ((('a', ..., ..., "positional_or_keyword"), | ||||
|                            ('b', 10, 'foo', "positional_or_keyword"), | ||||
|                            ('args', ..., 'bar', "var_positional"), | ||||
|                            ('spam', ..., 'baz', "keyword_only"), | ||||
|                            ('b', 10, repr('foo'), "positional_or_keyword"), | ||||
|                            ('args', ..., repr('bar'), "var_positional"), | ||||
|                            ('spam', ..., repr('baz'), "keyword_only"), | ||||
|                            ('ham', 123, ..., "keyword_only"), | ||||
|                            ('kwargs', ..., int, "var_keyword")), | ||||
|                            ('kwargs', ..., 'int', "var_keyword")), | ||||
|                           ...)) | ||||
| 
 | ||||
|     def test_signature_without_self(self): | ||||
|  | @ -2640,12 +2640,12 @@ def test(a, b, c:int) -> 42: | |||
| 
 | ||||
|         self.assertEqual(self.signature(partial(partial(test, 1))), | ||||
|                          ((('b', ..., ..., "positional_or_keyword"), | ||||
|                            ('c', ..., int, "positional_or_keyword")), | ||||
|                           42)) | ||||
|                            ('c', ..., 'int', "positional_or_keyword")), | ||||
|                           '42')) | ||||
| 
 | ||||
|         self.assertEqual(self.signature(partial(partial(test, 1), 2)), | ||||
|                          ((('c', ..., int, "positional_or_keyword"),), | ||||
|                           42)) | ||||
|                          ((('c', ..., 'int', "positional_or_keyword"),), | ||||
|                           '42')) | ||||
| 
 | ||||
|         psig = inspect.signature(partial(partial(test, 1), 2)) | ||||
| 
 | ||||
|  | @ -2764,12 +2764,12 @@ def test(it, a, *, c) -> 'spam': | |||
|                          ((('it', ..., ..., 'positional_or_keyword'), | ||||
|                            ('a', ..., ..., 'positional_or_keyword'), | ||||
|                            ('c', 1, ..., 'keyword_only')), | ||||
|                           'spam')) | ||||
|                           repr('spam'))) | ||||
| 
 | ||||
|         self.assertEqual(self.signature(Spam().ham), | ||||
|                          ((('a', ..., ..., 'positional_or_keyword'), | ||||
|                            ('c', 1, ..., 'keyword_only')), | ||||
|                           'spam')) | ||||
|                           repr('spam'))) | ||||
| 
 | ||||
|         class Spam: | ||||
|             def test(self: 'anno', x): | ||||
|  | @ -2778,7 +2778,7 @@ def test(self: 'anno', x): | |||
|             g = partialmethod(test, 1) | ||||
| 
 | ||||
|         self.assertEqual(self.signature(Spam.g), | ||||
|                          ((('self', ..., 'anno', 'positional_or_keyword'),), | ||||
|                          ((('self', ..., repr('anno'), 'positional_or_keyword'),), | ||||
|                           ...)) | ||||
| 
 | ||||
|     def test_signature_on_fake_partialmethod(self): | ||||
|  | @ -3116,20 +3116,16 @@ def foo(a={}): pass | |||
|         with self.assertRaisesRegex(TypeError, 'unhashable type'): | ||||
|             hash(inspect.signature(foo)) | ||||
| 
 | ||||
|         def foo(a) -> {}: pass | ||||
|         with self.assertRaisesRegex(TypeError, 'unhashable type'): | ||||
|             hash(inspect.signature(foo)) | ||||
| 
 | ||||
|     def test_signature_str(self): | ||||
|         def foo(a:int=1, *, b, c=None, **kwargs) -> 42: | ||||
|             pass | ||||
|         self.assertEqual(str(inspect.signature(foo)), | ||||
|                          '(a: int = 1, *, b, c=None, **kwargs) -> 42') | ||||
|                          '(a: \'int\' = 1, *, b, c=None, **kwargs) -> \'42\'') | ||||
| 
 | ||||
|         def foo(a:int=1, *args, b, c=None, **kwargs) -> 42: | ||||
|             pass | ||||
|         self.assertEqual(str(inspect.signature(foo)), | ||||
|                          '(a: int = 1, *args, b, c=None, **kwargs) -> 42') | ||||
|                          '(a: \'int\' = 1, *args, b, c=None, **kwargs) -> \'42\'') | ||||
| 
 | ||||
|         def foo(): | ||||
|             pass | ||||
|  | @ -3172,8 +3168,8 @@ def test() -> 42: | |||
|         self.assertIs(sig.return_annotation, None) | ||||
|         sig = sig.replace(return_annotation=sig.empty) | ||||
|         self.assertIs(sig.return_annotation, sig.empty) | ||||
|         sig = sig.replace(return_annotation=42) | ||||
|         self.assertEqual(sig.return_annotation, 42) | ||||
|         sig = sig.replace(return_annotation='42') | ||||
|         self.assertEqual(sig.return_annotation, '42') | ||||
|         self.assertEqual(sig, inspect.signature(test)) | ||||
| 
 | ||||
|     def test_signature_on_mangled_parameters(self): | ||||
|  | @ -3185,8 +3181,8 @@ class Ham(Spam): | |||
| 
 | ||||
|         self.assertEqual(self.signature(Spam.foo), | ||||
|                          ((('self', ..., ..., "positional_or_keyword"), | ||||
|                            ('_Spam__p1', 2, 1, "positional_or_keyword"), | ||||
|                            ('_Spam__p2', 3, 2, "keyword_only")), | ||||
|                            ('_Spam__p1', 2, '1', "positional_or_keyword"), | ||||
|                            ('_Spam__p2', 3, '2', "keyword_only")), | ||||
|                           ...)) | ||||
| 
 | ||||
|         self.assertEqual(self.signature(Spam.foo), | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ class C: pass | |||
|     def test_use_existing_annotations(self): | ||||
|         ns = {'__annotations__': {1: 2}} | ||||
|         exec('x: int', ns) | ||||
|         self.assertEqual(ns['__annotations__'], {'x': int, 1: 2}) | ||||
|         self.assertEqual(ns['__annotations__'], {'x': 'int', 1: 2}) | ||||
| 
 | ||||
|     def test_do_not_recreate_annotations(self): | ||||
|         # Don't rely on the existence of the '__annotations__' global. | ||||
|  |  | |||
|  | @ -302,14 +302,14 @@ def inner_has_pos_only(): | |||
|             def f(x: int, /): ... | ||||
|             return f | ||||
| 
 | ||||
|         assert inner_has_pos_only().__annotations__ == {'x': int} | ||||
|         assert inner_has_pos_only().__annotations__ == {'x': 'int'} | ||||
| 
 | ||||
|         class Something: | ||||
|             def method(self): | ||||
|                 def f(x: int, /): ... | ||||
|                 return f | ||||
| 
 | ||||
|         assert Something().method().__annotations__ == {'x': int} | ||||
|         assert Something().method().__annotations__ == {'x': 'int'} | ||||
| 
 | ||||
|         def multiple_levels(): | ||||
|             def inner_has_pos_only(): | ||||
|  | @ -317,7 +317,7 @@ def f(x: int, /): ... | |||
|                 return f | ||||
|             return inner_has_pos_only() | ||||
| 
 | ||||
|         assert multiple_levels().__annotations__ == {'x': int} | ||||
|         assert multiple_levels().__annotations__ == {'x': 'int'} | ||||
| 
 | ||||
|     def test_same_keyword_as_positional_with_kwargs(self): | ||||
|         def f(something,/,**kwargs): | ||||
|  | @ -429,17 +429,6 @@ def method(self, /): | |||
| 
 | ||||
|         self.assertEqual(C().method(), sentinel) | ||||
| 
 | ||||
|     def test_annotations_constant_fold(self): | ||||
|         def g(): | ||||
|             def f(x: not (int is int), /): ... | ||||
| 
 | ||||
|         # without constant folding we end up with | ||||
|         # COMPARE_OP(is), IS_OP (0) | ||||
|         # with constant folding we should expect a IS_OP (1) | ||||
|         codes = [(i.opname, i.argval) for i in dis.get_instructions(g)] | ||||
|         self.assertNotIn(('UNARY_NOT', None), codes) | ||||
|         self.assertIn(('IS_OP', 1), codes) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     unittest.main() | ||||
|  |  | |||
|  | @ -81,7 +81,7 @@ class B(builtins.object) | |||
|      |\x20\x20 | ||||
|      |  NO_MEANING = 'eggs' | ||||
|      |\x20\x20 | ||||
|      |  __annotations__ = {'NO_MEANING': <class 'str'>} | ||||
|      |  __annotations__ = {'NO_MEANING': 'str'} | ||||
| \x20\x20\x20\x20 | ||||
|     class C(builtins.object) | ||||
|      |  Methods defined here: | ||||
|  | @ -194,7 +194,7 @@ class C(builtins.object) | |||
| Data and other attributes defined here:<br> | ||||
| <dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl> | ||||
| 
 | ||||
| <dl><dt><strong>__annotations__</strong> = {'NO_MEANING': <class 'str'>}</dl> | ||||
| <dl><dt><strong>__annotations__</strong> = {'NO_MEANING': 'str'}</dl> | ||||
| 
 | ||||
| </td></tr></table> <p> | ||||
| <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> | ||||
|  |  | |||
|  | @ -752,14 +752,6 @@ | |||
|     Traceback (most recent call last): | ||||
|     SyntaxError: cannot assign to __debug__ | ||||
| 
 | ||||
|     >>> def f(*args:(lambda __debug__:0)): pass | ||||
|     Traceback (most recent call last): | ||||
|     SyntaxError: cannot assign to __debug__ | ||||
| 
 | ||||
|     >>> def f(**kwargs:(lambda __debug__:0)): pass | ||||
|     Traceback (most recent call last): | ||||
|     SyntaxError: cannot assign to __debug__ | ||||
| 
 | ||||
|     >>> with (lambda *:0): pass | ||||
|     Traceback (most recent call last): | ||||
|     SyntaxError: named arguments must follow bare * | ||||
|  |  | |||
|  | @ -671,8 +671,8 @@ def test_or_type_operator_with_forward(self): | |||
|         ForwardBefore = 'Forward' | T | ||||
|         def forward_after(x: ForwardAfter[int]) -> None: ... | ||||
|         def forward_before(x: ForwardBefore[int]) -> None: ... | ||||
|         assert typing.get_args(typing.get_type_hints(forward_after)['x']) == (int, Forward) | ||||
|         assert typing.get_args(typing.get_type_hints(forward_before)['x']) == (int, Forward) | ||||
|         assert typing.get_args(typing.get_type_hints(forward_after, localns=locals())['x']) == (int, Forward) | ||||
|         assert typing.get_args(typing.get_type_hints(forward_before, localns=locals())['x']) == (int, Forward) | ||||
| 
 | ||||
|     def test_or_type_operator_with_Protocol(self): | ||||
|         class Proto(typing.Protocol): | ||||
|  |  | |||
|  | @ -349,7 +349,7 @@ def test_empty(self): | |||
|     def test_no_eval_union(self): | ||||
|         u = Union[int, str] | ||||
|         def f(x: u): ... | ||||
|         self.assertIs(get_type_hints(f)['x'], u) | ||||
|         self.assertIs(get_type_hints(f, globals(), locals())['x'], u) | ||||
| 
 | ||||
|     def test_function_repr_union(self): | ||||
|         def fun() -> int: ... | ||||
|  | @ -2849,11 +2849,11 @@ def test_get_type_hints_classes(self): | |||
|         self.assertEqual(gth(HasForeignBaseClass), | ||||
|                          {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, | ||||
|                           'some_b': mod_generics_cache.B}) | ||||
|         self.assertEqual(gth(XRepr.__new__), | ||||
|         self.assertEqual(gth(XRepr), | ||||
|                          {'x': int, 'y': int}) | ||||
|         self.assertEqual(gth(mod_generics_cache.B), | ||||
|                          {'my_inner_a1': mod_generics_cache.B.A, | ||||
|                           'my_inner_a2': mod_generics_cache.B.A, | ||||
|                           'my_inner_a2': mod_generics_cache.A, | ||||
|                           'my_outer_a': mod_generics_cache.A}) | ||||
| 
 | ||||
|     def test_respect_no_type_check(self): | ||||
|  | @ -3641,7 +3641,7 @@ def test_annotation_usage(self): | |||
|         self.assertEqual(tim.cool, 9000) | ||||
|         self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') | ||||
|         self.assertEqual(CoolEmployee._fields, ('name', 'cool')) | ||||
|         self.assertEqual(CoolEmployee.__annotations__, | ||||
|         self.assertEqual(gth(CoolEmployee), | ||||
|                          collections.OrderedDict(name=str, cool=int)) | ||||
| 
 | ||||
|     def test_annotation_usage_with_default(self): | ||||
|  | @ -3655,7 +3655,7 @@ def test_annotation_usage_with_default(self): | |||
| 
 | ||||
|         self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault') | ||||
|         self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) | ||||
|         self.assertEqual(CoolEmployeeWithDefault.__annotations__, | ||||
|         self.assertEqual(gth(CoolEmployeeWithDefault), | ||||
|                          dict(name=str, cool=int)) | ||||
|         self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) | ||||
| 
 | ||||
|  | @ -3823,7 +3823,7 @@ def test_typeddict_errors(self): | |||
|     def test_py36_class_syntax_usage(self): | ||||
|         self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') | ||||
|         self.assertEqual(LabelPoint2D.__module__, __name__) | ||||
|         self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str}) | ||||
|         self.assertEqual(gth(LabelPoint2D), {'x': int, 'y': int, 'label': str}) | ||||
|         self.assertEqual(LabelPoint2D.__bases__, (dict,)) | ||||
|         self.assertEqual(LabelPoint2D.__total__, True) | ||||
|         self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) | ||||
|  | @ -3882,11 +3882,11 @@ class Cat(Animal): | |||
| 
 | ||||
|         assert BaseAnimal.__required_keys__ == frozenset(['name']) | ||||
|         assert BaseAnimal.__optional_keys__ == frozenset([]) | ||||
|         assert BaseAnimal.__annotations__ == {'name': str} | ||||
|         assert gth(BaseAnimal) == {'name': str} | ||||
| 
 | ||||
|         assert Animal.__required_keys__ == frozenset(['name']) | ||||
|         assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) | ||||
|         assert Animal.__annotations__ == { | ||||
|         assert gth(Animal) == { | ||||
|             'name': str, | ||||
|             'tail': bool, | ||||
|             'voice': str, | ||||
|  | @ -3894,7 +3894,7 @@ class Cat(Animal): | |||
| 
 | ||||
|         assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) | ||||
|         assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) | ||||
|         assert Cat.__annotations__ == { | ||||
|         assert gth(Cat) == { | ||||
|             'fur_color': str, | ||||
|             'name': str, | ||||
|             'tail': bool, | ||||
|  | @ -3915,7 +3915,7 @@ def test_io(self): | |||
|         def stuff(a: IO) -> AnyStr: | ||||
|             return a.readline() | ||||
| 
 | ||||
|         a = stuff.__annotations__['a'] | ||||
|         a = gth(stuff)['a'] | ||||
|         self.assertEqual(a.__parameters__, (AnyStr,)) | ||||
| 
 | ||||
|     def test_textio(self): | ||||
|  | @ -3923,7 +3923,7 @@ def test_textio(self): | |||
|         def stuff(a: TextIO) -> str: | ||||
|             return a.readline() | ||||
| 
 | ||||
|         a = stuff.__annotations__['a'] | ||||
|         a = gth(stuff)['a'] | ||||
|         self.assertEqual(a.__parameters__, ()) | ||||
| 
 | ||||
|     def test_binaryio(self): | ||||
|  | @ -3931,7 +3931,7 @@ def test_binaryio(self): | |||
|         def stuff(a: BinaryIO) -> bytes: | ||||
|             return a.readline() | ||||
| 
 | ||||
|         a = stuff.__annotations__['a'] | ||||
|         a = gth(stuff)['a'] | ||||
|         self.assertEqual(a.__parameters__, ()) | ||||
| 
 | ||||
|     def test_io_submodule(self): | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ | |||
| """ | ||||
| 
 | ||||
| from abc import abstractmethod, ABCMeta | ||||
| import ast | ||||
| import collections | ||||
| import collections.abc | ||||
| import contextlib | ||||
|  | @ -469,6 +470,13 @@ class ForwardRef(_Final, _root=True): | |||
|     def __init__(self, arg, is_argument=True): | ||||
|         if not isinstance(arg, str): | ||||
|             raise TypeError(f"Forward reference must be a string -- got {arg!r}") | ||||
| 
 | ||||
|         # Double-stringified forward references is a result of activating | ||||
|         # the 'annotations' future by default. This way, we eliminate them in | ||||
|         # the runtime. | ||||
|         if arg.startswith(("'", '\"')) and arg.endswith(("'", '"')): | ||||
|             arg = arg[1:-1] | ||||
| 
 | ||||
|         try: | ||||
|             code = compile(arg, '<string>', 'eval') | ||||
|         except SyntaxError: | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| Enable ``from __future__ import annotations`` (:pep:`563`) by default. | ||||
| The values found in :attr:`__annotations__` dicts are now strings, e.g. | ||||
| ``{"x": "int"}`` instead of ``{"x": int}``. | ||||
|  | @ -392,7 +392,6 @@ static int astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state | |||
| static int astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||
| static int astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||
| static int astfold_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||
| static int astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||
| static int astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||
| static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||
| #define CALL(FUNC, TYPE, ARG) \ | ||||
|  | @ -595,25 +594,11 @@ astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState | |||
| static int | ||||
| astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | ||||
| { | ||||
|     CALL_SEQ(astfold_arg, arg, node_->posonlyargs); | ||||
|     CALL_SEQ(astfold_arg, arg, node_->args); | ||||
|     CALL_OPT(astfold_arg, arg_ty, node_->vararg); | ||||
|     CALL_SEQ(astfold_arg, arg, node_->kwonlyargs); | ||||
|     CALL_SEQ(astfold_expr, expr, node_->kw_defaults); | ||||
|     CALL_OPT(astfold_arg, arg_ty, node_->kwarg); | ||||
|     CALL_SEQ(astfold_expr, expr, node_->defaults); | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | ||||
| { | ||||
|     if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { | ||||
|         CALL_OPT(astfold_expr, expr_ty, node_->annotation); | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | ||||
| { | ||||
|  | @ -622,17 +607,11 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | |||
|         CALL(astfold_arguments, arguments_ty, node_->v.FunctionDef.args); | ||||
|         CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body); | ||||
|         CALL_SEQ(astfold_expr, expr, node_->v.FunctionDef.decorator_list); | ||||
|         if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { | ||||
|             CALL_OPT(astfold_expr, expr_ty, node_->v.FunctionDef.returns); | ||||
|         } | ||||
|         break; | ||||
|     case AsyncFunctionDef_kind: | ||||
|         CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args); | ||||
|         CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body); | ||||
|         CALL_SEQ(astfold_expr, expr, node_->v.AsyncFunctionDef.decorator_list); | ||||
|         if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { | ||||
|             CALL_OPT(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.returns); | ||||
|         } | ||||
|         break; | ||||
|     case ClassDef_kind: | ||||
|         CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.bases); | ||||
|  | @ -656,9 +635,6 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | |||
|         break; | ||||
|     case AnnAssign_kind: | ||||
|         CALL(astfold_expr, expr_ty, node_->v.AnnAssign.target); | ||||
|         if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) { | ||||
|             CALL(astfold_expr, expr_ty, node_->v.AnnAssign.annotation); | ||||
|         } | ||||
|         CALL_OPT(astfold_expr, expr_ty, node_->v.AnnAssign.value); | ||||
|         break; | ||||
|     case For_kind: | ||||
|  |  | |||
|  | @ -2026,12 +2026,7 @@ compiler_visit_argannotation(struct compiler *c, identifier id, | |||
| { | ||||
|     if (annotation) { | ||||
|         PyObject *mangled; | ||||
|         if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { | ||||
|             VISIT(c, annexpr, annotation) | ||||
|         } | ||||
|         else { | ||||
|             VISIT(c, expr, annotation); | ||||
|         } | ||||
|         VISIT(c, annexpr, annotation); | ||||
|         mangled = _Py_Mangle(c->u->u_private, id); | ||||
|         if (!mangled) | ||||
|             return 0; | ||||
|  | @ -5261,12 +5256,7 @@ compiler_annassign(struct compiler *c, stmt_ty s) | |||
|         if (s->v.AnnAssign.simple && | ||||
|             (c->u->u_scope_type == COMPILER_SCOPE_MODULE || | ||||
|              c->u->u_scope_type == COMPILER_SCOPE_CLASS)) { | ||||
|             if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { | ||||
|                 VISIT(c, annexpr, s->v.AnnAssign.annotation) | ||||
|             } | ||||
|             else { | ||||
|                 VISIT(c, expr, s->v.AnnAssign.annotation); | ||||
|             } | ||||
|             VISIT(c, annexpr, s->v.AnnAssign.annotation); | ||||
|             ADDOP_NAME(c, LOAD_NAME, __annotations__, names); | ||||
|             mangled = _Py_Mangle(c->u->u_private, targ->v.Name.id); | ||||
|             ADDOP_LOAD_CONST_NEW(c, mangled); | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename) | |||
|         } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) { | ||||
|             continue; | ||||
|         } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) { | ||||
|             ff->ff_features |= CO_FUTURE_ANNOTATIONS; | ||||
|             continue; | ||||
|         } else if (strcmp(feature, "braces") == 0) { | ||||
|             PyErr_SetString(PyExc_SyntaxError, | ||||
|                             "not a chance"); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Batuhan Taskaya
						Batuhan Taskaya