mirror of
				https://github.com/python/cpython.git
				synced 2025-10-25 02:43:41 +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 | ``*identifier`` or ``**identifier``.  Functions may have "return" annotation of | ||||||
| the form "``-> expression``" after the parameter list.  These annotations can be | the form "``-> expression``" after the parameter list.  These annotations can be | ||||||
| any valid Python expression.  The presence of annotations does not change the | any valid Python expression.  The presence of annotations does not change the | ||||||
| semantics of a function.  The annotation values are available as values of | semantics of a function.  The annotation values are available as string values | ||||||
| a dictionary keyed by the parameters' names in the :attr:`__annotations__` | in a dictionary keyed by the parameters' names in the :attr:`__annotations__` | ||||||
| attribute of the function object.  If the ``annotations`` import from | attribute of the function object. | ||||||
| :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. |  | ||||||
| 
 | 
 | ||||||
| .. index:: pair: lambda; expression | .. index:: pair: lambda; expression | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -70,6 +70,23 @@ Summary -- Release highlights | ||||||
| New Features | 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 | * 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 |   number of ones in the binary expansion of a given integer, also known | ||||||
|   as the population count. (Contributed by Niklas Fiekas in :issue:`29882`.) |   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 = {} |     ns = {} | ||||||
|     exec(txt, globals, 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): | def _field_assign(frozen, name, value, self_name): | ||||||
|     # If we're a frozen class, then assign to our fields in __init__ |     # 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 |     # a eval() penalty for every single field of every dataclass | ||||||
|     # that's defined.  It was judged not worth it. |     # 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) |     match = _MODULE_IDENTIFIER_RE.match(annotation) | ||||||
|     if match: |     if match: | ||||||
|         ns = None |         ns = None | ||||||
|  | @ -991,7 +998,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen): | ||||||
|     if not getattr(cls, '__doc__'): |     if not getattr(cls, '__doc__'): | ||||||
|         # Create a class doc-string. |         # Create a class doc-string. | ||||||
|         cls.__doc__ = (cls.__name__ + |         cls.__doc__ = (cls.__name__ + | ||||||
|                        str(inspect.signature(cls)).replace(' -> None', '')) |                        str(inspect.signature(cls)).replace(' -> NoneType', '')) | ||||||
| 
 | 
 | ||||||
|     abc.update_abstractmethods(cls) |     abc.update_abstractmethods(cls) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -45,6 +45,7 @@ | ||||||
| import tokenize | import tokenize | ||||||
| import token | import token | ||||||
| import types | import types | ||||||
|  | import typing | ||||||
| import warnings | import warnings | ||||||
| import functools | import functools | ||||||
| import builtins | import builtins | ||||||
|  | @ -1877,7 +1878,10 @@ def _signature_is_functionlike(obj): | ||||||
|     code = getattr(obj, '__code__', None) |     code = getattr(obj, '__code__', None) | ||||||
|     defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... |     defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... | ||||||
|     kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here |     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 |     return (isinstance(code, types.CodeType) and | ||||||
|             isinstance(name, str) and |             isinstance(name, str) and | ||||||
|  | @ -2118,6 +2122,16 @@ def p(name_node, default_node, default=empty): | ||||||
| 
 | 
 | ||||||
|     return cls(parameters, return_annotation=cls.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): | def _signature_from_builtin(cls, func, skip_bound_arg=True): | ||||||
|     """Private helper function to get signature for |     """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] |     positional = arg_names[:pos_count] | ||||||
|     keyword_only_count = func_code.co_kwonlyargcount |     keyword_only_count = func_code.co_kwonlyargcount | ||||||
|     keyword_only = arg_names[pos_count:pos_count + keyword_only_count] |     keyword_only = arg_names[pos_count:pos_count + keyword_only_count] | ||||||
|     annotations = func.__annotations__ |     annotations = _get_type_hints(func) | ||||||
|  | 
 | ||||||
|     defaults = func.__defaults__ |     defaults = func.__defaults__ | ||||||
|     kwdefaults = func.__kwdefaults__ |     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 dataclasses | ||||||
| import typing | 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 dataclasses import dataclass, InitVar | ||||||
| from typing import ClassVar | 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 | 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 |                 pass | ||||||
|             """, |             """, | ||||||
| 
 | 
 | ||||||
|             """async def foo(a:await something()): |  | ||||||
|                 pass |  | ||||||
|             """, |  | ||||||
| 
 |  | ||||||
|             """async def foo(): |             """async def foo(): | ||||||
|                 def bar(): |                 def bar(): | ||||||
|                  [i async for i in els] |                  [i async for i in els] | ||||||
|  | @ -299,10 +295,6 @@ def bar(): | ||||||
|                    pass |                    pass | ||||||
|             """, |             """, | ||||||
| 
 | 
 | ||||||
|             """async def foo(a:await b): |  | ||||||
|                    pass |  | ||||||
|             """, |  | ||||||
| 
 |  | ||||||
|             """def baz(): |             """def baz(): | ||||||
|                    async def foo(a=await b): |                    async def foo(a=await b): | ||||||
|                        pass |                        pass | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| import inspect | import inspect | ||||||
| import builtins | import builtins | ||||||
| import unittest | import unittest | ||||||
|  | from textwrap import dedent | ||||||
| from unittest.mock import Mock | from unittest.mock import Mock | ||||||
| from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional | from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional | ||||||
| from typing import get_type_hints | from typing import get_type_hints | ||||||
|  | @ -562,17 +563,17 @@ class C: | ||||||
|         self.assertEqual(len(the_fields), 3) |         self.assertEqual(len(the_fields), 3) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(the_fields[0].name, 'x') |         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.assertFalse(hasattr(C, 'x')) | ||||||
|         self.assertTrue (the_fields[0].init) |         self.assertTrue (the_fields[0].init) | ||||||
|         self.assertTrue (the_fields[0].repr) |         self.assertTrue (the_fields[0].repr) | ||||||
|         self.assertEqual(the_fields[1].name, 'y') |         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.assertIsNone(getattr(C, 'y')) | ||||||
|         self.assertFalse(the_fields[1].init) |         self.assertFalse(the_fields[1].init) | ||||||
|         self.assertTrue (the_fields[1].repr) |         self.assertTrue (the_fields[1].repr) | ||||||
|         self.assertEqual(the_fields[2].name, 'z') |         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.assertFalse(hasattr(C, 'z')) | ||||||
|         self.assertTrue (the_fields[2].init) |         self.assertTrue (the_fields[2].init) | ||||||
|         self.assertFalse(the_fields[2].repr) |         self.assertFalse(the_fields[2].repr) | ||||||
|  | @ -758,11 +759,11 @@ class F: | ||||||
|         def validate_class(cls): |         def validate_class(cls): | ||||||
|             # First, check __annotations__, even though they're not |             # First, check __annotations__, even though they're not | ||||||
|             #  function annotations. |             #  function annotations. | ||||||
|             self.assertEqual(cls.__annotations__['i'], int) |             self.assertEqual(cls.__annotations__['i'], 'int') | ||||||
|             self.assertEqual(cls.__annotations__['j'], str) |             self.assertEqual(cls.__annotations__['j'], 'str') | ||||||
|             self.assertEqual(cls.__annotations__['k'], F) |             self.assertEqual(cls.__annotations__['k'], 'F') | ||||||
|             self.assertEqual(cls.__annotations__['l'], float) |             self.assertEqual(cls.__annotations__['l'], 'float') | ||||||
|             self.assertEqual(cls.__annotations__['z'], complex) |             self.assertEqual(cls.__annotations__['z'], 'complex') | ||||||
| 
 | 
 | ||||||
