Skip to content

Commit

Permalink
pythongh-119562: Remove AST nodes deprecated since Python 3.8
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed May 25, 2024
1 parent 4b7eb32 commit 236b497
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 581 deletions.
28 changes: 28 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,34 @@ Deprecated
Removed
=======

ast
---

* Remove the following classes. They were all deprecated since Python 3.8,
and have emitted deprecation warnings since Python 3.12:

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

Use :class:`ast.Constant` instead. As a consequence of these removals,
``visit_Num``, ``visit_Str``, ``visit_Bytes``, ``visit_NameConstant`` and
``visit_Ellipsis`` methods will no longer have any effect on
:class:`ast.NodeVisitor` subclasses; use ``visit_Constant`` instead.

Also, remove the following deprecated properties on :class:`ast.Constant`,
which were present for compatibility with the now-removed AST classes:

* :attr:`!ast.Constant.n`
* :attr:`!ast.Constant.s`

Use :attr:`!ast.Constant.value` instead.

(Contributed by Alex Waygood in :gh:`119562`.)


argparse
--------

Expand Down
174 changes: 1 addition & 173 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,27 +508,6 @@ def generic_visit(self, node):
elif isinstance(value, AST):
self.visit(value)

def visit_Constant(self, node):
value = node.value
type_name = _const_node_type_names.get(type(value))
if type_name is None:
for cls, name in _const_node_type_names.items():
if isinstance(value, cls):
type_name = name
break
if type_name is not None:
method = 'visit_' + type_name
try:
visitor = getattr(self, method)
except AttributeError:
pass
else:
import warnings
warnings.warn(f"{method} is deprecated; add visit_Constant",
DeprecationWarning, 2)
return visitor(node)
return self.generic_visit(node)


class NodeTransformer(NodeVisitor):
"""
Expand Down Expand Up @@ -597,142 +576,6 @@ def generic_visit(self, node):
"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 _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 _s_setter(self, value):
import warnings
warnings._deprecated(
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
)
self.value = value

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:
try:
value = inst.value
except AttributeError:
return False
else:
return (
isinstance(value, _const_types[cls]) and
not isinstance(value, _const_types_not.get(cls, ()))
)
return type.__instancecheck__(cls, inst)

def _new(cls, *args, **kwargs):
for key in kwargs:
if key not in cls._fields:
# arbitrary keyword arguments are accepted
continue
pos = cls._fields.index(key)
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)

class Num(Constant, metaclass=_ABC):
_fields = ('n',)
__new__ = _new

class Str(Constant, metaclass=_ABC):
_fields = ('s',)
__new__ = _new

class Bytes(Constant, metaclass=_ABC):
_fields = ('s',)
__new__ = _new

class NameConstant(Constant, metaclass=_ABC):
__new__ = _new

class Ellipsis(Constant, metaclass=_ABC):
_fields = ()

def __new__(cls, *args, **kwargs):
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,),
Bytes: (bytes,),
NameConstant: (type(None), bool),
Ellipsis: (type(...),),
}
_const_types_not = {
Num: (bool,),
}

_const_node_type_names = {
bool: 'NameConstant', # should be before int
type(None): 'NameConstant',
int: 'Num',
float: 'Num',
complex: 'Num',
str: 'Str',
bytes: 'Bytes',
type(...): 'Ellipsis',
}

class slice(AST):
"""Deprecated AST node class."""

Expand Down Expand Up @@ -1884,27 +1727,12 @@ def visit_MatchOr(self, node):
self.set_precedence(_Precedence.BOR.next(), *node.patterns)
self.interleave(lambda: self.write(" | "), self.traverse, node.patterns)


def unparse(ast_obj):
unparser = _Unparser()
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

0 comments on commit 236b497

Please sign in to comment.