[Python-checkins] bpo-38605: Make 'from __future__ import annotations' the default (GH-20434)

Batuhan Taskaya webhook-mailer at python.org
Tue Oct 6 16:03:31 EDT 2020


https://github.com/python/cpython/commit/044a1048ca93d466965afc027b91a5a9eb9ce23c
commit: 044a1048ca93d466965afc027b91a5a9eb9ce23c
branch: master
author: Batuhan Taskaya <batuhanosmantaskaya at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-10-06T13:03:02-07:00
summary:

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).

files:
A Lib/test/test_annotations.py
A Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst
D Lib/test/dataclass_module_1_str.py
D Lib/test/dataclass_module_2_str.py
M Doc/reference/compound_stmts.rst
M Doc/whatsnew/3.10.rst
M Lib/dataclasses.py
M Lib/inspect.py
M Lib/test/dataclass_module_1.py
M Lib/test/dataclass_module_2.py
M Lib/test/dataclass_textanno.py
M Lib/test/test_coroutines.py
M Lib/test/test_dataclasses.py
M Lib/test/test_dis.py
M Lib/test/test_functools.py
M Lib/test/test_grammar.py
M Lib/test/test_inspect.py
M Lib/test/test_opcodes.py
M Lib/test/test_positional_only_arg.py
M Lib/test/test_pydoc.py
M Lib/test/test_syntax.py
M Lib/test/test_types.py
M Lib/test/test_typing.py
M Lib/typing.py
M Python/ast_opt.c
M Python/compile.c
M Python/future.c

diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index 158d6a8f164e2..04a3948d0c9dc 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -610,13 +610,9 @@ following the parameter name.  Any parameter may have an annotation, even those
 ``*identifier`` or ``**identifier``.  Functions may have "return" annotation of
 the form "``-> expression``" after the parameter list.  These annotations can be
 any valid Python expression.  The presence of annotations does not change the
-semantics of a function.  The annotation values are available as values of
-a dictionary keyed by the parameters' names in the :attr:`__annotations__`
-attribute of the function object.  If the ``annotations`` import from
-:mod:`__future__` is used, annotations are preserved as strings at runtime which
-enables postponed evaluation.  Otherwise, they are evaluated when the function
-definition is executed.  In this case annotations may be evaluated in
-a different order than they appear in the source code.
+semantics of a function.  The annotation values are available as string values
+in a dictionary keyed by the parameters' names in the :attr:`__annotations__`
+attribute of the function object.
 
 .. index:: pair: lambda; expression
 
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index 1ea5aeac8a3c6..2bcdba69957b6 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -70,6 +70,23 @@ Summary -- Release highlights
 New Features
 ============
 
+.. _whatsnew310-pep563:
+
+PEP 563: Postponed Evaluation of Annotations Becomes Default
+------------------------------------------------------------
+
+In Python 3.7, postponed evaluation of annotations was added,
+to be enabled with a ``from __future__ import annotations``
+directive.  In 3.10 this became the default behavior, even
+without that future directive.  With this being default, all
+annotations stored in :attr:`__annotations__` will be strings.
+If needed, annotations can be resolved at runtime using
+:func:`typing.get_type_hints`.  See :pep:`563` for a full
+description.  Also, the :func:`inspect.signature` will try to
+resolve types from now on, and when it fails it will fall back to
+showing the string annotations.  (Contributed by Batuhan Taskaya
+in :issue:`38605`.)
+
 * The :class:`int` type has a new method :meth:`int.bit_count`, returning the
   number of ones in the binary expansion of a given integer, also known
   as the population count. (Contributed by Niklas Fiekas in :issue:`29882`.)
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 65091021f3716..adfb9b7240b9f 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -399,8 +399,10 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
 
     ns = {}
     exec(txt, globals, ns)
-    return ns['__create_fn__'](**locals)
-
+    func = ns['__create_fn__'](**locals)
+    for arg, annotation in func.__annotations__.copy().items():
+        func.__annotations__[arg] = locals[annotation]
+    return func
 
 def _field_assign(frozen, name, value, self_name):
     # If we're a frozen class, then assign to our fields in __init__
@@ -651,6 +653,11 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate):
     # a eval() penalty for every single field of every dataclass
     # that's defined.  It was judged not worth it.
 
+    # Strip away the extra quotes as a result of double-stringifying when the
+    # 'annotations' feature became default.
+    if annotation.startswith(("'", '"')) and annotation.endswith(("'", '"')):
+        annotation = annotation[1:-1]
+
     match = _MODULE_IDENTIFIER_RE.match(annotation)
     if match:
         ns = None
@@ -991,7 +998,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
     if not getattr(cls, '__doc__'):
         # Create a class doc-string.
         cls.__doc__ = (cls.__name__ +
-                       str(inspect.signature(cls)).replace(' -> None', ''))
+                       str(inspect.signature(cls)).replace(' -> NoneType', ''))
 
     abc.update_abstractmethods(cls)
 
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 887a3424057b6..ac127cbd725b9 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -45,6 +45,7 @@
 import tokenize
 import token
 import types
+import typing
 import warnings
 import functools
 import builtins
@@ -1877,7 +1878,10 @@ def _signature_is_functionlike(obj):
     code = getattr(obj, '__code__', None)
     defaults = getattr(obj, '__defaults__', _void) # Important to use _void ...
     kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here
