Skip to content

Commit

Permalink
bpo-38605: Revert making 'from __future__ import annotations' the def…
Browse files Browse the repository at this point in the history
…ault (GH-25490)

This reverts commits 044a104 and 1be456a, adapting the code to changes that happened after it.
  • Loading branch information
pablogsal authored Apr 21, 2021
1 parent d35eef3 commit b0544ba
Show file tree
Hide file tree
Showing 32 changed files with 436 additions and 523 deletions.
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
7 changes: 5 additions & 2 deletions Doc/reference/simple_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -877,11 +877,14 @@ can appear before a future statement are:
* blank lines, and
* other future statements.

The only feature that requires using the future statement is
``annotations`` (see :pep:`563`).

All historical features enabled by the future statement are still recognized
by Python 3. The list includes ``absolute_import``, ``division``,
``generators``, ``generator_stop``, ``unicode_literals``,
``print_function``, ``nested_scopes``, ``with_statement`` and ``annotations``.
They are all redundant because they are always enabled, and only kept for
``print_function``, ``nested_scopes`` and ``with_statement``. They are
all redundant because they are always enabled, and only kept for
backwards compatibility.

A future statement is recognized and treated specially at compile time: Changes
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
3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.10a6 3434 (PEP 634: Structural Pattern Matching)
# Python 3.10a7 3435 Use instruction offsets (as opposed to byte offsets).
# Python 3.10b1 3436 (Add GEN_START bytecode #43683)
# Python 3.10b1 3437 (Undo making 'annotations' future by default - We like to dance among core devs!)

#
# MAGIC must change whenever the bytecode emitted by the compiler may no
Expand All @@ -358,7 +359,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3436).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3437).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
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

0 comments on commit b0544ba

Please sign in to comment.