Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
bpo-38605: Revert making 'from __future__ import annotations' the def…
…ault

This reverts commit 044a104, adapting
the code to changes that happened after it.
  • Loading branch information
pablogsal committed Apr 20, 2021
commit 1e024afa0f407085a5904f4786c569c5e8759d32
10 changes: 7 additions & 3 deletions Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1244,9 +1244,13 @@ 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 string values
in a dictionary keyed by the parameters' names in the :attr:`__annotations__`
attribute of the function object.
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.

.. index:: pair: lambda; expression

Expand Down
16 changes: 0 additions & 16 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -624,22 +624,6 @@ This section covers major changes affecting :pep:`484` type annotations and
the :mod:`typing` module.


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


PEP 604: New Type Union Operator
--------------------------------

Expand Down
13 changes: 3 additions & 10 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,10 +413,8 @@ def _create_fn(name, args, body, *, globals=None, locals=None,

ns = {}
exec(txt, globals, ns)
func = ns['__create_fn__'](**locals)
for arg, annotation in func.__annotations__.copy().items():
func.__annotations__[arg] = locals[annotation]
return func
return ns['__create_fn__'](**locals)


def _field_assign(frozen, name, value, self_name):
# If we're a frozen class, then assign to our fields in __init__
Expand Down Expand Up @@ -667,11 +665,6 @@ 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
Expand Down Expand Up @@ -1020,7 +1013,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(' -> NoneType', ''))
str(inspect.signature(cls)).replace(' -> None', ''))

if match_args:
_set_new_attribute(cls, '__match_args__',
Expand Down
19 changes: 2 additions & 17 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import tokenize
import token
import types
import typing
import warnings
import functools
import builtins
Expand Down Expand Up @@ -1893,10 +1892,7 @@ 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
try:
annotations = _get_type_hints(obj)
except AttributeError:
annotations = None
annotations = getattr(obj, '__annotations__', None)

return (isinstance(code, types.CodeType) and
isinstance(name, str) and
Expand Down Expand Up @@ -2137,16 +2133,6 @@ def p(name_node, default_node, default=empty):

return cls(parameters, return_annotation=cls.empty)

def _get_type_hints(func, **kwargs):
try:
return typing.get_type_hints(func, **kwargs)
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
Expand Down Expand Up @@ -2191,8 +2177,7 @@ 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 = _get_type_hints(func, globalns=globalns, localns=localns)

annotations = func.__annotations__
defaults = func.__defaults__
kwdefaults = func.__kwdefaults__

Expand Down
6 changes: 6 additions & 0 deletions Lib/test/dataclass_module_1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#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

Expand Down
32 changes: 32 additions & 0 deletions Lib/test/dataclass_module_1_str.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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.
6 changes: 6 additions & 0 deletions Lib/test/dataclass_module_2.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#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

Expand Down
32 changes: 32 additions & 0 deletions Lib/test/dataclass_module_2_str.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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.
2 changes: 2 additions & 0 deletions Lib/test/dataclass_textanno.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import dataclasses


Expand Down
Loading