Skip to content

Commit 2b3e622

Browse files
committed
Fix AST identifier interning
Ensure parsed name and function identifier fields use interned strings, matching CPython behavior. Assisted-by: Codex:gpt-5.6-sol
1 parent 28454cc commit 2b3e622

4 files changed

Lines changed: 24 additions & 5 deletions

File tree

Lib/test/test_ast/test_ast.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,6 @@ class MyNode(ast.AST):
13311331
self.assertEqual(repl.x, 0)
13321332
self.assertEqual(repl.y, y)
13331333

1334-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'x' is not 'x'
13351334
def test_replace_ignore_known_custom_instance_fields(self):
13361335
node = ast.parse('x').body[0].value
13371336
node.extra = extra = object() # add instance 'extra' field
@@ -1401,7 +1400,6 @@ def test_replace_accept_missing_field_with_default(self):
14011400
self.assertIs(node2.returns, None)
14021401
self.assertEqual(node2.decorator_list, [])
14031402

1404-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "Name\.__replace__\ got\ an\ unexpected\ keyword\ argument\ 'extra'\." does not match "replace() does not support Name objects"
14051403
def test_replace_reject_known_custom_instance_fields_commits(self):
14061404
node = ast.parse('x').body[0].value
14071405
node.extra = extra = object() # add instance 'extra' field
@@ -1417,7 +1415,6 @@ def test_replace_reject_known_custom_instance_fields_commits(self):
14171415
self.assertIs(node.ctx, context)
14181416
self.assertIs(node.extra, extra)
14191417

1420-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "Name\.__replace__\ got\ an\ unexpected\ keyword\ argument\ 'unknown'\." does not match "replace() does not support Name objects"
14211418
def test_replace_reject_unknown_instance_fields(self):
14221419
node = ast.parse('x').body[0].value
14231420
context = node.ctx

crates/vm/src/stdlib/_ast/expression.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,8 @@ impl Node for ast::ExprName {
13761376
.into_ref_with_type(vm, pyast::NodeExprName::static_type().to_owned())
13771377
.unwrap();
13781378
let dict = node.as_object().dict().unwrap();
1379-
dict.set_item("id", id.to_pyobject(vm), vm).unwrap();
1379+
dict.set_item("id", id.ast_to_object(vm, source_file), vm)
1380+
.unwrap();
13801381
dict.set_item("ctx", ctx.ast_to_object(vm, source_file), vm)
13811382
.unwrap();
13821383
node_add_location(&dict, range, vm, source_file);

crates/vm/src/stdlib/_ast/statement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ impl Node for ast::StmtFunctionDef {
409409

410410
let node = NodeAst.into_ref_with_type(vm, cls).unwrap();
411411
let dict = node.as_object().dict().unwrap();
412-
dict.set_item("name", vm.ctx.new_str(name.as_str()).to_pyobject(vm), vm)
412+
dict.set_item("name", name.ast_to_object(vm, source_file), vm)
413413
.unwrap();
414414
dict.set_item("args", parameters.ast_to_object(vm, source_file), vm)
415415
.unwrap();

extra_tests/snippets/stdlib_ast.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,27 @@ def foo():
3939
assert i.names[0].asname is None
4040

4141

42+
# Regression: parsed AST identifier fields are interned, matching CPython.
43+
import copy
44+
45+
name_literal = "x"
46+
name = ast.parse("x").body[0].value
47+
assert name.id is name_literal
48+
49+
name.extra = object()
50+
replacement = copy.replace(name)
51+
assert replacement.id is name.id
52+
assert replacement.ctx is name.ctx
53+
assert not hasattr(replacement, "extra")
54+
55+
function_name = "f"
56+
function = ast.parse("def f(): pass").body[0]
57+
assert function.name is function_name
58+
59+
async_function = ast.parse("async def f(): pass").body[0]
60+
assert async_function.name is function_name
61+
62+
4263
# Regression test for issue #4862:
4364
# A cyclic AST fed to compile() used to overflow the Rust stack and SIGSEGV.
4465
# After the fix, the recursion guard in ast_from_object raises RecursionError,

0 commit comments

Comments
 (0)