Skip to content
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

[3.8] bpo-36917: Add default implementation of ast.NodeVisitor.visit_Constant(). (GH-15490) #15509

Merged
merged 3 commits into from
Aug 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions Doc/library/ast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ and classes for traversing abstract syntax trees:
during traversal. For this a special visitor exists
(:class:`NodeTransformer`) that allows modifications.

.. deprecated:: 3.8

Methods :meth:`visit_Num`, :meth:`visit_Str`, :meth:`visit_Bytes`,
:meth:`visit_NameConstant` and :meth:`visit_Ellipsis` are deprecated
now and will not be called in future Python versions. Add the
:meth:`visit_Constant` method to handle all constant nodes.


.. class:: NodeTransformer()

Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,13 @@ Deprecated
versions. :class:`~ast.Constant` should be used instead.
(Contributed by Serhiy Storchaka in :issue:`32892`.)

* :class:`ast.NodeVisitor` methods ``visit_Num()``, ``visit_Str()``,
``visit_Bytes()``, ``visit_NameConstant()`` and ``visit_Ellipsis()`` are
deprecated now and will not be called in future Python versions.
Add the :meth:`~ast.NodeVisitor.visit_Constant` method to handle all
constant nodes.
(Contributed by Serhiy Storchaka in :issue:`36917`.)

* The following functions and methods are deprecated in the :mod:`gettext`
module: :func:`~gettext.lgettext`, :func:`~gettext.ldgettext`,
:func:`~gettext.lngettext` and :func:`~gettext.ldngettext`.
Expand Down
31 changes: 31 additions & 0 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,27 @@ 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",
PendingDeprecationWarning, 2)
return visitor(node)
return self.generic_visit(node)


class NodeTransformer(NodeVisitor):
"""
Expand Down Expand Up @@ -487,3 +508,13 @@ def __new__(cls, *args, **kwargs):
_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',
}
51 changes: 51 additions & 0 deletions Lib/test/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import sys
import unittest
import warnings
import weakref
from textwrap import dedent

Expand Down Expand Up @@ -1662,6 +1663,56 @@ class C:
self.assertEqual(ast.get_source_segment(s, cdef.body[0], padded=True), s_method)


class NodeVisitorTests(unittest.TestCase):
def test_old_constant_nodes(self):
class Visitor(ast.NodeVisitor):
def visit_Num(self, node):
log.append((node.lineno, 'Num', node.n))
def visit_Str(self, node):
log.append((node.lineno, 'Str', node.s))
def visit_Bytes(self, node):
log.append((node.lineno, 'Bytes', node.s))
def visit_NameConstant(self, node):
log.append((node.lineno, 'NameConstant', node.value))
def visit_Ellipsis(self, node):
log.append((node.lineno, 'Ellipsis', ...))
mod = ast.parse(dedent('''\
i = 42
f = 4.25
c = 4.25j
s = 'string'
b = b'bytes'
t = True
n = None
e = ...
'''))
visitor = Visitor()
log = []
with warnings.catch_warnings(record=True) as wlog:
warnings.filterwarnings('always', '', PendingDeprecationWarning)
visitor.visit(mod)
self.assertEqual(log, [
(1, 'Num', 42),
(2, 'Num', 4.25),
(3, 'Num', 4.25j),
(4, 'Str', 'string'),
(5, 'Bytes', b'bytes'),
(6, 'NameConstant', True),
(7, 'NameConstant', None),
(8, 'Ellipsis', ...),
])
self.assertEqual([str(w.message) for w in wlog], [
'visit_Num is deprecated; add visit_Constant',
'visit_Num is deprecated; add visit_Constant',
'visit_Num is deprecated; add visit_Constant',
'visit_Str is deprecated; add visit_Constant',
'visit_Bytes is deprecated; add visit_Constant',
'visit_NameConstant is deprecated; add visit_Constant',
'visit_NameConstant is deprecated; add visit_Constant',
'visit_Ellipsis is deprecated; add visit_Constant',
])


def main():
if __name__ != '__main__':
return
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add default implementation of the :meth:`ast.NodeVisitor.visit_Constant`
method which emits a deprecation warning and calls corresponding methody
``visit_Num()``, ``visit_Str()``, etc.