-    annotations = getattr(obj, '__annotations__', None)
+    try:
+        annotations = _get_type_hints(obj)
+    except AttributeError:
+        annotations = None
 
     return (isinstance(code, types.CodeType) and
             isinstance(name, str) and
@@ -2118,6 +2122,16 @@ def p(name_node, default_node, default=empty):
 
     return cls(parameters, return_annotation=cls.empty)
 
+def _get_type_hints(func):
+    try:
+        return typing.get_type_hints(func)
+    except Exception:
+        # First, try to use the get_type_hints to resolve
+        # annotations. But for keeping the behavior intact
+        # if there was a problem with that (like the namespace
+        # can't resolve some annotation) continue to use
+        # string annotations
+        return func.__annotations__
 
 def _signature_from_builtin(cls, func, skip_bound_arg=True):
     """Private helper function to get signature for
@@ -2161,7 +2175,8 @@ def _signature_from_function(cls, func, skip_bound_arg=True):
     positional = arg_names[:pos_count]
     keyword_only_count = func_code.co_kwonlyargcount
     keyword_only = arg_names[pos_count:pos_count + keyword_only_count]
-    annotations = func.__annotations__
+    annotations = _get_type_hints(func)
+
     defaults = func.__defaults__
     kwdefaults = func.__kwdefaults__
 
diff --git a/Lib/test/dataclass_module_1.py b/Lib/test/dataclass_module_1.py
index 87a33f8191d3d..9f0aeda67f9ab 100644
--- a/Lib/test/dataclass_module_1.py
+++ b/Lib/test/dataclass_module_1.py
@@ -1,9 +1,3 @@
-#from __future__ import annotations
-USING_STRINGS = False
-
-# dataclass_module_1.py and dataclass_module_1_str.py are identical
-# except only the latter uses string annotations.
-
 import dataclasses
 import typing
 
diff --git a/Lib/test/dataclass_module_1_str.py b/Lib/test/dataclass_module_1_str.py
deleted file mode 100644
index 6de490b7ad784..0000000000000
--- a/Lib/test/dataclass_module_1_str.py
+++ /dev/null
@@ -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
-
- at 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.
-
- at 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.
diff --git a/Lib/test/dataclass_module_2.py b/Lib/test/dataclass_module_2.py
index 68fb733e29925..8d120d181bd3d 100644
--- a/Lib/test/dataclass_module_2.py
+++ b/Lib/test/dataclass_module_2.py
@@ -1,9 +1,3 @@
-#from __future__ import annotations
-USING_STRINGS = False
-
-# dataclass_module_2.py and dataclass_module_2_str.py are identical
-# except only the latter uses string annotations.
-
 from dataclasses import dataclass, InitVar
 from typing import ClassVar
 
diff --git a/Lib/test/dataclass_module_2_str.py b/Lib/test/dataclass_module_2_str.py
deleted file mode 100644
index b363d17c176c2..0000000000000
--- a/Lib/test/dataclass_module_2_str.py
+++ /dev/null
@@ -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
-
- at 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.
-
- at 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.
diff --git a/Lib/test/dataclass_textanno.py b/Lib/test/dataclass_textanno.py
index 3eb6c943d4c43..589b60f0cd61d 100644
--- a/Lib/test/dataclass_textanno.py
+++ b/Lib/test/dataclass_textanno.py
@@ -1,5 +1,3 @@
-from __future__ import annotations
-
 import dataclasses
 
 
diff --git a/Lib/test/test_annotations.py b/Lib/test/test_annotations.py
new file mode 100644
index 0000000000000..3e6b709fb4f1e
--- /dev/null
+++ b/Lib/test/test_annotations.py
@@ -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()
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 145adb6778170..40c2eb8d232dd 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -91,10 +91,6 @@ def test_badsyntax_1(self):
                 pass
             """,
 
-            """async def foo(a:await something()):
-                pass
-            """,
-
             """async def foo():
                 def bar():
                  [i async for i in els]
@@ -299,10 +295,6 @@ def bar():
                    pass
             """,
 
-            """async def foo(a:await b):
-                   pass
-            """,
-
             """def baz():
                    async def foo(a=await b):
                        pass
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index b31a469ec7922..7c1d9c568f4ef 100644
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -9,6 +9,7 @@
 import inspect
 import builtins
 import unittest
+from textwrap import dedent
 from unittest.mock import Mock
 from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
 from typing import get_type_hints
@@ -562,17 +563,17 @@ class C:
         self.assertEqual(len(the_fields), 3)
 
         self.assertEqual(the_fields[0].name, 'x')
-        self.assertEqual(the_fields[0].type, int)
+        self.assertEqual(the_fields[0].type, 'int')
         self.assertFalse(hasattr(C, 'x'))
         self.assertTrue (the_fields[0].init)
         self.assertTrue (the_fields[0].repr)
         self.assertEqual(the_fields[1].name, 'y')
-        self.assertEqual(the_fields[1].type, str)
+        self.assertEqual(the_fields[1].type, 'str')
         self.assertIsNone(getattr(C, 'y'))
         self.assertFalse(the_fields[1].init)
         self.assertTrue (the_fields[1].repr)
         self.assertEqual(the_fields[2].name, 'z')
-        self.assertEqual(the_fields[2].type, str)
+        self.assertEqual(the_fields[2].type, 'str')
         self.assertFalse(hasattr(C, 'z'))
         self.assertTrue (the_fields[2].init)
         self.assertFalse(the_fields[2].repr)
@@ -758,11 +759,11 @@ class F:
         def validate_class(cls):
             # First, check __annotations__, even though they're not
             #  function annotations.
-            self.assertEqual(cls.__annotations__['i'], int)
-            self.assertEqual(cls.__annotations__['j'], str)
-            self.assertEqual(cls.__annotations__['k'], F)
-            self.assertEqual(cls.__annotations__['l'], float)
-            self.assertEqual(cls.__annotations__['z'], complex)
+            self.assertEqual(cls.__annotations__['i'], 'int')
+            self.assertEqual(cls.__annotations__['j'], 'str')
+            self.assertEqual(cls.__annotations__['k'], 'F')
+            self.assertEqual(cls.__annotations__['l'], 'float')
+            self.assertEqual(cls.__annotations__['z'], 'complex')
 
             # Verify __init__.
 
@@ -777,22 +778,22 @@ def validate_class(cls):
             self.assertEqual(param.name, 'self')
             param = next(params)
             self.assertEqual(param.name, 'i')
-            self.assertIs   (param.annotation, int)
+            self.assertIs   (param.annotation, 'int')
             self.assertEqual(param.default, inspect.Parameter.empty)
             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
             param = next(params)
             self.assertEqual(param.name, 'j')
-            self.assertIs   (param.annotation, str)
+            self.assertIs   (param.annotation, 'str')
             self.assertEqual(param.default, inspect.Parameter.empty)
             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
             param = next(params)
             self.assertEqual(param.name, 'k')
-            self.assertIs   (param.annotation, F)
+            self.assertIs   (param.annotation, 'F')
             # Don't test for the default, since it's set to MISSING.
             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
             param = next(params)
             self.assertEqual(param.name, 'l')
-            self.assertIs   (param.annotation, float)
+            self.assertIs   (param.annotation, 'float')
             # Don't test for the default, since it's set to MISSING.
             self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
             self.assertRaises(StopIteration, next, params)
@@ -2806,13 +2807,10 @@ class C:
 
 class TestStringAnnotations(unittest.TestCase):
     def test_classvar(self):
-        # Some expressions recognized as ClassVar really aren't.  But
-        #  if you're using string annotations, it's not an exact
-        #  science.
         # These tests assume that both "import typing" and "from
         # typing import *" have been run in this file.
         for typestr in ('ClassVar[int]',
-                        'ClassVar [int]'
+                        'ClassVar [int]',
                         ' ClassVar [int]',
                         'ClassVar',
                         ' ClassVar ',
@@ -2823,17 +2821,15 @@ def test_classvar(self):
                         'typing. ClassVar[str]',
                         'typing.ClassVar [str]',
                         'typing.ClassVar [ str]',
-
+                        # Double stringified
+                        '"typing.ClassVar[int]"',
                         # Not syntactically valid, but these will
-                        #  be treated as ClassVars.
+                        # be treated as ClassVars.
                         'typing.ClassVar.[int]',
                         'typing.ClassVar+',
                         ):
             with self.subTest(typestr=typestr):
-                @dataclass
-                class C:
-                    x: typestr
-
+                C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))
                 # x is a ClassVar, so C() takes no args.
                 C()
 
@@ -2854,9 +2850,7 @@ def test_isnt_classvar(self):
                         'typingxClassVar[str]',
                         ):
             with self.subTest(typestr=typestr):
-                @dataclass
-                class C:
-                    x: typestr
+                C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))
 
                 # x is not a ClassVar, so C() takes one arg.
                 self.assertEqual(C(10).x, 10)
@@ -2876,16 +2870,16 @@ def test_initvar(self):
                         'dataclasses. InitVar[str]',
                         'dataclasses.InitVar [str]',
                         'dataclasses.InitVar [ str]',
-
+                        # Double stringified
+                        '"dataclasses.InitVar[int]"',
                         # Not syntactically valid, but these will
                         #  be treated as InitVars.
                         'dataclasses.InitVar.[int]',
                         'dataclasses.InitVar+',
                         ):
             with self.subTest(typestr=typestr):
-                @dataclass
-                class C:
-                    x: typestr
+                C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))
+
 
                 # x is an InitVar, so doesn't create a member.
                 with self.assertRaisesRegex(AttributeError,
@@ -2899,30 +2893,22 @@ def test_isnt_initvar(self):
                         'typing.xInitVar[int]',
                         ):
             with self.subTest(typestr=typestr):
-                @dataclass
-                class C:
-                    x: typestr
+                C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))
 
                 # x is not an InitVar, so there will be a member x.
                 self.assertEqual(C(10).x, 10)
 
     def test_classvar_module_level_import(self):
         from test import dataclass_module_1
-        from test import dataclass_module_1_str
         from test import dataclass_module_2
-        from test import dataclass_module_2_str
 
-        for m in (dataclass_module_1, dataclass_module_1_str,
-                  dataclass_module_2, dataclass_module_2_str,
-                  ):
+        for m in (dataclass_module_1,
+                  dataclass_module_2):
             with self.subTest(m=m):
                 # There's a difference in how the ClassVars are
                 # interpreted when using string annotations or
                 # not. See the imported modules for details.
-                if m.USING_STRINGS:
-                    c = m.CV(10)
-                else:
-                    c = m.CV()
+                c = m.CV(10)
                 self.assertEqual(c.cv0, 20)
 
 
@@ -2938,14 +2924,9 @@ def test_classvar_module_level_import(self):
                             # not an instance field.
                             getattr(c, field_name)
 
-                if m.USING_STRINGS:
-                    # iv4 is interpreted as a normal field.
-                    self.assertIn('not_iv4', c.__dict__)
-                    self.assertEqual(c.not_iv4, 4)
-                else:
-                    # iv4 is interpreted as an InitVar, so it
-                    # won't exist on the instance.
-                    self.assertNotIn('not_iv4', c.__dict__)
+                # iv4 is interpreted as a normal field.
+                self.assertIn('not_iv4', c.__dict__)
+                self.assertEqual(c.not_iv4, 4)
 
     def test_text_annotations(self):
         from test import dataclass_textanno
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 4533a016a2fab..bbaddd57d2918 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -227,28 +227,26 @@ def bug1333982(x=[]):
   2           0 SETUP_ANNOTATIONS
               2 LOAD_CONST               0 (1)
               4 STORE_NAME               0 (x)
-              6 LOAD_NAME                1 (int)
-              8 LOAD_NAME                2 (__annotations__)
-             10 LOAD_CONST               1 ('x')
+              6 LOAD_CONST               1 ('int')
+              8 LOAD_NAME                1 (__annotations__)
+             10 LOAD_CONST               2 ('x')
              12 STORE_SUBSCR
 
-  3          14 LOAD_NAME                3 (fun)
-             16 LOAD_CONST               0 (1)
-             18 CALL_FUNCTION            1
-             20 LOAD_NAME                2 (__annotations__)
-             22 LOAD_CONST               2 ('y')
-             24 STORE_SUBSCR
-
-  4          26 LOAD_CONST               0 (1)
-             28 LOAD_NAME                4 (lst)
-             30 LOAD_NAME                3 (fun)
-             32 LOAD_CONST               3 (0)
-             34 CALL_FUNCTION            1
-             36 STORE_SUBSCR
-             38 LOAD_NAME                1 (int)
-             40 POP_TOP
-             42 LOAD_CONST               4 (None)
-             44 RETURN_VALUE
+  3          14 LOAD_CONST               3 ('fun(1)')
+             16 LOAD_NAME                1 (__annotations__)
+             18 LOAD_CONST               4 ('y')
+             20 STORE_SUBSCR
+
+  4          22 LOAD_CONST               0 (1)
+             24 LOAD_NAME                2 (lst)
+             26 LOAD_NAME                3 (fun)
+             28 LOAD_CONST               5 (0)
+             30 CALL_FUNCTION            1
+             32 STORE_SUBSCR
+             34 LOAD_NAME                4 (int)
+             36 POP_TOP
+             38 LOAD_CONST               6 (None)
+             40 RETURN_VALUE
 """
 
 compound_stmt_str = """\
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index edd5773e13d54..bee9f9112bf18 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -618,7 +618,7 @@ def check_wrapper(self, wrapper, wrapped,
 
 
     def _default_update(self):
-        def f(a:'This is a new annotation'):
+        def f(a: int):
             """This is a test"""
             pass
         f.attr = 'This is also a test'
@@ -635,7 +635,7 @@ def test_default_update(self):
         self.assertEqual(wrapper.__name__, 'f')
         self.assertEqual(wrapper.__qualname__, f.__qualname__)
         self.assertEqual(wrapper.attr, 'This is also a test')
-        self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
+        self.assertEqual(wrapper.__annotations__['a'], 'int')
         self.assertNotIn('b', wrapper.__annotations__)
 
     @unittest.skipIf(sys.flags.optimize >= 2,
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 5235fa2c783f0..2f6716dfc9a13 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -362,7 +362,7 @@ class C:
             z = 2
             def __init__(self, x):
                 self.x: int = x
-        self.assertEqual(C.__annotations__, {'_C__foo': int, 's': str})
+        self.assertEqual(C.__annotations__, {'_C__foo': 'int', 's': 'str'})
         with self.assertRaises(NameError):
             class CBad:
                 no_such_name_defined.attr: int = 0
@@ -378,15 +378,15 @@ def __prepare__(metacls, name, bases, **kwds):
                 return {'__annotations__': CNS()}
         class CC(metaclass=CMeta):
             XX: 'ANNOT'
-        self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
+        self.assertEqual(CC.__annotations__['xx'], repr('ANNOT'))
 
     def test_var_annot_module_semantics(self):
         with self.assertRaises(AttributeError):
             print(test.__annotations__)
         self.assertEqual(ann_module.__annotations__,
-                     {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]})
+                     {1: 2, 'x': 'int', 'y': 'str', 'f': 'Tuple[int, int]'})
         self.assertEqual(ann_module.M.__annotations__,
-                              {'123': 123, 'o': type})
+                              {'123': 123, 'o': 'type'})
         self.assertEqual(ann_module2.__annotations__, {})
 
     def test_var_annot_in_module(self):
@@ -405,7 +405,7 @@ def test_var_annot_simple_exec(self):
         exec("'docstring'\n"
              "__annotations__[1] = 2\n"
              "x: int = 5\n", gns, lns)
-        self.assertEqual(lns["__annotations__"], {1: 2, 'x': int})
+        self.assertEqual(lns["__annotations__"], {1: 2, 'x': 'int'})
         with self.assertRaises(KeyError):
             gns['__annotations__']
 
@@ -413,8 +413,8 @@ def test_var_annot_custom_maps(self):
         # tests with custom locals() and __annotations__
         ns = {'__annotations__': CNS()}
         exec('X: int; Z: str = "Z"; (w): complex = 1j', ns)
-        self.assertEqual(ns['__annotations__']['x'], int)
-        self.assertEqual(ns['__annotations__']['z'], str)
+        self.assertEqual(ns['__annotations__']['x'], 'int')
+        self.assertEqual(ns['__annotations__']['z'], 'str')
         with self.assertRaises(KeyError):
             ns['__annotations__']['w']
         nonloc_ns = {}
@@ -428,7 +428,7 @@ def __setitem__(self, item, value):
             def __getitem__(self, item):
                 return self._dct[item]
         exec('x: int = 1', {}, CNS2())
-        self.assertEqual(nonloc_ns['__annotations__']['x'], int)
+        self.assertEqual(nonloc_ns['__annotations__']['x'], 'int')
 
     def test_var_annot_refleak(self):
         # complex case: custom locals plus custom __annotations__
@@ -445,7 +445,7 @@ def __setitem__(self, item, value):
             def __getitem__(self, item):
                 return self._dct[item]
         exec('X: str', {}, CNS2())
-        self.assertEqual(nonloc_ns['__annotations__']['x'], str)
+        self.assertEqual(nonloc_ns['__annotations__']['x'], 'str')
 
     def test_var_annot_rhs(self):
         ns = {}
@@ -625,50 +625,46 @@ def f(*args, **kwargs):
 
         # argument annotation tests
         def f(x) -> list: pass
-        self.assertEqual(f.__annotations__, {'return': list})
+        self.assertEqual(f.__annotations__, {'return': 'list'})
         def f(x: int): pass
-        self.assertEqual(f.__annotations__, {'x': int})
+        self.assertEqual(f.__annotations__, {'x': 'int'})
         def f(x: int, /): pass
-        self.assertEqual(f.__annotations__, {'x': int})
+        self.assertEqual(f.__annotations__, {'x': 'int'})
         def f(x: int = 34, /): pass
-        self.assertEqual(f.__annotations__, {'x': int})
+        self.assertEqual(f.__annotations__, {'x': 'int'})
         def f(*x: str): pass
-        self.assertEqual(f.__annotations__, {'x': str})
+        self.assertEqual(f.__annotations__, {'x': 'str'})
         def f(**x: float): pass
-        self.assertEqual(f.__annotations__, {'x': float})
-        def f(x, y: 1+2): pass
-        self.assertEqual(f.__annotations__, {'y': 3})
-        def f(x, y: 1+2, /): pass
-        self.assertEqual(f.__annotations__, {'y': 3})
+        self.assertEqual(f.__annotations__, {'x': 'float'})
         def f(a, b: 1, c: 2, d): pass
-        self.assertEqual(f.__annotations__, {'b': 1, 'c': 2})
+        self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'})
         def f(a, b: 1, /, c: 2, d): pass
-        self.assertEqual(f.__annotations__, {'b': 1, 'c': 2})
+        self.assertEqual(f.__annotations__, {'b': '1', 'c': '2'})
         def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6): pass
         self.assertEqual(f.__annotations__,
-                         {'b': 1, 'c': 2, 'e': 3, 'g': 6})
+                         {'b': '1', 'c': '2', 'e': '3', 'g': '6'})
         def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6, h: 7, i=8, j: 9 = 10,
               **k: 11) -> 12: pass
         self.assertEqual(f.__annotations__,
-                         {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9,
-                          'k': 11, 'return': 12})
+                         {'b': '1', 'c': '2', 'e': '3', 'g': '6', 'h': '7', 'j': '9',
+                          'k': '11', 'return': '12'})
         def f(a, b: 1, c: 2, d, e: 3 = 4, f: int = 5, /, *g: 6, h: 7, i=8, j: 9 = 10,
               **k: 11) -> 12: pass
         self.assertEqual(f.__annotations__,
-                          {'b': 1, 'c': 2, 'e': 3, 'f': int, 'g': 6, 'h': 7, 'j': 9,
-                           'k': 11, 'return': 12})
+                          {'b': '1', 'c': '2', 'e': '3', 'f': 'int', 'g': '6', 'h': '7', 'j': '9',
+                           'k': '11', 'return': '12'})
         # Check for issue #20625 -- annotations mangling
         class Spam:
             def f(self, *, __kw: 1):
                 pass
         class Ham(Spam): pass
-        self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': 1})
-        self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': 1})
+        self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': '1'})
+        self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': '1'})
         # Check for SF Bug #1697248 - mixing decorators and a return annotation
         def null(x): return x
         @null
         def f(x) -> list: pass
-        self.assertEqual(f.__annotations__, {'return': list})
+        self.assertEqual(f.__annotations__, {'return': 'list'})
 
         # Test expressions as decorators (PEP 614):
         @False or null
@@ -1116,8 +1112,6 @@ def g(): rest = 4, 5, 6; yield 1, 2, 3, *rest
         # Not allowed at class scope
         check_syntax_error(self, "class foo:yield 1")
         check_syntax_error(self, "class foo:yield from ()")
-        # Check annotation refleak on SyntaxError
-        check_syntax_error(self, "def g(a:(yield)): pass")
 
     def test_yield_in_comprehensions(self):
         # Check yield in comprehensions
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 6667dc91edbce..71c4f27d27b98 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -862,7 +862,7 @@ def test_getfullargspec(self):
 
         self.assertFullArgSpecEquals(mod2.annotated, ['arg1'],
                                      ann_e={'arg1' : list},
-                                     formatted='(arg1: list)')
+                                     formatted="(arg1: list)")
         self.assertFullArgSpecEquals(mod2.keyword_only_arg, [],
                                      kwonlyargs_e=['arg'],
                                      formatted='(*, arg)')
@@ -2211,8 +2211,8 @@ def test(a, b:'foo') -> 123:
             pass
         self.assertEqual(self.signature(test),
                          ((('a', ..., ..., "positional_or_keyword"),
-                           ('b', ..., 'foo', "positional_or_keyword")),
-                          123))
+                           ('b', ..., repr('foo'), "positional_or_keyword")),
+                          '123'))
 
     def test_signature_on_wkwonly(self):
         def test(*, a:float, b:str) -> int:
@@ -2227,11 +2227,11 @@ def test(a, b:'foo'=10, *args:'bar', spam:'baz', ham=123, **kwargs:int):
             pass
         self.assertEqual(self.signature(test),
                          ((('a', ..., ..., "positional_or_keyword"),
-                           ('b', 10, 'foo', "positional_or_keyword"),
-                           ('args', ..., 'bar', "var_positional"),
-                           ('spam', ..., 'baz', "keyword_only"),
+                           ('b', 10, repr('foo'), "positional_or_keyword"),
+                           ('args', ..., repr('bar'), "var_positional"),
+                           ('spam', ..., repr('baz'), "keyword_only"),
                            ('ham', 123, ..., "keyword_only"),
-                           ('kwargs', ..., int, "var_keyword")),
+                           ('kwargs', ..., 'int', "var_keyword")),
                           ...))
 
     def test_signature_without_self(self):
@@ -2640,12 +2640,12 @@ def test(a, b, c:int) -> 42:
 
         self.assertEqual(self.signature(partial(partial(test, 1))),
                          ((('b', ..., ..., "positional_or_keyword"),
-                           ('c', ..., int, "positional_or_keyword")),
-                          42))
+                           ('c', ..., 'int', "positional_or_keyword")),
+                          '42'))
 
         self.assertEqual(self.signature(partial(partial(test, 1), 2)),
-                         ((('c', ..., int, "positional_or_keyword"),),
-                          42))
+                         ((('c', ..., 'int', "positional_or_keyword"),),
+                          '42'))
 
         psig = inspect.signature(partial(partial(test, 1), 2))
 
@@ -2764,12 +2764,12 @@ def test(it, a, *, c) -> 'spam':
                          ((('it', ..., ..., 'positional_or_keyword'),
                            ('a', ..., ..., 'positional_or_keyword'),
                            ('c', 1, ..., 'keyword_only')),
-                          'spam'))
+                          repr('spam')))
 
         self.assertEqual(self.signature(Spam().ham),
                          ((('a', ..., ..., 'positional_or_keyword'),
                            ('c', 1, ..., 'keyword_only')),
-                          'spam'))
+                          repr('spam')))
 
         class Spam:
             def test(self: 'anno', x):
@@ -2778,7 +2778,7 @@ def test(self: 'anno', x):
             g = partialmethod(test, 1)
 
         self.assertEqual(self.signature(Spam.g),
-                         ((('self', ..., 'anno', 'positional_or_keyword'),),
+                         ((('self', ..., repr('anno'), 'positional_or_keyword'),),
                           ...))
 
     def test_signature_on_fake_partialmethod(self):
@@ -3116,20 +3116,16 @@ def foo(a={}): pass
         with self.assertRaisesRegex(TypeError, 'unhashable type'):
             hash(inspect.signature(foo))
 
-        def foo(a) -> {}: pass
-        with self.assertRaisesRegex(TypeError, 'unhashable type'):
-            hash(inspect.signature(foo))
-
     def test_signature_str(self):
         def foo(a:int=1, *, b, c=None, **kwargs) -> 42:
             pass
         self.assertEqual(str(inspect.signature(foo)),
-                         '(a: int = 1, *, b, c=None, **kwargs) -> 42')
+                         '(a: \'int\' = 1, *, b, c=None, **kwargs) -> \'42\'')
 
         def foo(a:int=1, *args, b, c=None, **kwargs) -> 42:
             pass
         self.assertEqual(str(inspect.signature(foo)),
-                         '(a: int = 1, *args, b, c=None, **kwargs) -> 42')
+                         '(a: \'int\' = 1, *args, b, c=None, **kwargs) -> \'42\'')
 
         def foo():
             pass
@@ -3172,8 +3168,8 @@ def test() -> 42:
         self.assertIs(sig.return_annotation, None)
         sig = sig.replace(return_annotation=sig.empty)
         self.assertIs(sig.return_annotation, sig.empty)
-        sig = sig.replace(return_annotation=42)
-        self.assertEqual(sig.return_annotation, 42)
+        sig = sig.replace(return_annotation='42')
+        self.assertEqual(sig.return_annotation, '42')
         self.assertEqual(sig, inspect.signature(test))
 
     def test_signature_on_mangled_parameters(self):
@@ -3185,8 +3181,8 @@ class Ham(Spam):
 
         self.assertEqual(self.signature(Spam.foo),
                          ((('self', ..., ..., "positional_or_keyword"),
-                           ('_Spam__p1', 2, 1, "positional_or_keyword"),
-                           ('_Spam__p2', 3, 2, "keyword_only")),
+                           ('_Spam__p1', 2, '1', "positional_or_keyword"),
+                           ('_Spam__p2', 3, '2', "keyword_only")),
                           ...))
 
         self.assertEqual(self.signature(Spam.foo),
diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py
index 527aca664d38e..1152eb65bb2c3 100644
--- a/Lib/test/test_opcodes.py
+++ b/Lib/test/test_opcodes.py
@@ -39,7 +39,7 @@ class C: pass
     def test_use_existing_annotations(self):
         ns = {'__annotations__': {1: 2}}
         exec('x: int', ns)
-        self.assertEqual(ns['__annotations__'], {'x': int, 1: 2})
+        self.assertEqual(ns['__annotations__'], {'x': 'int', 1: 2})
 
     def test_do_not_recreate_annotations(self):
         # Don't rely on the existence of the '__annotations__' global.
diff --git a/Lib/test/test_positional_only_arg.py b/Lib/test/test_positional_only_arg.py
index 0a9503e2025d6..1fe8256d46ea4 100644
--- a/Lib/test/test_positional_only_arg.py
+++ b/Lib/test/test_positional_only_arg.py
@@ -302,14 +302,14 @@ def inner_has_pos_only():
             def f(x: int, /): ...
             return f
 
-        assert inner_has_pos_only().__annotations__ == {'x': int}
+        assert inner_has_pos_only().__annotations__ == {'x': 'int'}
 
         class Something:
             def method(self):
                 def f(x: int, /): ...
                 return f
 
-        assert Something().method().__annotations__ == {'x': int}
+        assert Something().method().__annotations__ == {'x': 'int'}
 
         def multiple_levels():
             def inner_has_pos_only():
@@ -317,7 +317,7 @@ def f(x: int, /): ...
                 return f
             return inner_has_pos_only()
 
-        assert multiple_levels().__annotations__ == {'x': int}
+        assert multiple_levels().__annotations__ == {'x': 'int'}
 
     def test_same_keyword_as_positional_with_kwargs(self):
         def f(something,/,**kwargs):
@@ -429,17 +429,6 @@ def method(self, /):
 
         self.assertEqual(C().method(), sentinel)
 
-    def test_annotations_constant_fold(self):
-        def g():
-            def f(x: not (int is int), /): ...
-
-        # without constant folding we end up with
-        # COMPARE_OP(is), IS_OP (0)
-        # with constant folding we should expect a IS_OP (1)
-        codes = [(i.opname, i.argval) for i in dis.get_instructions(g)]
-        self.assertNotIn(('UNARY_NOT', None), codes)
-        self.assertIn(('IS_OP', 1), codes)
-
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py
index 76d2af8e461ed..2f502627f4d0a 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -81,7 +81,7 @@ class B(builtins.object)
      |\x20\x20
      |  NO_MEANING = 'eggs'
      |\x20\x20
-     |  __annotations__ = {'NO_MEANING': <class 'str'>}
+     |  __annotations__ = {'NO_MEANING': 'str'}
 \x20\x20\x20\x20
     class C(builtins.object)
      |  Methods defined here:
@@ -194,7 +194,7 @@ class C(builtins.object)
 Data and other attributes defined here:<br>
 <dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl>
 
-<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': <class 'str'>}</dl>
+<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': 'str'}</dl>
 
 </td></tr></table> <p>
 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 09c6eb3375409..7c3302c1d46ae 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -752,14 +752,6 @@
     Traceback (most recent call last):
     SyntaxError: cannot assign to __debug__
 
-    >>> def f(*args:(lambda __debug__:0)): pass
-    Traceback (most recent call last):
-    SyntaxError: cannot assign to __debug__
-
-    >>> def f(**kwargs:(lambda __debug__:0)): pass
-    Traceback (most recent call last):
-    SyntaxError: cannot assign to __debug__
-
     >>> with (lambda *:0): pass
     Traceback (most recent call last):
     SyntaxError: named arguments must follow bare *
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index 52a59d54f044d..75c5eee42dc54 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -671,8 +671,8 @@ def test_or_type_operator_with_forward(self):
         ForwardBefore = 'Forward' | T
         def forward_after(x: ForwardAfter[int]) -> None: ...
         def forward_before(x: ForwardBefore[int]) -> None: ...
-        assert typing.get_args(typing.get_type_hints(forward_after)['x']) == (int, Forward)
-        assert typing.get_args(typing.get_type_hints(forward_before)['x']) == (int, Forward)
+        assert typing.get_args(typing.get_type_hints(forward_after, localns=locals())['x']) == (int, Forward)
+        assert typing.get_args(typing.get_type_hints(forward_before, localns=locals())['x']) == (int, Forward)
 
     def test_or_type_operator_with_Protocol(self):
         class Proto(typing.Protocol):
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 42aa430c5e107..4bef42f4f32fc 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -349,7 +349,7 @@ def test_empty(self):
     def test_no_eval_union(self):
         u = Union[int, str]
         def f(x: u): ...
-        self.assertIs(get_type_hints(f)['x'], u)
+        self.assertIs(get_type_hints(f, globals(), locals())['x'], u)
 
     def test_function_repr_union(self):
         def fun() -> int: ...
@@ -2849,11 +2849,11 @@ def test_get_type_hints_classes(self):
         self.assertEqual(gth(HasForeignBaseClass),
                          {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A,
                           'some_b': mod_generics_cache.B})
-        self.assertEqual(gth(XRepr.__new__),
+        self.assertEqual(gth(XRepr),
                          {'x': int, 'y': int})
         self.assertEqual(gth(mod_generics_cache.B),
                          {'my_inner_a1': mod_generics_cache.B.A,
-                          'my_inner_a2': mod_generics_cache.B.A,
+                          'my_inner_a2': mod_generics_cache.A,
                           'my_outer_a': mod_generics_cache.A})
 
     def test_respect_no_type_check(self):
@@ -3641,7 +3641,7 @@ def test_annotation_usage(self):
         self.assertEqual(tim.cool, 9000)
         self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
         self.assertEqual(CoolEmployee._fields, ('name', 'cool'))
-        self.assertEqual(CoolEmployee.__annotations__,
+        self.assertEqual(gth(CoolEmployee),
                          collections.OrderedDict(name=str, cool=int))
 
     def test_annotation_usage_with_default(self):
@@ -3655,7 +3655,7 @@ def test_annotation_usage_with_default(self):
 
         self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault')
         self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool'))
-        self.assertEqual(CoolEmployeeWithDefault.__annotations__,
+        self.assertEqual(gth(CoolEmployeeWithDefault),
                          dict(name=str, cool=int))
         self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0))
 
@@ -3823,7 +3823,7 @@ def test_typeddict_errors(self):
     def test_py36_class_syntax_usage(self):
         self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D')
         self.assertEqual(LabelPoint2D.__module__, __name__)
-        self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str})
+        self.assertEqual(gth(LabelPoint2D), {'x': int, 'y': int, 'label': str})
         self.assertEqual(LabelPoint2D.__bases__, (dict,))
         self.assertEqual(LabelPoint2D.__total__, True)
         self.assertNotIsSubclass(LabelPoint2D, typing.Sequence)
@@ -3882,11 +3882,11 @@ class Cat(Animal):
 
         assert BaseAnimal.__required_keys__ == frozenset(['name'])
         assert BaseAnimal.__optional_keys__ == frozenset([])
-        assert BaseAnimal.__annotations__ == {'name': str}
+        assert gth(BaseAnimal) == {'name': str}
 
         assert Animal.__required_keys__ == frozenset(['name'])
         assert Animal.__optional_keys__ == frozenset(['tail', 'voice'])
-        assert Animal.__annotations__ == {
+        assert gth(Animal) == {
             'name': str,
             'tail': bool,
             'voice': str,
@@ -3894,7 +3894,7 @@ class Cat(Animal):
 
         assert Cat.__required_keys__ == frozenset(['name', 'fur_color'])
         assert Cat.__optional_keys__ == frozenset(['tail', 'voice'])
-        assert Cat.__annotations__ == {
+        assert gth(Cat) == {
             'fur_color': str,
             'name': str,
             'tail': bool,
@@ -3915,7 +3915,7 @@ def test_io(self):
         def stuff(a: IO) -> AnyStr:
             return a.readline()
 
-        a = stuff.__annotations__['a']
+        a = gth(stuff)['a']
         self.assertEqual(a.__parameters__, (AnyStr,))
 
     def test_textio(self):
@@ -3923,7 +3923,7 @@ def test_textio(self):
         def stuff(a: TextIO) -> str:
             return a.readline()
 
-        a = stuff.__annotations__['a']
+        a = gth(stuff)['a']
         self.assertEqual(a.__parameters__, ())
 
     def test_binaryio(self):
@@ -3931,7 +3931,7 @@ def test_binaryio(self):
         def stuff(a: BinaryIO) -> bytes:
             return a.readline()
 
-        a = stuff.__annotations__['a']
+        a = gth(stuff)['a']
         self.assertEqual(a.__parameters__, ())
 
     def test_io_submodule(self):
diff --git a/Lib/typing.py b/Lib/typing.py
index 8c61bd8e084a8..4cf33c1ae9265 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -18,6 +18,7 @@
 """
 
 from abc import abstractmethod, ABCMeta
+import ast
 import collections
 import collections.abc
 import contextlib
@@ -469,6 +470,13 @@ class ForwardRef(_Final, _root=True):
     def __init__(self, arg, is_argument=True):
         if not isinstance(arg, str):
             raise TypeError(f"Forward reference must be a string -- got {arg!r}")
+
+        # Double-stringified forward references is a result of activating
+        # the 'annotations' future by default. This way, we eliminate them in
+        # the runtime.
+        if arg.startswith(("'", '\"')) and arg.endswith(("'", '"')):
+            arg = arg[1:-1]
+
         try:
             code = compile(arg, '<string>', 'eval')
         except SyntaxError:
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst
new file mode 100644
index 0000000000000..cbfe6e23523bb
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-27-16-08-16.bpo-38605.rcs2uK.rst	
@@ -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}``.
diff --git a/Python/ast_opt.c b/Python/ast_opt.c
index 5efaac4c8925a..22ca6f23aefa3 100644
--- a/Python/ast_opt.c
+++ b/Python/ast_opt.c
@@ -392,7 +392,6 @@ static int astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state
 static int astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
 static int astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
 static int astfold_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
