diff options
-rw-r--r-- | Doc/library/ast.rst | 7 | ||||
-rw-r--r-- | Doc/whatsnew/3.8.rst | 7 | ||||
-rw-r--r-- | Lib/ast.py | 31 | ||||
-rw-r--r-- | Lib/test/test_ast.py | 51 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2019-08-25-14-56-42.bpo-36917.GBxdw2.rst | 3 |
5 files changed, 99 insertions, 0 deletions
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 1e71838..3d2c420 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -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() diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 0294e9a..cd31cf6 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -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`. @@ -360,6 +360,27 @@ class NodeVisitor(object): 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): """ @@ -487,3 +508,13 @@ _const_types = { _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', +} diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index f35d9e6..3d12397 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -3,6 +3,7 @@ import dis import os import sys import unittest +import warnings import weakref from textwrap import dedent @@ -1662,6 +1663,56 @@ class EndPositionTests(unittest.TestCase): 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 diff --git a/Misc/NEWS.d/next/Library/2019-08-25-14-56-42.bpo-36917.GBxdw2.rst b/Misc/NEWS.d/next/Library/2019-08-25-14-56-42.bpo-36917.GBxdw2.rst new file mode 100644 index 0000000..3509a75 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-08-25-14-56-42.bpo-36917.GBxdw2.rst @@ -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. |