[3.8] bpo-36917: Add default implementation of ast.NodeVisitor.visit_… · python/cpython@522a394 · GitHub
Skip to content

Commit 522a394

Browse files
miss-islingtonserhiy-storchaka
authored andcommitted
[3.8] bpo-36917: Add default implementation of ast.NodeVisitor.visit_Constant(). (GH-15490) (GH-15509)
It emits a deprecation warning and calls corresponding method visit_Num(), visit_Str(), etc. (cherry picked from commit c3ea41e)
1 parent a387517 commit 522a394

5 files changed

Lines changed: 99 additions & 0 deletions

File tree

Doc/library/ast.rst

Lines changed: 7 additions & 0 deletions

Doc/whatsnew/3.8.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,13 @@ Deprecated
13601360
versions. :class:`~ast.Constant` should be used instead.
13611361
(Contributed by Serhiy Storchaka in :issue:`32892`.)
13621362

1363+
* :class:`ast.NodeVisitor` methods ``visit_Num()``, ``visit_Str()``,
1364+
``visit_Bytes()``, ``visit_NameConstant()`` and ``visit_Ellipsis()`` are
1365+
deprecated now and will not be called in future Python versions.
1366+
Add the :meth:`~ast.NodeVisitor.visit_Constant` method to handle all
1367+
constant nodes.
1368+
(Contributed by Serhiy Storchaka in :issue:`36917`.)
1369+
13631370
* The following functions and methods are deprecated in the :mod:`gettext`
13641371
module: :func:`~gettext.lgettext`, :func:`~gettext.ldgettext`,
13651372
:func:`~gettext.lngettext` and :func:`~gettext.ldngettext`.

Lib/ast.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,27 @@ def generic_visit(self, node):
360360
elif isinstance(value, AST):
361361
self.visit(value)
362362

363+
def visit_Constant(self, node):
364+
value = node.value
365+
type_name = _const_node_type_names.get(type(value))
366+
if type_name is None:
367+
for cls, name in _const_node_type_names.items():
368+
if isinstance(value, cls):
369+
type_name = name
370+
break
371+
if type_name is not None:
372+
method = 'visit_' + type_name
373+
try:
374+
visitor = getattr(self, method)
375+
except AttributeError:
376+
pass
377+
else:
378+
import warnings
379+
warnings.warn(f"{method} is deprecated; add visit_Constant",
380+
PendingDeprecationWarning, 2)
381+
return visitor(node)
382+
return self.generic_visit(node)
383+
363384

364385
class NodeTransformer(NodeVisitor):
365386
"""
@@ -487,3 +508,13 @@ def __new__(cls, *args, **kwargs):
487508
_const_types_not = {
488509
Num: (bool,),
489510
}
511+
_const_node_type_names = {
512+
bool: 'NameConstant', # should be before int
513+
type(None): 'NameConstant',
514+
int: 'Num',
515+
float: 'Num',
516+
complex: 'Num',
517+
str: 'Str',
518+
bytes: 'Bytes',
519+
type(...): 'Ellipsis',
520+
}

Lib/test/test_ast.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import sys
55
import unittest
6+
import warnings
67
import weakref
78
from textwrap import dedent
89

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

16641665

1666+
class NodeVisitorTests(unittest.TestCase):
1667+
def test_old_constant_nodes(self):
1668+
class Visitor(ast.NodeVisitor):
1669+
def visit_Num(self, node):
1670+
log.append((node.lineno, 'Num', node.n))
1671+
def visit_Str(self, node):
1672+
log.append((node.lineno, 'Str', node.s))
1673+
def visit_Bytes(self, node):
1674+
log.append((node.lineno, 'Bytes', node.s))
1675+
def visit_NameConstant(self, node):
1676+
log.append((node.lineno, 'NameConstant', node.value))
1677+
def visit_Ellipsis(self, node):
1678+
log.append((node.lineno, 'Ellipsis', ...))
1679+
mod = ast.parse(dedent('''\
1680+
i = 42
1681+
f = 4.25
1682+
c = 4.25j
1683+
s = 'string'
1684+
b = b'bytes'
1685+
t = True
1686+
n = None
1687+
e = ...
1688+
'''))
1689+
visitor = Visitor()
1690+
log = []
1691+
with warnings.catch_warnings(record=True) as wlog:
1692+
warnings.filterwarnings('always', '', PendingDeprecationWarning)
1693+
visitor.visit(mod)
1694+
self.assertEqual(log, [
1695+
(1, 'Num', 42),
1696+
(2, 'Num', 4.25),
1697+
(3, 'Num', 4.25j),
1698+
(4, 'Str', 'string'),
1699+
(5, 'Bytes', b'bytes'),
1700+
(6, 'NameConstant', True),
1701+
(7, 'NameConstant', None),
1702+
(8, 'Ellipsis', ...),
1703+
])
1704+
self.assertEqual([str(w.message) for w in wlog], [
1705+
'visit_Num is deprecated; add visit_Constant',
1706+
'visit_Num is deprecated; add visit_Constant',
1707+
'visit_Num is deprecated; add visit_Constant',
1708+
'visit_Str is deprecated; add visit_Constant',
1709+
'visit_Bytes is deprecated; add visit_Constant',
1710+
'visit_NameConstant is deprecated; add visit_Constant',
1711+
'visit_NameConstant is deprecated; add visit_Constant',
1712+
'visit_Ellipsis is deprecated; add visit_Constant',
1713+
])
1714+
1715+
16651716
def main():
16661717
if __name__ != '__main__':
16671718
return
Lines changed: 3 additions & 0 deletions

0 commit comments

Comments
 (0)