summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-06-16 16:59:25 (GMT)
committerGitHub <noreply@github.com>2023-06-16 16:59:25 (GMT)
commit5ca707d1e41af165d6bbc6bbc8026256a0a941d3 (patch)
tree49c59fcb0f645a4082aec941384014b4a609e7f8
parente6982c58602bba9bc1f7c1e6e9cc5f65610d1c6e (diff)
downloadcpython-5ca707d1e41af165d6bbc6bbc8026256a0a941d3.zip
cpython-5ca707d1e41af165d6bbc6bbc8026256a0a941d3.tar.gz
cpython-5ca707d1e41af165d6bbc6bbc8026256a0a941d3.tar.bz2
[3.12] gh-104799: PEP 695 backward compatibility for ast.unparse (GH-105846) (#105862)
(cherry picked from commit 957a974d4fc1575787e4a29a399a47520d6df6d3) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
-rw-r--r--Lib/ast.py6
-rw-r--r--Lib/test/test_unparse.py74
-rw-r--r--Misc/NEWS.d/next/Library/2023-06-15-18-11-47.gh-issue-104799.BcLzbP.rst3
3 files changed, 80 insertions, 3 deletions
diff --git a/Lib/ast.py b/Lib/ast.py
index 226910e..a307f3e 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -1051,7 +1051,8 @@ class _Unparser(NodeVisitor):
self.fill("@")
self.traverse(deco)
self.fill("class " + node.name)
- self._type_params_helper(node.type_params)
+ if hasattr(node, "type_params"):
+ self._type_params_helper(node.type_params)
with self.delimit_if("(", ")", condition = node.bases or node.keywords):
comma = False
for e in node.bases:
@@ -1083,7 +1084,8 @@ class _Unparser(NodeVisitor):
self.traverse(deco)
def_str = fill_suffix + " " + node.name
self.fill(def_str)
- self._type_params_helper(node.type_params)
+ if hasattr(node, "type_params"):
+ self._type_params_helper(node.type_params)
with self.delimit("(", ")"):
self.traverse(node.args)
if node.returns:
diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py
index 88c7c3a..41a6318 100644
--- a/Lib/test/test_unparse.py
+++ b/Lib/test/test_unparse.py
@@ -1,4 +1,4 @@
-"""Tests for the unparse.py script in the Tools/parser directory."""
+"""Tests for ast.unparse."""
import unittest
import test.support
@@ -625,6 +625,78 @@ class CosmeticTestCase(ASTTestCase):
self.check_src_roundtrip("a, b = [c, d] = e, f = g")
+class ManualASTCreationTestCase(unittest.TestCase):
+ """Test that AST nodes created without a type_params field unparse correctly."""
+
+ def test_class(self):
+ node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[])
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "class X:\n pass")
+
+ def test_class_with_type_params(self):
+ node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[],
+ type_params=[ast.TypeVar("T")])
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "class X[T]:\n pass")
+
+ def test_function(self):
+ node = ast.FunctionDef(
+ name="f",
+ args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
+ body=[ast.Pass()],
+ decorator_list=[],
+ returns=None,
+ )
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "def f():\n pass")
+
+ def test_function_with_type_params(self):
+ node = ast.FunctionDef(
+ name="f",
+ args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
+ body=[ast.Pass()],
+ decorator_list=[],
+ returns=None,
+ type_params=[ast.TypeVar("T")],
+ )
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "def f[T]():\n pass")
+
+ def test_function_with_type_params_and_bound(self):
+ node = ast.FunctionDef(
+ name="f",
+ args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
+ body=[ast.Pass()],
+ decorator_list=[],
+ returns=None,
+ type_params=[ast.TypeVar("T", bound=ast.Name("int"))],
+ )
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "def f[T: int]():\n pass")
+
+ def test_async_function(self):
+ node = ast.AsyncFunctionDef(
+ name="f",
+ args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
+ body=[ast.Pass()],
+ decorator_list=[],
+ returns=None,
+ )
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "async def f():\n pass")
+
+ def test_async_function_with_type_params(self):
+ node = ast.AsyncFunctionDef(
+ name="f",
+ args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
+ body=[ast.Pass()],
+ decorator_list=[],
+ returns=None,
+ type_params=[ast.TypeVar("T")],
+ )
+ ast.fix_missing_locations(node)
+ self.assertEqual(ast.unparse(node), "async def f[T]():\n pass")
+
class DirectoryTestCase(ASTTestCase):
"""Test roundtrip behaviour on all files in Lib and Lib/test."""
diff --git a/Misc/NEWS.d/next/Library/2023-06-15-18-11-47.gh-issue-104799.BcLzbP.rst b/Misc/NEWS.d/next/Library/2023-06-15-18-11-47.gh-issue-104799.BcLzbP.rst
new file mode 100644
index 0000000..d0dbff4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-06-15-18-11-47.gh-issue-104799.BcLzbP.rst
@@ -0,0 +1,3 @@
+Enable :func:`ast.unparse` to unparse function and class definitions created
+without the new ``type_params`` field from :pep:`695`. Patch by Jelle
+Zijlstra.