|             # Verify __init__. |             # Verify __init__. | ||||||
| 
 | 
 | ||||||
|  | @ -777,22 +778,22 @@ def validate_class(cls): | ||||||
|             self.assertEqual(param.name, 'self') |             self.assertEqual(param.name, 'self') | ||||||
|             param = next(params) |             param = next(params) | ||||||
|             self.assertEqual(param.name, 'i') |             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.default, inspect.Parameter.empty) | ||||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) |             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||||
|             param = next(params) |             param = next(params) | ||||||
|             self.assertEqual(param.name, 'j') |             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.default, inspect.Parameter.empty) | ||||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) |             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||||
|             param = next(params) |             param = next(params) | ||||||
|             self.assertEqual(param.name, 'k') |             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. |             # Don't test for the default, since it's set to MISSING. | ||||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) |             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||||
|             param = next(params) |             param = next(params) | ||||||
|             self.assertEqual(param.name, 'l') |             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. |             # Don't test for the default, since it's set to MISSING. | ||||||
|             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) |             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD) | ||||||
|             self.assertRaises(StopIteration, next, params) |             self.assertRaises(StopIteration, next, params) | ||||||
|  | @ -2806,13 +2807,10 @@ class C: | ||||||
| 
 | 
 | ||||||
| class TestStringAnnotations(unittest.TestCase): | class TestStringAnnotations(unittest.TestCase): | ||||||
|     def test_classvar(self): |     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 |         # These tests assume that both "import typing" and "from | ||||||
|         # typing import *" have been run in this file. |         # typing import *" have been run in this file. | ||||||
|         for typestr in ('ClassVar[int]', |         for typestr in ('ClassVar[int]', | ||||||
|                         'ClassVar [int]' |                         'ClassVar [int]', | ||||||
|                         ' ClassVar [int]', |                         ' ClassVar [int]', | ||||||
|                         'ClassVar', |                         'ClassVar', | ||||||
|                         ' ClassVar ', |                         ' ClassVar ', | ||||||
|  | @ -2823,17 +2821,15 @@ def test_classvar(self): | ||||||
|                         'typing. ClassVar[str]', |                         'typing. ClassVar[str]', | ||||||
|                         'typing.ClassVar [str]', |                         'typing.ClassVar [str]', | ||||||
|                         'typing.ClassVar [ str]', |                         'typing.ClassVar [ str]', | ||||||
| 
 |                         # Double stringified | ||||||
|  |                         '"typing.ClassVar[int]"', | ||||||
|                         # Not syntactically valid, but these will |                         # Not syntactically valid, but these will | ||||||
|                         #  be treated as ClassVars. |                         # be treated as ClassVars. | ||||||
|                         'typing.ClassVar.[int]', |                         'typing.ClassVar.[int]', | ||||||
|                         'typing.ClassVar+', |                         'typing.ClassVar+', | ||||||
|                         ): |                         ): | ||||||
|             with self.subTest(typestr=typestr): |             with self.subTest(typestr=typestr): | ||||||
|                 @dataclass |                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||||
|                 class C: |  | ||||||
|                     x: typestr |  | ||||||
| 
 |  | ||||||
|                 # x is a ClassVar, so C() takes no args. |                 # x is a ClassVar, so C() takes no args. | ||||||
|                 C() |                 C() | ||||||
| 
 | 
 | ||||||
|  | @ -2854,9 +2850,7 @@ def test_isnt_classvar(self): | ||||||
|                         'typingxClassVar[str]', |                         'typingxClassVar[str]', | ||||||
|                         ): |                         ): | ||||||
|             with self.subTest(typestr=typestr): |             with self.subTest(typestr=typestr): | ||||||
|                 @dataclass |                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||||
|                 class C: |  | ||||||
|                     x: typestr |  | ||||||
| 
 | 
 | ||||||
|                 # x is not a ClassVar, so C() takes one arg. |                 # x is not a ClassVar, so C() takes one arg. | ||||||
|                 self.assertEqual(C(10).x, 10) |                 self.assertEqual(C(10).x, 10) | ||||||
|  | @ -2876,16 +2870,16 @@ def test_initvar(self): | ||||||
|                         'dataclasses. InitVar[str]', |                         'dataclasses. InitVar[str]', | ||||||
|                         'dataclasses.InitVar [str]', |                         'dataclasses.InitVar [str]', | ||||||
|                         'dataclasses.InitVar [ str]', |                         'dataclasses.InitVar [ str]', | ||||||
| 
 |                         # Double stringified | ||||||
|  |                         '"dataclasses.InitVar[int]"', | ||||||
|                         # Not syntactically valid, but these will |                         # Not syntactically valid, but these will | ||||||
|                         #  be treated as InitVars. |                         #  be treated as InitVars. | ||||||
|                         'dataclasses.InitVar.[int]', |                         'dataclasses.InitVar.[int]', | ||||||
|                         'dataclasses.InitVar+', |                         'dataclasses.InitVar+', | ||||||
|                         ): |                         ): | ||||||
|             with self.subTest(typestr=typestr): |             with self.subTest(typestr=typestr): | ||||||
|                 @dataclass |                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||||
|                 class C: | 
 | ||||||
|                     x: typestr |  | ||||||
| 
 | 
 | ||||||
|                 # x is an InitVar, so doesn't create a member. |                 # x is an InitVar, so doesn't create a member. | ||||||
|                 with self.assertRaisesRegex(AttributeError, |                 with self.assertRaisesRegex(AttributeError, | ||||||
|  | @ -2899,30 +2893,22 @@ def test_isnt_initvar(self): | ||||||
|                         'typing.xInitVar[int]', |                         'typing.xInitVar[int]', | ||||||
|                         ): |                         ): | ||||||
|             with self.subTest(typestr=typestr): |             with self.subTest(typestr=typestr): | ||||||
|                 @dataclass |                 C = dataclass(type("C", (), {"__annotations__": {"x": typestr}})) | ||||||
|                 class C: |  | ||||||
|                     x: typestr |  | ||||||
| 
 | 
 | ||||||
