-
-
Notifications
You must be signed in to change notification settings - Fork 32k
bpo-38605: Make postponed evaluation of annotations default #20434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
4c5b314
make future annotations default
isidentical 3402877
Fix test_coroutines
isidentical 9a131a2
Regenerate bytecode for test_dis
isidentical fdcea4d
Regenerate static strings for docxmlrpc and pydoc tests
isidentical 14e7c2d
Update tests for: syntax, opcodes, posonly, grammar...
isidentical f217d48
Refactor inspect tests(+doctests) with string annotations
isidentical 6209fa0
Add documentation, tests for annotations
isidentical bbf01bb
Escape double stringified annotations on typing.ForwardRef
isidentical 246577d
Fix test_dataclassess
isidentical 1d77917
Initial attempt for typing fix
isidentical 6bd5fee
Rename test, apply doc suggestion
isidentical 1e2c3f8
Adapt code into the latest changes
isidentical 0a04da8
Support double-stringified annotations in dataclasses
isidentical 6d1feb5
Use get_type_hints in inspect.signature
isidentical 86181e4
remove reduntant test
isidentical 4276284
Add what's new entry
isidentical 5ae4bd1
mention about inspect.signature
isidentical 1e73aa7
8000
Tweak what's new words
gvanrossum File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add documentation, tests for annotations
- Loading branch information
commit 6209fa021a194ca9beca184fef904d03f6dc27a7
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
import unittest | ||
from textwrap import dedent | ||
import sys | ||
|
||
class AnnotationsFutureTestCase(unittest.TestCase): | ||
template = dedent( | ||
""" | ||
from __future__ import annotations | ||
def f() -> {ann}: | ||
... | ||
10000 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)}") | ||
10000 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 + 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() |
1 change: 1 addition & 0 deletions
1
Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Enable postponed evaluation of annotations (:pep:`563`) by default. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.