diff --git a/rope/base/ast.py b/rope/base/ast.py index 16c44da4..8a408969 100644 --- a/rope/base/ast.py +++ b/rope/base/ast.py @@ -8,18 +8,31 @@ # Suppress the mypy complaint: Module "ast" has no attribute "_const_node_type_names" from ast import _const_node_type_names # type:ignore except ImportError: - # backported from stdlib `ast` - assert sys.version_info < (3, 8) or sys.version_info >= (3, 14) - _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", - } + if sys.version_info >= (3, 8): + # Python 3.14+ removed _const_node_type_names; all constant literals + # are represented as ast.Constant nodes. + _const_node_type_names = { + bool: "Constant", # should be before int + type(None): "Constant", + int: "Constant", + float: "Constant", + complex: "Constant", + str: "Constant", + bytes: "Constant", + type(...): "Constant", + } + else: + # Python < 3.8 used separate node types (Num, Str, Bytes, etc.) + _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", + } def parse(source, filename="", *args, **kwargs): # type: ignore diff --git a/ropetest/refactor/patchedasttest.py b/ropetest/refactor/patchedasttest.py index 3ac341f2..5516be5e 100644 --- a/ropetest/refactor/patchedasttest.py +++ b/ropetest/refactor/patchedasttest.py @@ -1511,6 +1511,28 @@ def test_match_node_with_match_mapping_match_as(self): ]) +class ConstNodeTypeNamesTest(unittest.TestCase): + def _name(self, value): + import ast as stdlib_ast + node = stdlib_ast.Constant(value=value) + return ast.get_const_subtype_name(node) + + @unittest.skipIf(sys.version_info < (3, 8), "get_const_subtype_name requires 3.8+") + def test_int_name(self): + expected = "Constant" if sys.version_info >= (3, 14) else "Num" + self.assertEqual(self._name(1), expected) + + @unittest.skipIf(sys.version_info < (3, 8), "get_const_subtype_name requires 3.8+") + def test_str_name(self): + expected = "Constant" if sys.version_info >= (3, 14) else "Str" + self.assertEqual(self._name("hello"), expected) + + @unittest.skipIf(sys.version_info < (3, 8), "get_const_subtype_name requires 3.8+") + def test_none_name(self): + expected = "Constant" if sys.version_info >= (3, 14) else "NameConstant" + self.assertEqual(self._name(None), expected) + + class _ResultChecker: def __init__(self, test_case, ast): self.test_case = test_case