|                 # x is not an InitVar, so there will be a member x. |                 # x is not an InitVar, so there will be a member x. | ||||||
|                 self.assertEqual(C(10).x, 10) |                 self.assertEqual(C(10).x, 10) | ||||||
| 
 | 
 | ||||||
|     def test_classvar_module_level_import(self): |     def test_classvar_module_level_import(self): | ||||||
|         from test import dataclass_module_1 |         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 | ||||||
|         from test import dataclass_module_2_str |  | ||||||
| 
 | 
 | ||||||
|         for m in (dataclass_module_1, dataclass_module_1_str, |         for m in (dataclass_module_1, | ||||||
|                   dataclass_module_2, dataclass_module_2_str, |                   dataclass_module_2): | ||||||
|                   ): |  | ||||||
|             with self.subTest(m=m): |             with self.subTest(m=m): | ||||||
|                 # There's a difference in how the ClassVars are |                 # There's a difference in how the ClassVars are | ||||||
|                 # interpreted when using string annotations or |                 # interpreted when using string annotations or | ||||||
|                 # not. See the imported modules for details. |                 # not. See the imported modules for details. | ||||||
|                 if m.USING_STRINGS: |                 c = m.CV(10) | ||||||
|                     c = m.CV(10) |  | ||||||
|                 else: |  | ||||||
|                     c = m.CV() |  | ||||||
|                 self.assertEqual(c.cv0, 20) |                 self.assertEqual(c.cv0, 20) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -2938,14 +2924,9 @@ def test_classvar_module_level_import(self): | ||||||
|                             # not an instance field. |                             # not an instance field. | ||||||
|                             getattr(c, field_name) |                             getattr(c, field_name) | ||||||
| 
 | 
 | ||||||
|                 if m.USING_STRINGS: |                 # iv4 is interpreted as a normal field. | ||||||
|                     # iv4 is interpreted as a normal field. |                 self.assertIn('not_iv4', c.__dict__) | ||||||
|                     self.assertIn('not_iv4', c.__dict__) |                 self.assertEqual(c.not_iv4, 4) | ||||||
|                     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__) |  | ||||||
| 
 | 
 | ||||||
|     def test_text_annotations(self): |     def test_text_annotations(self): | ||||||
|         from test import dataclass_textanno |         from test import dataclass_textanno | ||||||
|  |  | ||||||
|  | @ -227,28 +227,26 @@ def bug1333982(x=[]): | ||||||
|   2           0 SETUP_ANNOTATIONS |   2           0 SETUP_ANNOTATIONS | ||||||
|               2 LOAD_CONST               0 (1) |               2 LOAD_CONST               0 (1) | ||||||
|               4 STORE_NAME               0 (x) |               4 STORE_NAME               0 (x) | ||||||
|               6 LOAD_NAME                1 (int) |               6 LOAD_CONST               1 ('int') | ||||||
|               8 LOAD_NAME                2 (__annotations__) |               8 LOAD_NAME                1 (__annotations__) | ||||||
|              10 LOAD_CONST               1 ('x') |              10 LOAD_CONST               2 ('x') | ||||||
|              12 STORE_SUBSCR |              12 STORE_SUBSCR | ||||||
| 
 | 
 | ||||||
|   3          14 LOAD_NAME                3 (fun) |   3          14 LOAD_CONST               3 ('fun(1)') | ||||||
|              16 LOAD_CONST               0 (1) |              16 LOAD_NAME                1 (__annotations__) | ||||||
|              18 CALL_FUNCTION            1 |              18 LOAD_CONST               4 ('y') | ||||||
|              20 LOAD_NAME                2 (__annotations__) |              20 STORE_SUBSCR | ||||||
|              22 LOAD_CONST               2 ('y') |  | ||||||
|              24 STORE_SUBSCR |  | ||||||
| 
 | 
 | ||||||
|   4          26 LOAD_CONST               0 (1) |   4          22 LOAD_CONST               0 (1) | ||||||
|              28 LOAD_NAME                4 (lst) |              24 LOAD_NAME                2 (lst) | ||||||
|              30 LOAD_NAME                3 (fun) |              26 LOAD_NAME                3 (fun) | ||||||
|              32 LOAD_CONST               3 (0) |              28 LOAD_CONST               5 (0) | ||||||
|              34 CALL_FUNCTION            1 |              30 CALL_FUNCTION            1 | ||||||
|              36 STORE_SUBSCR |              32 STORE_SUBSCR | ||||||
|              38 LOAD_NAME                1 (int) |              34 LOAD_NAME                4 (int) | ||||||
|              40 POP_TOP |              36 POP_TOP | ||||||
|              42 LOAD_CONST               4 (None) |              38 LOAD_CONST               6 (None) | ||||||
|              44 RETURN_VALUE |              40 RETURN_VALUE | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| compound_stmt_str = """\ | compound_stmt_str = """\ | ||||||
|  |  | ||||||
|  | @ -618,7 +618,7 @@ def check_wrapper(self, wrapper, wrapped, | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def _default_update(self): |     def _default_update(self): | ||||||
|         def f(a:'This is a new annotation'): |         def f(a: int): | ||||||
|             """This is a test""" |             """This is a test""" | ||||||
|             pass |             pass | ||||||
|         f.attr = 'This is also a test' |         f.attr = 'This is also a test' | ||||||
|  | @ -635,7 +635,7 @@ def test_default_update(self): | ||||||
|         self.assertEqual(wrapper.__name__, 'f') |         self.assertEqual(wrapper.__name__, 'f') | ||||||
|         self.assertEqual(wrapper.__qualname__, f.__qualname__) |         self.assertEqual(wrapper.__qualname__, f.__qualname__) | ||||||
|         self.assertEqual(wrapper.attr, 'This is also a test') |         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__) |         self.assertNotIn('b', wrapper.__annotations__) | ||||||
| 
 | 
 | ||||||