-static int astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
 static int astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
 static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
 #define CALL(FUNC, TYPE, ARG) \
@@ -595,25 +594,11 @@ astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState
 static int
 astfold_arguments(arguments_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
 {
-    CALL_SEQ(astfold_arg, arg, node_->posonlyargs);
-    CALL_SEQ(astfold_arg, arg, node_->args);
-    CALL_OPT(astfold_arg, arg_ty, node_->vararg);
-    CALL_SEQ(astfold_arg, arg, node_->kwonlyargs);
     CALL_SEQ(astfold_expr, expr, node_->kw_defaults);
-    CALL_OPT(astfold_arg, arg_ty, node_->kwarg);
     CALL_SEQ(astfold_expr, expr, node_->defaults);
     return 1;
 }
 
-static int
-astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
-{
-    if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
-        CALL_OPT(astfold_expr, expr_ty, node_->annotation);
-    }
-    return 1;
-}
-
 static int
 astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
 {
@@ -622,17 +607,11 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
         CALL(astfold_arguments, arguments_ty, node_->v.FunctionDef.args);
         CALL(astfold_body, asdl_seq, node_->v.FunctionDef.body);
         CALL_SEQ(astfold_expr, expr, node_->v.FunctionDef.decorator_list);
-        if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
-            CALL_OPT(astfold_expr, expr_ty, node_->v.FunctionDef.returns);
-        }
         break;
     case AsyncFunctionDef_kind:
         CALL(astfold_arguments, arguments_ty, node_->v.AsyncFunctionDef.args);
         CALL(astfold_body, asdl_seq, node_->v.AsyncFunctionDef.body);
         CALL_SEQ(astfold_expr, expr, node_->v.AsyncFunctionDef.decorator_list);
