From 0a7d2a643159f6571a842dae0a7ab403cce717bc Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 9 Jul 2025 21:56:02 +0900 Subject: [PATCH] codeobj.qualname --- Lib/test/test_code.py | 2 -- compiler/codegen/src/compile.rs | 26 ++++++++++++++++++++++++-- compiler/codegen/src/ir.rs | 5 ++++- compiler/core/src/bytecode.rs | 4 ++++ compiler/core/src/marshal.rs | 5 +++++ vm/src/builtins/code.rs | 5 +++++ 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 1aceff4efc..6b0dc09e28 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -249,8 +249,6 @@ def func(): pass co.co_freevars, co.co_cellvars) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_qualname(self): self.assertEqual( CodeTest.test_qualname.__code__.co_qualname, diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 4180b79712..ab9b469ad8 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -310,8 +310,8 @@ impl<'src> Compiler<'src> { kwonlyarg_count: 0, source_path: source_code.path.to_owned(), first_line_number: OneIndexed::MIN, - obj_name: code_name, - + obj_name: code_name.clone(), + qualname: Some(code_name), blocks: vec![ir::Block::default()], current_block: ir::BlockIdx(0), constants: IndexSet::default(), @@ -402,6 +402,13 @@ impl Compiler<'_> { .map(|(var, _)| var.clone()) .collect(); + // Calculate qualname based on the current qualified path + let qualname = if self.qualified_path.is_empty() { + Some(obj_name.clone()) + } else { + Some(self.qualified_path.join(".")) + }; + let info = ir::CodeInfo { flags, posonlyarg_count, @@ -410,6 +417,7 @@ impl Compiler<'_> { source_path, first_line_number, obj_name, + qualname, blocks: vec![ir::Block::default()], current_block: ir::BlockIdx(0), @@ -1496,6 +1504,10 @@ impl Compiler<'_> { self.push_qualified_path(name); let qualified_name = self.qualified_path.join("."); + + // Update the qualname in the current code info + self.code_stack.last_mut().unwrap().qualname = Some(qualified_name.clone()); + self.push_qualified_path(""); let (doc_str, body) = split_doc(body, &self.opts); @@ -1720,6 +1732,9 @@ impl Compiler<'_> { self.push_output(bytecode::CodeFlags::empty(), 0, 0, 0, name.to_owned()); + // Update the qualname in the current code info + self.code_stack.last_mut().unwrap().qualname = Some(qualified_name.clone()); + let (doc_str, body) = split_doc(body, &self.opts); let dunder_name = self.name("__name__"); @@ -3495,6 +3510,9 @@ impl Compiler<'_> { let mut func_flags = self .enter_function(&name, parameters.as_deref().unwrap_or(&Default::default()))?; + // Lambda qualname should be + self.code_stack.last_mut().unwrap().qualname = Some(name.clone()); + self.ctx = CompileContext { loop_data: Option::None, in_class: prev_ctx.in_class, @@ -3956,6 +3974,10 @@ impl Compiler<'_> { // Create magnificent function : self.push_output(flags, 1, 1, 0, name.to_owned()); + + // Set qualname for comprehension + self.code_stack.last_mut().unwrap().qualname = Some(name.to_owned()); + let arg0 = self.varname(".0")?; let return_none = init_collection.is_none(); diff --git a/compiler/codegen/src/ir.rs b/compiler/codegen/src/ir.rs index 7acd9d7f6a..5e115d9dee 100644 --- a/compiler/codegen/src/ir.rs +++ b/compiler/codegen/src/ir.rs @@ -73,6 +73,7 @@ pub struct CodeInfo { pub source_path: String, pub first_line_number: OneIndexed, pub obj_name: String, // Name of the object that created this code object + pub qualname: Option, // Qualified name of the object pub blocks: Vec, pub current_block: BlockIdx, @@ -99,6 +100,7 @@ impl CodeInfo { source_path, first_line_number, obj_name, + qualname, mut blocks, current_block: _, @@ -162,7 +164,8 @@ impl CodeInfo { kwonlyarg_count, source_path, first_line_number: Some(first_line_number), - obj_name, + obj_name: obj_name.clone(), + qualname: qualname.unwrap_or(obj_name), max_stackdepth, instructions: instructions.into_boxed_slice(), diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index be55fe3502..3fe9356004 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -115,6 +115,8 @@ pub struct CodeObject { pub max_stackdepth: u32, pub obj_name: C::Name, // Name of the object that created this code object + pub qualname: C::Name, + // Qualified name of the object (like CPython's co_qualname) pub cell2arg: Option>, pub constants: Box<[C]>, pub names: Box<[C::Name]>, @@ -1140,6 +1142,7 @@ impl CodeObject { freevars: map_names(self.freevars), source_path: bag.make_name(self.source_path.as_ref()), obj_name: bag.make_name(self.obj_name.as_ref()), + qualname: bag.make_name(self.qualname.as_ref()), instructions: self.instructions, locations: self.locations, @@ -1169,6 +1172,7 @@ impl CodeObject { freevars: map_names(&self.freevars), source_path: bag.make_name(self.source_path.as_ref()), obj_name: bag.make_name(self.obj_name.as_ref()), + qualname: bag.make_name(self.qualname.as_ref()), instructions: self.instructions.clone(), locations: self.locations.clone(), diff --git a/compiler/core/src/marshal.rs b/compiler/core/src/marshal.rs index 700bb48230..fdbae7ec30 100644 --- a/compiler/core/src/marshal.rs +++ b/compiler/core/src/marshal.rs @@ -210,6 +210,9 @@ pub fn deserialize_code( let len = rdr.read_u32()?; let obj_name = bag.make_name(rdr.read_str(len)?); + let len = rdr.read_u32()?; + let qualname = bag.make_name(rdr.read_str(len)?); + let len = rdr.read_u32()?; let cell2arg = (len != 0) .then(|| { @@ -250,6 +253,7 @@ pub fn deserialize_code( first_line_number, max_stackdepth, obj_name, + qualname, cell2arg, constants, names, @@ -609,6 +613,7 @@ pub fn serialize_code(buf: &mut W, code: &CodeObject) buf.write_u32(code.max_stackdepth); write_vec(buf, code.obj_name.as_ref().as_bytes()); + write_vec(buf, code.qualname.as_ref().as_bytes()); let cell2arg = code.cell2arg.as_deref().unwrap_or(&[]); write_len(buf, cell2arg.len()); diff --git a/vm/src/builtins/code.rs b/vm/src/builtins/code.rs index 0058dbf555..37c883043a 100644 --- a/vm/src/builtins/code.rs +++ b/vm/src/builtins/code.rs @@ -298,6 +298,10 @@ impl PyCode { fn co_name(&self) -> PyStrRef { self.code.obj_name.to_owned() } + #[pygetset] + fn co_qualname(&self) -> PyStrRef { + self.code.qualname.to_owned() + } #[pygetset] fn co_names(&self, vm: &VirtualMachine) -> PyTupleRef { @@ -401,6 +405,7 @@ impl PyCode { source_path: source_path.as_object().as_interned_str(vm).unwrap(), first_line_number, obj_name: obj_name.as_object().as_interned_str(vm).unwrap(), + qualname: self.code.qualname, max_stackdepth: self.code.max_stackdepth, instructions: self.code.instructions.clone(),