Skip to content

Commit 5ca707d

Browse files
[3.12] gh-104799: PEP 695 backward compatibility for ast.unparse (GH-105846) (#105862)
(cherry picked from commit 957a974) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
1 parent e6982c5 commit 5ca707d

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

Lib/ast.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,8 @@ def visit_ClassDef(self, node):
10511051
self.fill("@")
10521052
self.traverse(deco)
10531053
self.fill("class " + node.name)
1054-
self._type_params_helper(node.type_params)
1054+
if hasattr(node, "type_params"):
1055+
self._type_params_helper(node.type_params)
10551056
with self.delimit_if("(", ")", condition = node.bases or node.keywords):
10561057
comma = False
10571058
for e in node.bases:
@@ -1083,7 +1084,8 @@ def _function_helper(self, node, fill_suffix):
10831084
self.traverse(deco)
10841085
def_str = fill_suffix + " " + node.name
10851086
self.fill(def_str)
1086-
self._type_params_helper(node.type_params)
1087+
if hasattr(node, "type_params"):
1088+
self._type_params_helper(node.type_params)
10871089
with self.delimit("(", ")"):
10881090
self.traverse(node.args)
10891091
if node.returns:

Lib/test/test_unparse.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Tests for the unparse.py script in the Tools/parser directory."""
1+
"""Tests for ast.unparse."""
22

33
import unittest
44
import test.support
@@ -625,6 +625,78 @@ def test_star_expr_assign_target_multiple(self):
625625
self.check_src_roundtrip("a, b = [c, d] = e, f = g")
626626

627627

628+
class ManualASTCreationTestCase(unittest.TestCase):
629+
"""Test that AST nodes created without a type_params field unparse correctly."""
630+
631+
def test_class(self):
632+
node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[])
633+
ast.fix_missing_locations(node)
634+
self.assertEqual(ast.unparse(node), "class X:\n pass")
635+
636+
def test_class_with_type_params(self):
637+
node = ast.ClassDef(name="X", bases=[], keywords=[], body=[ast.Pass()], decorator_list=[],
638+
type_params=[ast.TypeVar("T")])
639+
ast.fix_missing_locations(node)
640+
self.assertEqual(ast.unparse(node), "class X[T]:\n pass")
641+
642+
def test_function(self):
643+
node = ast.FunctionDef(
644+
name="f",
645+
args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
646+
body=[ast.Pass()],
647+
decorator_list=[],
648+
returns=None,
649+
)
650+
ast.fix_missing_locations(node)
651+
self.assertEqual(ast.unparse(node), "def f():\n pass")
652+
653+
def test_function_with_type_params(self):
654+
node = ast.FunctionDef(
655+
name="f",
656+
args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
657+
body=[ast.Pass()],
658+
decorator_list=[],
659+
returns=None,
660+
type_params=[ast.TypeVar("T")],
661+
)
662+
ast.fix_missing_locations(node)
663+
self.assertEqual(ast.unparse(node), "def f[T]():\n pass")
664+
665+
def test_function_with_type_params_and_bound(self):
666+
node = ast.FunctionDef(
667+
name="f",
668+
args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
669+
body=[ast.Pass()],
670+
decorator_list=[],
671+
returns=None,
672+
type_params=[ast.TypeVar("T", bound=ast.Name("int"))],
673+
)
674+
ast.fix_missing_locations(node)
675+
self.assertEqual(ast.unparse(node), "def f[T: int]():\n pass")
676+
677+
def test_async_function(self):
678+
node = ast.AsyncFunctionDef(
679+
name="f",
680+
args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
681+
body=[ast.Pass()],
682+
decorator_list=[],
683+
returns=None,
684+
)
685+
ast.fix_missing_locations(node)
686+
self.assertEqual(ast.unparse(node), "async def f():\n pass")
687+
688+
def test_async_function_with_type_params(self):
689+
node = ast.AsyncFunctionDef(
690+
name="f",
691+
args=ast.arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
692+
body=[ast.Pass()],
693+
decorator_list=[],
694+
returns=None,
695+
type_params=[ast.TypeVar("T")],
696+
)
697+
ast.fix_missing_locations(node)
698+
self.assertEqual(ast.unparse(node), "async def f[T]():\n pass")
699+
628700

629701
class DirectoryTestCase(ASTTestCase):
630702
"""Test roundtrip behaviour on all files in Lib and Lib/test."""
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Enable :func:`ast.unparse` to unparse function and class definitions created
2+
without the new ``type_params`` field from :pep:`695`. Patch by Jelle
3+
Zijlstra.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy