[Python-checkins] bpo-36917: Add default implementation of ast.NodeVisitor.visit_Constant(). (GH-15490)

Serhiy Storchaka webhook-mailer at python.org
Mon Aug 26 03:13:26 EDT 2019


https://github.com/python/cpython/commit/c3ea41e9bf100a5396b851488c3efe208e5e2179
commit: c3ea41e9bf100a5396b851488c3efe208e5e2179
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-08-26T10:13:19+03:00
summary:

bpo-36917: Add default implementation of ast.NodeVisitor.visit_Constant(). (GH-15490)

It emits a deprecation warning and calls corresponding method
visit_Num(), visit_Str(), etc.

files:
A Misc/NEWS.d/next/Library/2019-08-25-14-56-42.bpo-36917.GBxdw2.rst
M Doc/library/ast.rst
M Doc/whatsnew/3.8.rst
M Lib/ast.py
M Lib/test/test_ast.py

diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 1e718382589c..3d2c420bc224 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 0294e9a0830b..cd31cf6db6e8 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`.
diff --git a/Lib/ast.py b/Lib/ast.py
index ffeba179e510..1e639d11feec 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -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):
     """
@@ -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',
+}
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index f35d9e6f5451..3d12397ef683 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -3,6 +3,7 @@
 import os
 import sys
 import unittest
+import warnings
 import weakref
 from textwrap import dedent
 
@@ -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
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 000000000000..3509a7530b89
--- /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.



More information about the Python-checkins mailing list