Skip to content

gh-90953: Emit deprecation warnings for ast features deprecated in Python 3.8 #104199

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 29 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8de6eba
bpo-46797: Emit deprecation warnings for deprecated ast features
serhiy-storchaka Feb 19, 2022
7908047
Merge branch 'main' into ast-deprecation-warnings
serhiy-storchaka May 8, 2022
dfedc35
Merge branch 'main' into ast-deprecation-warnings
iritkatriel Nov 27, 2022
b2ca297
Merge branch 'main' into ast-deprecation-warnings
serhiy-storchaka Dec 3, 2022
fbd02f3
Merge branch 'main' into ast-deprecation-warnings
iritkatriel Dec 6, 2022
4320111
Merge branch 'main' into ast-deprecation-warnings
hugovk May 4, 2023
fdefe87
fix patchcheck
AlexWaygood May 4, 2023
60ab0c4
Remove `DeprecationWarning`s for `Index` and `ExtSlice`
AlexWaygood May 4, 2023
951c548
Also emit DeprecationWarning during `isinstance()` checks
AlexWaygood May 5, 2023
9025b7e
Add whatsnew
AlexWaygood May 5, 2023
3d2de74
Tweak news entry
AlexWaygood May 5, 2023
4b818b6
Merge branch 'main' into ast-deprecation-warnings
AlexWaygood May 5, 2023
6e9f5f5
Apply suggestions from code review
AlexWaygood May 5, 2023
8f86b9a
Use `warnings._deprecated
AlexWaygood May 5, 2023
7e4cf89
Merge branch 'ast-deprecation-warnings' of https://github.com/AlexWay…
AlexWaygood May 5, 2023
8c949f3
Move whatsnew entry to correct location
AlexWaygood May 5, 2023
4a7a9c1
Merge branch 'main' into ast-deprecation-warnings
AlexWaygood May 5, 2023
3d71e66
Revert spurious whitespace change
AlexWaygood May 5, 2023
4261cc6
Revert warning for `dims` attribute (only deprecated in 3.9)
AlexWaygood May 5, 2023
a785a0c
Also remove deprecation for `ast.slice`, deprecated in 3.9
AlexWaygood May 5, 2023
a3491ea
Better unittest style
AlexWaygood May 5, 2023
3613908
.
AlexWaygood May 5, 2023
9bba5dd
Update 3.12.rst
AlexWaygood May 5, 2023
234243a
typo
AlexWaygood May 5, 2023
915e12e
Add comment explaining subtle code
AlexWaygood May 6, 2023
a7abed2
Simplify slightly
AlexWaygood May 6, 2023
7ceb9cc
Unnecessary f-string
AlexWaygood May 6, 2023
ecf092f
One last nit
AlexWaygood May 6, 2023
dd3e1e1
Merge branch 'main' into ast-deprecation-warnings
AlexWaygood May 6, 2023
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
13 changes: 13 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,19 @@ Pending Removal in Python 3.14
use :func:`importlib.util.find_spec` instead.
(Contributed by Nikita Sobolev in :gh:`97850`.)

* The following :mod:`ast` features have been deprecated in documentation since
Python 3.8, now cause a :exc:`DeprecationWarning` to be emitted at runtime
when they are accessed or used, and will be removed in Python 3.14:

* :class:`!ast.Num`
* :class:`!ast.Str`
* :class:`!ast.Bytes`
* :class:`!ast.NameConstant`
* :class:`!ast.Ellipsis`

Use :class:`ast.Constant` instead.
(Contributed by Serhiy Storchaka in :gh:`90953`.)

Pending Removal in Future Versions
----------------------------------

Expand Down
82 changes: 74 additions & 8 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,7 @@ def get_docstring(node, clean=True):
if not(node.body and isinstance(node.body[0], Expr)):
return None
node = node.body[0].value
if isinstance(node, Str):
text = node.s
elif isinstance(node, Constant) and isinstance(node.value, str):
if isinstance(node, Constant) and isinstance(node.value, str):
text = node.value
else:
return None
Expand Down Expand Up @@ -499,27 +497,66 @@ def generic_visit(self, node):
return node


_DEPRECATED_VALUE_ALIAS_MESSAGE = (
"{name} is deprecated and will be removed in Python {remove}; use value instead"
)
_DEPRECATED_CLASS_MESSAGE = (
"{name} is deprecated and will be removed in Python {remove}; "
"use ast.Constant instead"
)


# If the ast module is loaded more than once, only add deprecated methods once
if not hasattr(Constant, 'n'):
# The following code is for backward compatibility.
# It will be removed in future.

def _getter(self):
def _n_getter(self):
"""Deprecated. Use value instead."""
import warnings
warnings._deprecated(
"Attribute n", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
)
return self.value

def _n_setter(self, value):
import warnings
warnings._deprecated(
"Attribute n", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
)
self.value = value

def _s_getter(self):
"""Deprecated. Use value instead."""
import warnings
warnings._deprecated(
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
)
return self.value

def _setter(self, value):
def _s_setter(self, value):
import warnings
warnings._deprecated(
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
)
self.value = value

Constant.n = property(_getter, _setter)
Constant.s = property(_getter, _setter)
Constant.n = property(_n_getter, _n_setter)
Constant.s = property(_s_getter, _s_setter)

class _ABC(type):

def __init__(cls, *args):
cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""

def __instancecheck__(cls, inst):
if cls in _const_types:
import warnings
warnings._deprecated(
f"ast.{cls.__qualname__}",
message=_DEPRECATED_CLASS_MESSAGE,
remove=(3, 14)
)
if not isinstance(inst, Constant):
return False
if cls in _const_types:
Expand All @@ -543,6 +580,10 @@ def _new(cls, *args, **kwargs):
if pos < len(args):
raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
if cls in _const_types:
import warnings
warnings._deprecated(
f"ast.{cls.__qualname__}", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
)
return Constant(*args, **kwargs)
return Constant.__new__(cls, *args, **kwargs)

Expand All @@ -565,10 +606,19 @@ class Ellipsis(Constant, metaclass=_ABC):
_fields = ()

def __new__(cls, *args, **kwargs):
if cls is Ellipsis:
if cls is _ast_Ellipsis:
import warnings
warnings._deprecated(
"ast.Ellipsis", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
)
return Constant(..., *args, **kwargs)
return Constant.__new__(cls, *args, **kwargs)

# Keep another reference to Ellipsis in the global namespace
# so it can be referenced in Ellipsis.__new__
# (The original "Ellipsis" name is removed from the global namespace later on)
_ast_Ellipsis = Ellipsis

_const_types = {
Num: (int, float, complex),
Str: (str,),
Expand Down Expand Up @@ -1699,6 +1749,22 @@ def unparse(ast_obj):
return unparser.visit(ast_obj)


_deprecated_globals = {
name: globals().pop(name)
for name in ('Num', 'Str', 'Bytes', 'NameConstant', 'Ellipsis')
}

def __getattr__(name):
if name in _deprecated_globals:
globals()[name] = value = _deprecated_globals[name]
import warnings
warnings._deprecated(
f"ast.{name}", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
)
return value
raise AttributeError(f"module 'ast' has no attribute '{name}'")


def main():
import argparse

Expand Down
Loading