-        if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
-            CALL_OPT(astfold_expr, expr_ty, node_->v.AsyncFunctionDef.returns);
-        }
         break;
     case ClassDef_kind:
         CALL_SEQ(astfold_expr, expr, node_->v.ClassDef.bases);
@@ -656,9 +635,6 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
         break;
     case AnnAssign_kind:
         CALL(astfold_expr, expr_ty, node_->v.AnnAssign.target);
-        if (!(state->ff_features & CO_FUTURE_ANNOTATIONS)) {
-            CALL(astfold_expr, expr_ty, node_->v.AnnAssign.annotation);
-        }
         CALL_OPT(astfold_expr, expr_ty, node_->v.AnnAssign.value);
         break;
     case For_kind:
diff --git a/Python/compile.c b/Python/compile.c
index f2563d7f7a411..ddd2a049629c1 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -2026,12 +2026,7 @@ compiler_visit_argannotation(struct compiler *c, identifier id,
 {
     if (annotation) {
         PyObject *mangled;
-        if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) {
-            VISIT(c, annexpr, annotation)
-        }
-        else {
-            VISIT(c, expr, annotation);
-        }
+        VISIT(c, annexpr, annotation);
         mangled = _Py_Mangle(c->u->u_private, id);
         if (!mangled)
             return 0;
@@ -5261,12 +5256,7 @@ compiler_annassign(struct compiler *c, stmt_ty s)
         if (s->v.AnnAssign.simple &&
             (c->u->u_scope_type == COMPILER_SCOPE_MODULE ||
              c->u->u_scope_type == COMPILER_SCOPE_CLASS)) {
-            if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) {
-                VISIT(c, annexpr, s->v.AnnAssign.annotation)
-            }
-            else {
-                VISIT(c, expr, s->v.AnnAssign.annotation);
-            }
+            VISIT(c, annexpr, s->v.AnnAssign.annotation);
             ADDOP_NAME(c, LOAD_NAME, __annotations__, names);
             mangled = _Py_Mangle(c->u->u_private, targ->v.Name.id);
             ADDOP_LOAD_CONST_NEW(c, mangled);
diff --git a/Python/future.c b/Python/future.c
index 3cea4fee78085..4b73eb6412905 100644
--- a/Python/future.c
+++ b/Python/future.c
@@ -41,7 +41,7 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename)
         } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) {
             continue;
         } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) {
-            ff->ff_features |= CO_FUTURE_ANNOTATIONS;
+            continue;
         } else if (strcmp(feature, "braces") == 0) {
             PyErr_SetString(PyExc_SyntaxError,
                             "not a chance");



More information about the Python-checkins mailing list