|     @unittest.skipIf(sys.flags.optimize >= 2, |     @unittest.skipIf(sys.flags.optimize >= 2, | ||||||
|  |  | ||||||
|  | @ -362,7 +362,7 @@ class C: | ||||||
|             z = 2 |             z = 2 | ||||||
|             def __init__(self, x): |             def __init__(self, x): | ||||||
|                 self.x: int = 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): |         with self.assertRaises(NameError): | ||||||
|             class CBad: |             class CBad: | ||||||
|                 no_such_name_defined.attr: int = 0 |                 no_such_name_defined.attr: int = 0 | ||||||
|  | @ -378,15 +378,15 @@ def __prepare__(metacls, name, bases, **kwds): | ||||||
|                 return {'__annotations__': CNS()} |                 return {'__annotations__': CNS()} | ||||||
|         class CC(metaclass=CMeta): |         class CC(metaclass=CMeta): | ||||||
|             XX: 'ANNOT' |             XX: 'ANNOT' | ||||||
|         self.assertEqual(CC.__annotations__['xx'], 'ANNOT') |         self.assertEqual(CC.__annotations__['xx'], repr('ANNOT')) | ||||||
| 
 | 
 | ||||||
|     def test_var_annot_module_semantics(self): |     def test_var_annot_module_semantics(self): | ||||||
|         with self.assertRaises(AttributeError): |         with self.assertRaises(AttributeError): | ||||||
|             print(test.__annotations__) |             print(test.__annotations__) | ||||||
|         self.assertEqual(ann_module.__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__, |         self.assertEqual(ann_module.M.__annotations__, | ||||||
|                               {'123': 123, 'o': type}) |                               {'123': 123, 'o': 'type'}) | ||||||
|         self.assertEqual(ann_module2.__annotations__, {}) |         self.assertEqual(ann_module2.__annotations__, {}) | ||||||
| 
 | 
 | ||||||
|     def test_var_annot_in_module(self): |     def test_var_annot_in_module(self): | ||||||
|  | @ -405,7 +405,7 @@ def test_var_annot_simple_exec(self): | ||||||
|         exec("'docstring'\n" |         exec("'docstring'\n" | ||||||
|              "__annotations__[1] = 2\n" |              "__annotations__[1] = 2\n" | ||||||
|              "x: int = 5\n", gns, lns) |              "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): |         with self.assertRaises(KeyError): | ||||||
|             gns['__annotations__'] |             gns['__annotations__'] | ||||||
| 
 | 
 | ||||||
|  | @ -413,8 +413,8 @@ def test_var_annot_custom_maps(self): | ||||||
|         # tests with custom locals() and __annotations__ |         # tests with custom locals() and __annotations__ | ||||||
|         ns = {'__annotations__': CNS()} |         ns = {'__annotations__': CNS()} | ||||||
|         exec('X: int; Z: str = "Z"; (w): complex = 1j', ns) |         exec('X: int; Z: str = "Z"; (w): complex = 1j', ns) | ||||||
|         self.assertEqual(ns['__annotations__']['x'], int) |         self.assertEqual(ns['__annotations__']['x'], 'int') | ||||||
|         self.assertEqual(ns['__annotations__']['z'], str) |         self.assertEqual(ns['__annotations__']['z'], 'str') | ||||||
|         with self.assertRaises(KeyError): |         with self.assertRaises(KeyError): | ||||||
|             ns['__annotations__']['w'] |             ns['__annotations__']['w'] | ||||||
|         nonloc_ns = {} |         nonloc_ns = {} | ||||||
|  | @ -428,7 +428,7 @@ def __setitem__(self, item, value): | ||||||
|             def __getitem__(self, item): |             def __getitem__(self, item): | ||||||
|                 return self._dct[item] |                 return self._dct[item] | ||||||
|         exec('x: int = 1', {}, CNS2()) |         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): |     def test_var_annot_refleak(self): | ||||||
|         # complex case: custom locals plus custom __annotations__ |         # complex case: custom locals plus custom __annotations__ | ||||||
|  | @ -445,7 +445,7 @@ def __setitem__(self, item, value): | ||||||
|             def __getitem__(self, item): |             def __getitem__(self, item): | ||||||
|                 return self._dct[item] |                 return self._dct[item] | ||||||
|         exec('X: str', {}, CNS2()) |         exec('X: str', {}, CNS2()) | ||||||
|         self.assertEqual(nonloc_ns['__annotations__']['x'], str) |         self.assertEqual(nonloc_ns['__annotations__']['x'], 'str') | ||||||
| 
 | 
 | ||||||
|     def test_var_annot_rhs(self): |     def test_var_annot_rhs(self): | ||||||
|         ns = {} |         ns = {} | ||||||
|  | @ -625,50 +625,46 @@ def f(*args, **kwargs): | ||||||
| 
 | 
 | ||||||
|         # argument annotation tests |         # argument annotation tests | ||||||
|         def f(x) -> list: pass |         def f(x) -> list: pass | ||||||
|         self.assertEqual(f.__annotations__, {'return': list}) |         self.assertEqual(f.__annotations__, {'return': 'list'}) | ||||||
|         def f(x: int): pass |         def f(x: int): pass | ||||||
|         self.assertEqual(f.__annotations__, {'x': int}) |         self.assertEqual(f.__annotations__, {'x': 'int'}) | ||||||
|         def f(x: int, /): pass |         def f(x: int, /): pass | ||||||
|         self.assertEqual(f.__annotations__, {'x': int}) |         self.assertEqual(f.__annotations__, {'x': 'int'}) | ||||||
|         def f(x: int = 34, /): pass |         def f(x: int = 34, /): pass | ||||||
|         self.assertEqual(f.__annotations__, {'x': int}) |         self.assertEqual(f.__annotations__, {'x': 'int'}) | ||||||
|         def f(*x: str): pass |         def f(*x: str): pass | ||||||
|         self.assertEqual(f.__annotations__, {'x': str}) |         self.assertEqual(f.__annotations__, {'x': 'str'}) | ||||||
|         def f(**x: float): pass |         def f(**x: float): pass | ||||||
|         self.assertEqual(f.__annotations__, {'x': float}) |         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}) |  | ||||||
|         def f(a, b: 1, c: 2, d): pass |         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 |         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 |         def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6): pass | ||||||
|         self.assertEqual(f.__annotations__, |         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, |         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 |               **k: 11) -> 12: pass | ||||||
|         self.assertEqual(f.__annotations__, |         self.assertEqual(f.__annotations__, | ||||||
|                          {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, |                          {'b': '1', 'c': '2', 'e': '3', 'g': '6', 'h': '7', 'j': '9', | ||||||
|                           'k': 11, 'return': 12}) |                           '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, |         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 |               **k: 11) -> 12: pass | ||||||
|         self.assertEqual(f.__annotations__, |         self.assertEqual(f.__annotations__, | ||||||
|                           {'b': 1, 'c': 2, 'e': 3, 'f': int, 'g': 6, 'h': 7, 'j': 9, |                           {'b': '1', 'c': '2', 'e': '3', 'f': 'int', 'g': '6', 'h': '7', 'j': '9', | ||||||
|                            'k': 11, 'return': 12}) |                            'k': '11', 'return': '12'}) | ||||||
|         # Check for issue #20625 -- annotations mangling |         # Check for issue #20625 -- annotations mangling | ||||||
|         class Spam: |         class Spam: | ||||||
|             def f(self, *, __kw: 1): |             def f(self, *, __kw: 1): | ||||||
|                 pass |                 pass | ||||||
|         class Ham(Spam): pass |         class Ham(Spam): pass | ||||||
|         self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': 1}) |         self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': '1'}) | ||||||
|         self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': 1}) |         self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': '1'}) | ||||||
|         # Check for SF Bug #1697248 - mixing decorators and a return annotation |         # Check for SF Bug #1697248 - mixing decorators and a return annotation | ||||||
|         def null(x): return x |         def null(x): return x | ||||||
|         @null |         @null | ||||||
|         def f(x) -> list: pass |         def f(x) -> list: pass | ||||||
|         self.assertEqual(f.__annotations__, {'return': list}) |         self.assertEqual(f.__annotations__, {'return': 'list'}) | ||||||
| 
 | 
 | ||||||
