Skip to content

Commit

Permalink
bpo-36917: Add default implementation of ast.NodeVisitor.visit_Consta…
Browse files Browse the repository at this point in the history
…nt(). (pythonGH-15490)

It emits a deprecation warning and calls corresponding method
visit_Num(), visit_Str(), etc.
(cherry picked from commit c3ea41e)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
  • Loading branch information
serhiy-storchaka authored and miss-islington committed Aug 26, 2019
1 parent 0778870 commit 3d02195
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Doc/library/ast.rst
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
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
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",
DeprecationWarning, 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
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', '', DeprecationWarning)
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
@@ -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.

0 comments on commit 3d02195

Please sign in to comment.