|         # Test expressions as decorators (PEP 614): |         # Test expressions as decorators (PEP 614): | ||||||
|         @False or null |         @False or null | ||||||
|  | @ -1116,8 +1112,6 @@ def g(): rest = 4, 5, 6; yield 1, 2, 3, *rest | ||||||
|         # Not allowed at class scope |         # Not allowed at class scope | ||||||
|         check_syntax_error(self, "class foo:yield 1") |         check_syntax_error(self, "class foo:yield 1") | ||||||
|         check_syntax_error(self, "class foo:yield from ()") |         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): |     def test_yield_in_comprehensions(self): | ||||||
|         # Check yield in comprehensions |         # Check yield in comprehensions | ||||||
|  |  | ||||||
|  | @ -862,7 +862,7 @@ def test_getfullargspec(self): | ||||||
| 
 | 
 | ||||||
|         self.assertFullArgSpecEquals(mod2.annotated, ['arg1'], |         self.assertFullArgSpecEquals(mod2.annotated, ['arg1'], | ||||||
|                                      ann_e={'arg1' : list}, |                                      ann_e={'arg1' : list}, | ||||||
|                                      formatted='(arg1: list)') |                                      formatted="(arg1: list)") | ||||||
|         self.assertFullArgSpecEquals(mod2.keyword_only_arg, [], |         self.assertFullArgSpecEquals(mod2.keyword_only_arg, [], | ||||||
|                                      kwonlyargs_e=['arg'], |                                      kwonlyargs_e=['arg'], | ||||||
|                                      formatted='(*, arg)') |                                      formatted='(*, arg)') | ||||||
|  | @ -2211,8 +2211,8 @@ def test(a, b:'foo') -> 123: | ||||||
|             pass |             pass | ||||||
|         self.assertEqual(self.signature(test), |         self.assertEqual(self.signature(test), | ||||||
|                          ((('a', ..., ..., "positional_or_keyword"), |                          ((('a', ..., ..., "positional_or_keyword"), | ||||||
|                            ('b', ..., 'foo', "positional_or_keyword")), |                            ('b', ..., repr('foo'), "positional_or_keyword")), | ||||||
|                           123)) |                           '123')) | ||||||
| 
 | 
 | ||||||
|     def test_signature_on_wkwonly(self): |     def test_signature_on_wkwonly(self): | ||||||
|         def test(*, a:float, b:str) -> int: |         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 |             pass | ||||||
|         self.assertEqual(self.signature(test), |         self.assertEqual(self.signature(test), | ||||||
|                          ((('a', ..., ..., "positional_or_keyword"), |                          ((('a', ..., ..., "positional_or_keyword"), | ||||||
|                            ('b', 10, 'foo', "positional_or_keyword"), |                            ('b', 10, repr('foo'), "positional_or_keyword"), | ||||||
|                            ('args', ..., 'bar', "var_positional"), |                            ('args', ..., repr('bar'), "var_positional"), | ||||||
|                            ('spam', ..., 'baz', "keyword_only"), |                            ('spam', ..., repr('baz'), "keyword_only"), | ||||||
|                            ('ham', 123, ..., "keyword_only"), |                            ('ham', 123, ..., "keyword_only"), | ||||||
|                            ('kwargs', ..., int, "var_keyword")), |                            ('kwargs', ..., 'int', "var_keyword")), | ||||||
|                           ...)) |                           ...)) | ||||||
| 
 | 
 | ||||||
|     def test_signature_without_self(self): |     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))), |         self.assertEqual(self.signature(partial(partial(test, 1))), | ||||||
|                          ((('b', ..., ..., "positional_or_keyword"), |                          ((('b', ..., ..., "positional_or_keyword"), | ||||||
|                            ('c', ..., int, "positional_or_keyword")), |                            ('c', ..., 'int', "positional_or_keyword")), | ||||||
|                           42)) |                           '42')) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(self.signature(partial(partial(test, 1), 2)), |         self.assertEqual(self.signature(partial(partial(test, 1), 2)), | ||||||
|                          ((('c', ..., int, "positional_or_keyword"),), |                          ((('c', ..., 'int', "positional_or_keyword"),), | ||||||
|                           42)) |                           '42')) | ||||||
| 
 | 
 | ||||||
|         psig = inspect.signature(partial(partial(test, 1), 2)) |         psig = inspect.signature(partial(partial(test, 1), 2)) | ||||||
| 
 | 
 | ||||||
|  | @ -2764,12 +2764,12 @@ def test(it, a, *, c) -> 'spam': | ||||||
|                          ((('it', ..., ..., 'positional_or_keyword'), |                          ((('it', ..., ..., 'positional_or_keyword'), | ||||||
|                            ('a', ..., ..., 'positional_or_keyword'), |                            ('a', ..., ..., 'positional_or_keyword'), | ||||||
|                            ('c', 1, ..., 'keyword_only')), |                            ('c', 1, ..., 'keyword_only')), | ||||||
|                           'spam')) |                           repr('spam'))) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(self.signature(Spam().ham), |         self.assertEqual(self.signature(Spam().ham), | ||||||
|                          ((('a', ..., ..., 'positional_or_keyword'), |                          ((('a', ..., ..., 'positional_or_keyword'), | ||||||
|                            ('c', 1, ..., 'keyword_only')), |                            ('c', 1, ..., 'keyword_only')), | ||||||
|                           'spam')) |                           repr('spam'))) | ||||||
| 
 | 
 | ||||||
|         class Spam: |         class Spam: | ||||||
|             def test(self: 'anno', x): |             def test(self: 'anno', x): | ||||||
|  | @ -2778,7 +2778,7 @@ def test(self: 'anno', x): | ||||||
|             g = partialmethod(test, 1) |             g = partialmethod(test, 1) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(self.signature(Spam.g), |         self.assertEqual(self.signature(Spam.g), | ||||||
|                          ((('self', ..., 'anno', 'positional_or_keyword'),), |                          ((('self', ..., repr('anno'), 'positional_or_keyword'),), | ||||||
|                           ...)) |                           ...)) | ||||||
| 
 | 
 | ||||||
|     def test_signature_on_fake_partialmethod(self): |     def test_signature_on_fake_partialmethod(self): | ||||||
|  | @ -3116,20 +3116,16 @@ def foo(a={}): pass | ||||||
|         with self.assertRaisesRegex(TypeError, 'unhashable type'): |         with self.assertRaisesRegex(TypeError, 'unhashable type'): | ||||||
|             hash(inspect.signature(foo)) |             hash(inspect.signature(foo)) | ||||||
| 
 | 
 | ||||||
|         def foo(a) -> {}: pass |  | ||||||
|         with self.assertRaisesRegex(TypeError, 'unhashable type'): |  | ||||||
|             hash(inspect.signature(foo)) |  | ||||||
| 
 |  | ||||||
|     def test_signature_str(self): |     def test_signature_str(self): | ||||||
|         def foo(a:int=1, *, b, c=None, **kwargs) -> 42: |         def foo(a:int=1, *, b, c=None, **kwargs) -> 42: | ||||||
|             pass |             pass | ||||||
|         self.assertEqual(str(inspect.signature(foo)), |         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: |         def foo(a:int=1, *args, b, c=None, **kwargs) -> 42: | ||||||
|             pass |             pass | ||||||
|         self.assertEqual(str(inspect.signature(foo)), |         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(): |         def foo(): | ||||||
|             pass |             pass | ||||||
|  | @ -3172,8 +3168,8 @@ def test() -> 42: | ||||||
|         self.assertIs(sig.return_annotation, None) |         self.assertIs(sig.return_annotation, None) | ||||||
|         sig = sig.replace(return_annotation=sig.empty) |         sig = sig.replace(return_annotation=sig.empty) | ||||||
|         self.assertIs(sig.return_annotation, sig.empty) |         self.assertIs(sig.return_annotation, sig.empty) | ||||||
|         sig = sig.replace(return_annotation=42) |         sig = sig.replace(return_annotation='42') | ||||||
|         self.assertEqual(sig.return_annotation, 42) |         self.assertEqual(sig.return_annotation, '42') | ||||||
|         self.assertEqual(sig, inspect.signature(test)) |         self.assertEqual(sig, inspect.signature(test)) | ||||||
| 
 | 
 | ||||||
|     def test_signature_on_mangled_parameters(self): |     def test_signature_on_mangled_parameters(self): | ||||||
|  | @ -3185,8 +3181,8 @@ class Ham(Spam): | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(self.signature(Spam.foo), |         self.assertEqual(self.signature(Spam.foo), | ||||||
|                          ((('self', ..., ..., "positional_or_keyword"), |                          ((('self', ..., ..., "positional_or_keyword"), | ||||||
|                            ('_Spam__p1', 2, 1, "positional_or_keyword"), |                            ('_Spam__p1', 2, '1', "positional_or_keyword"), | ||||||
|                            ('_Spam__p2', 3, 2, "keyword_only")), |                            ('_Spam__p2', 3, '2', "keyword_only")), | ||||||
|                           ...)) |                           ...)) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(self.signature(Spam.foo), |         self.assertEqual(self.signature(Spam.foo), | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ class C: pass | ||||||
|     def test_use_existing_annotations(self): |     def test_use_existing_annotations(self): | ||||||
|         ns = {'__annotations__': {1: 2}} |         ns = {'__annotations__': {1: 2}} | ||||||
|         exec('x: int', ns) |         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): |     def test_do_not_recreate_annotations(self): | ||||||
|         # Don't rely on the existence of the '__annotations__' global. |         # Don't rely on the existence of the '__annotations__' global. | ||||||
|  |  | ||||||
|  | @ -302,14 +302,14 @@ def inner_has_pos_only(): | ||||||
|             def f(x: int, /): ... |             def f(x: int, /): ... | ||||||
|             return f |             return f | ||||||
| 
 | 
 | ||||||
|         assert inner_has_pos_only().__annotations__ == {'x': int} |         assert inner_has_pos_only().__annotations__ == {'x': 'int'} | ||||||
| 
 | 
 | ||||||
|         class Something: |         class Something: | ||||||
|             def method(self): |             def method(self): | ||||||
|                 def f(x: int, /): ... |                 def f(x: int, /): ... | ||||||
|                 return f |                 return f | ||||||
| 
 | 
 | ||||||
|         assert Something().method().__annotations__ == {'x': int} |         assert Something().method().__annotations__ == {'x': 'int'} | ||||||
| 
 | 
 | ||||||
|         def multiple_levels(): |         def multiple_levels(): | ||||||
|             def inner_has_pos_only(): |             def inner_has_pos_only(): | ||||||
|  | @ -317,7 +317,7 @@ def f(x: int, /): ... | ||||||
|                 return f |                 return f | ||||||
|             return inner_has_pos_only() |             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 test_same_keyword_as_positional_with_kwargs(self): | ||||||
|         def f(something,/,**kwargs): |         def f(something,/,**kwargs): | ||||||
|  | @ -429,17 +429,6 @@ def method(self, /): | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(C().method(), sentinel) |         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__": | if __name__ == "__main__": | ||||||
|     unittest.main() |     unittest.main() | ||||||
|  |  | ||||||
|  | @ -81,7 +81,7 @@ class B(builtins.object) | ||||||
|      |\x20\x20 |      |\x20\x20 | ||||||
|      |  NO_MEANING = 'eggs' |      |  NO_MEANING = 'eggs' | ||||||
|      |\x20\x20 |      |\x20\x20 | ||||||
|      |  __annotations__ = {'NO_MEANING': <class 'str'>} |      |  __annotations__ = {'NO_MEANING': 'str'} | ||||||
| \x20\x20\x20\x20 | \x20\x20\x20\x20 | ||||||
|     class C(builtins.object) |     class C(builtins.object) | ||||||
|      |  Methods defined here: |      |  Methods defined here: | ||||||
|  | @ -194,7 +194,7 @@ class C(builtins.object) | ||||||
| Data and other attributes defined here:<br> | Data and other attributes defined here:<br> | ||||||
| <dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl> | <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> | </td></tr></table> <p> | ||||||
| <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> | <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> | ||||||
|  |  | ||||||
|  | @ -752,14 +752,6 @@ | ||||||
|     Traceback (most recent call last): |     Traceback (most recent call last): | ||||||
|     SyntaxError: cannot assign to __debug__ |     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 |     >>> with (lambda *:0): pass | ||||||
|     Traceback (most recent call last): |     Traceback (most recent call last): | ||||||
|     SyntaxError: named arguments must follow bare * |     SyntaxError: named arguments must follow bare * | ||||||
|  |  | ||||||
|  | @ -671,8 +671,8 @@ def test_or_type_operator_with_forward(self): | ||||||
|         ForwardBefore = 'Forward' | T |         ForwardBefore = 'Forward' | T | ||||||
|         def forward_after(x: ForwardAfter[int]) -> None: ... |         def forward_after(x: ForwardAfter[int]) -> None: ... | ||||||
|         def forward_before(x: ForwardBefore[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_after, localns=locals())['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_before, localns=locals())['x']) == (int, Forward) | ||||||
| 
 | 
 | ||||||
|     def test_or_type_operator_with_Protocol(self): |     def test_or_type_operator_with_Protocol(self): | ||||||
|         class Proto(typing.Protocol): |         class Proto(typing.Protocol): | ||||||
|  |  | ||||||
|  | @ -349,7 +349,7 @@ def test_empty(self): | ||||||
|     def test_no_eval_union(self): |     def test_no_eval_union(self): | ||||||
|         u = Union[int, str] |         u = Union[int, str] | ||||||
|         def f(x: u): ... |         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 test_function_repr_union(self): | ||||||
|         def fun() -> int: ... |         def fun() -> int: ... | ||||||
|  | @ -2849,11 +2849,11 @@ def test_get_type_hints_classes(self): | ||||||
|         self.assertEqual(gth(HasForeignBaseClass), |         self.assertEqual(gth(HasForeignBaseClass), | ||||||
|                          {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, |                          {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, | ||||||
|                           'some_b': mod_generics_cache.B}) |                           'some_b': mod_generics_cache.B}) | ||||||
|         self.assertEqual(gth(XRepr.__new__), |         self.assertEqual(gth(XRepr), | ||||||
|                          {'x': int, 'y': int}) |                          {'x': int, 'y': int}) | ||||||
|         self.assertEqual(gth(mod_generics_cache.B), |         self.assertEqual(gth(mod_generics_cache.B), | ||||||
|                          {'my_inner_a1': mod_generics_cache.B.A, |                          {'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}) |                           'my_outer_a': mod_generics_cache.A}) | ||||||
| 
 | 
 | ||||||
|     def test_respect_no_type_check(self): |     def test_respect_no_type_check(self): | ||||||
|  | @ -3641,7 +3641,7 @@ def test_annotation_usage(self): | ||||||
|         self.assertEqual(tim.cool, 9000) |         self.assertEqual(tim.cool, 9000) | ||||||
|         self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') |         self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') | ||||||
|         self.assertEqual(CoolEmployee._fields, ('name', 'cool')) |         self.assertEqual(CoolEmployee._fields, ('name', 'cool')) | ||||||
|         self.assertEqual(CoolEmployee.__annotations__, |         self.assertEqual(gth(CoolEmployee), | ||||||
|                          collections.OrderedDict(name=str, cool=int)) |                          collections.OrderedDict(name=str, cool=int)) | ||||||
| 
 | 
 | ||||||
|     def test_annotation_usage_with_default(self): |     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.__name__, 'CoolEmployeeWithDefault') | ||||||
|         self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) |         self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) | ||||||
|         self.assertEqual(CoolEmployeeWithDefault.__annotations__, |         self.assertEqual(gth(CoolEmployeeWithDefault), | ||||||
|                          dict(name=str, cool=int)) |                          dict(name=str, cool=int)) | ||||||
|         self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) |         self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) | ||||||
| 
 | 
 | ||||||
|  | @ -3823,7 +3823,7 @@ def test_typeddict_errors(self): | ||||||
|     def test_py36_class_syntax_usage(self): |     def test_py36_class_syntax_usage(self): | ||||||
|         self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') |         self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') | ||||||
|         self.assertEqual(LabelPoint2D.__module__, __name__) |         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.__bases__, (dict,)) | ||||||
|         self.assertEqual(LabelPoint2D.__total__, True) |         self.assertEqual(LabelPoint2D.__total__, True) | ||||||
|         self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) |         self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) | ||||||
|  | @ -3882,11 +3882,11 @@ class Cat(Animal): | ||||||
| 
 | 
 | ||||||
|         assert BaseAnimal.__required_keys__ == frozenset(['name']) |         assert BaseAnimal.__required_keys__ == frozenset(['name']) | ||||||
|         assert BaseAnimal.__optional_keys__ == frozenset([]) |         assert BaseAnimal.__optional_keys__ == frozenset([]) | ||||||
|         assert BaseAnimal.__annotations__ == {'name': str} |         assert gth(BaseAnimal) == {'name': str} | ||||||
| 
 | 
 | ||||||
|         assert Animal.__required_keys__ == frozenset(['name']) |         assert Animal.__required_keys__ == frozenset(['name']) | ||||||
|         assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) |         assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) | ||||||
|         assert Animal.__annotations__ == { |         assert gth(Animal) == { | ||||||
|             'name': str, |             'name': str, | ||||||
|             'tail': bool, |             'tail': bool, | ||||||
|             'voice': str, |             'voice': str, | ||||||
|  | @ -3894,7 +3894,7 @@ class Cat(Animal): | ||||||
| 
 | 
 | ||||||
|         assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) |         assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) | ||||||
|         assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) |         assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) | ||||||
|         assert Cat.__annotations__ == { |         assert gth(Cat) == { | ||||||
|             'fur_color': str, |             'fur_color': str, | ||||||
|             'name': str, |             'name': str, | ||||||
|             'tail': bool, |             'tail': bool, | ||||||
|  | @ -3915,7 +3915,7 @@ def test_io(self): | ||||||
|         def stuff(a: IO) -> AnyStr: |         def stuff(a: IO) -> AnyStr: | ||||||
|             return a.readline() |             return a.readline() | ||||||
| 
 | 
 | ||||||
|         a = stuff.__annotations__['a'] |         a = gth(stuff)['a'] | ||||||
|         self.assertEqual(a.__parameters__, (AnyStr,)) |         self.assertEqual(a.__parameters__, (AnyStr,)) | ||||||
| 
 | 
 | ||||||
|     def test_textio(self): |     def test_textio(self): | ||||||
|  | @ -3923,7 +3923,7 @@ def test_textio(self): | ||||||
|         def stuff(a: TextIO) -> str: |         def stuff(a: TextIO) -> str: | ||||||
|             return a.readline() |             return a.readline() | ||||||
| 
 | 
 | ||||||
|         a = stuff.__annotations__['a'] |         a = gth(stuff)['a'] | ||||||
|         self.assertEqual(a.__parameters__, ()) |         self.assertEqual(a.__parameters__, ()) | ||||||
| 
 | 
 | ||||||
|     def test_binaryio(self): |     def test_binaryio(self): | ||||||
|  | @ -3931,7 +3931,7 @@ def test_binaryio(self): | ||||||
|         def stuff(a: BinaryIO) -> bytes: |         def stuff(a: BinaryIO) -> bytes: | ||||||
|             return a.readline() |             return a.readline() | ||||||
| 
 | 
 | ||||||
|         a = stuff.__annotations__['a'] |         a = gth(stuff)['a'] | ||||||
|         self.assertEqual(a.__parameters__, ()) |         self.assertEqual(a.__parameters__, ()) | ||||||
| 
 | 
 | ||||||
|     def test_io_submodule(self): |     def test_io_submodule(self): | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from abc import abstractmethod, ABCMeta | from abc import abstractmethod, ABCMeta | ||||||
|  | import ast | ||||||
| import collections | import collections | ||||||
| import collections.abc | import collections.abc | ||||||
| import contextlib | import contextlib | ||||||
|  | @ -469,6 +470,13 @@ class ForwardRef(_Final, _root=True): | ||||||
|     def __init__(self, arg, is_argument=True): |     def __init__(self, arg, is_argument=True): | ||||||
|         if not isinstance(arg, str): |         if not isinstance(arg, str): | ||||||
|             raise TypeError(f"Forward reference must be a string -- got {arg!r}") |             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: |         try: | ||||||
|             code = compile(arg, '<string>', 'eval') |             code = compile(arg, '<string>', 'eval') | ||||||
|         except SyntaxError: |         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_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||||
| static int astfold_comprehension(comprehension_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_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_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||||
| static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); | ||||||
| #define CALL(FUNC, TYPE, ARG) \ | #define CALL(FUNC, TYPE, ARG) \ | ||||||
|  | @ -595,25 +594,11 @@ astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState | ||||||
| static int | static int | ||||||
| astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | 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_SEQ(astfold_expr, expr, node_->kw_defaults); | ||||||
|     CALL_OPT(astfold_arg, arg_ty, node_->kwarg); |  | ||||||
|     CALL_SEQ(astfold_expr, expr, node_->defaults); |     CALL_SEQ(astfold_expr, expr, node_->defaults); | ||||||
|     return 1; |     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 | static int | ||||||
| astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | 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_arguments, arguments_ty, node_->v.FunctionDef.args); | ||||||
|         CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body); |         CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body); | ||||||
|         CALL_SEQ(astfold_expr, expr, node_->v.FunctionDef.decorator_list); |         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; |         break; | ||||||
|     case AsyncFunctionDef_kind: |     case AsyncFunctionDef_kind: | ||||||
|         CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args); |         CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args); | ||||||
|         CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body); |         CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body); | ||||||
|         CALL_SEQ(astfold_expr, expr, node_->v.AsyncFunctionDef.decorator_list); |         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; |         break; | ||||||
|     case ClassDef_kind: |     case ClassDef_kind: | ||||||
|         CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.bases); |         CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.bases); | ||||||
|  | @ -656,9 +635,6 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) | ||||||
|         break; |         break; | ||||||
|     case AnnAssign_kind: |     case AnnAssign_kind: | ||||||
|         CALL(astfold_expr, expr_ty, node_->v.AnnAssign.target); |         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); |         CALL_OPT(astfold_expr, expr_ty, node_->v.AnnAssign.value); | ||||||
|         break; |         break; | ||||||
|     case For_kind: |     case For_kind: | ||||||
|  |  | ||||||
|  | @ -2026,12 +2026,7 @@ compiler_visit_argannotation(struct compiler *c, identifier id, | ||||||
| { | { | ||||||
|     if (annotation) { |     if (annotation) { | ||||||
|         PyObject *mangled; |         PyObject *mangled; | ||||||
|         if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { |         VISIT(c, annexpr, annotation); | ||||||
|             VISIT(c, annexpr, annotation) |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             VISIT(c, expr, annotation); |  | ||||||
|         } |  | ||||||
|         mangled = _Py_Mangle(c->u->u_private, id); |         mangled = _Py_Mangle(c->u->u_private, id); | ||||||
|         if (!mangled) |         if (!mangled) | ||||||
|             return 0; |             return 0; | ||||||
|  | @ -5261,12 +5256,7 @@ compiler_annassign(struct compiler *c, stmt_ty s) | ||||||
|         if (s->v.AnnAssign.simple && |         if (s->v.AnnAssign.simple && | ||||||
|             (c->u->u_scope_type == COMPILER_SCOPE_MODULE || |             (c->u->u_scope_type == COMPILER_SCOPE_MODULE || | ||||||
|              c->u->u_scope_type == COMPILER_SCOPE_CLASS)) { |              c->u->u_scope_type == COMPILER_SCOPE_CLASS)) { | ||||||
|             if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { |             VISIT(c, annexpr, s->v.AnnAssign.annotation); | ||||||
|                 VISIT(c, annexpr, s->v.AnnAssign.annotation) |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 VISIT(c, expr, s->v.AnnAssign.annotation); |  | ||||||
|             } |  | ||||||
|             ADDOP_NAME(c, LOAD_NAME, __annotations__, names); |             ADDOP_NAME(c, LOAD_NAME, __annotations__, names); | ||||||
|             mangled = _Py_Mangle(c->u->u_private, targ->v.Name.id); |             mangled = _Py_Mangle(c->u->u_private, targ->v.Name.id); | ||||||
|             ADDOP_LOAD_CONST_NEW(c, mangled); |             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) { |         } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) { | ||||||
|             continue; |             continue; | ||||||
|         } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) { |         } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) { | ||||||
|             ff->ff_features |= CO_FUTURE_ANNOTATIONS; |             continue; | ||||||
|         } else if (strcmp(feature, "braces") == 0) { |         } else if (strcmp(feature, "braces") == 0) { | ||||||
|             PyErr_SetString(PyExc_SyntaxError, |             PyErr_SetString(PyExc_SyntaxError, | ||||||
|                             "not a chance"); |                             "not a chance"); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Batuhan Taskaya
						Batuhan Taskaya