From d5d35079210347a5fd0385f9d91feeb65764a904 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Mon, 30 Jun 2025 22:50:27 +0900 Subject: [PATCH] Add sys._getframemodulename (#5873) --- .cspell.dict/python-more.txt | 1 + extra_tests/snippets/stdlib_sys.py | 11 +++++++++++ vm/src/builtins/function.rs | 3 +++ vm/src/frame.rs | 3 +++ vm/src/stdlib/sys.rs | 26 ++++++++++++++++++++++++++ vm/src/vm/mod.rs | 21 ++++++++++++++++++++- 6 files changed, 64 insertions(+), 1 deletion(-) diff --git a/.cspell.dict/python-more.txt b/.cspell.dict/python-more.txt index 90789b181..8e1c01283 100644 --- a/.cspell.dict/python-more.txt +++ b/.cspell.dict/python-more.txt @@ -78,6 +78,7 @@ getfilesystemencodeerrors getfilesystemencoding getformat getframe +getframemodulename getnewargs getpip getrandom diff --git a/extra_tests/snippets/stdlib_sys.py b/extra_tests/snippets/stdlib_sys.py index 09babbdd1..a3e9367c5 100644 --- a/extra_tests/snippets/stdlib_sys.py +++ b/extra_tests/snippets/stdlib_sys.py @@ -147,3 +147,14 @@ env["PYTHONSAFEPATH"] = "1" proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True, env=env) assert proc.stdout.rstrip() == "True" assert proc.returncode == 0, proc + +assert sys._getframemodulename() == "__main__", sys._getframemodulename() + + +def test_getframemodulename(): + return sys._getframemodulename() + + +test_getframemodulename.__module__ = "awesome_module" + +assert test_getframemodulename() == "awesome_module" diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index ae4784079..9dd9daa10 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -315,7 +315,9 @@ impl PyFunction { Ok(()) } +} +impl Py { pub fn invoke_with_locals( &self, func_args: FuncArgs, @@ -352,6 +354,7 @@ impl PyFunction { Scope::new(Some(locals), self.globals.clone()), vm.builtins.dict(), self.closure.as_ref().map_or(&[], |c| c.as_slice()), + Some(self.to_owned().into()), vm, ) .into_ref(&vm.ctx); diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3a02d582a..afea6b301 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -97,6 +97,7 @@ type Lasti = std::cell::Cell; #[pyclass(module = false, name = "frame")] pub struct Frame { pub code: PyRef, + pub func_obj: Option, pub fastlocals: PyMutex]>>, pub(crate) cells_frees: Box<[PyCellRef]>, @@ -139,6 +140,7 @@ impl Frame { scope: Scope, builtins: PyDictRef, closure: &[PyCellRef], + func_obj: Option, vm: &VirtualMachine, ) -> Frame { let cells_frees = std::iter::repeat_with(|| PyCell::default().into_ref(&vm.ctx)) @@ -160,6 +162,7 @@ impl Frame { globals: scope.globals, builtins, code, + func_obj, lasti: Lasti::new(0), state: PyMutex::new(state), trace: PyMutex::new(vm.ctx.none()), diff --git a/vm/src/stdlib/sys.rs b/vm/src/stdlib/sys.rs index 2e4f9e57e..3011c3b6c 100644 --- a/vm/src/stdlib/sys.rs +++ b/vm/src/stdlib/sys.rs @@ -513,6 +513,32 @@ mod sys { Ok(frame.clone()) } + #[pyfunction] + fn _getframemodulename(depth: OptionalArg, vm: &VirtualMachine) -> PyResult { + let depth = depth.into_option().unwrap_or(0); + + // Get the frame at the specified depth + if depth > vm.frames.borrow().len() - 1 { + return Ok(vm.ctx.none()); + } + + let idx = vm.frames.borrow().len() - depth - 1; + let frame = &vm.frames.borrow()[idx]; + + // If the frame has a function object, return its __module__ attribute + if let Some(func_obj) = &frame.func_obj { + match func_obj.get_attr(identifier!(vm, __module__), vm) { + Ok(module) => Ok(module), + Err(_) => { + // CPython clears the error and returns None + Ok(vm.ctx.none()) + } + } + } else { + Ok(vm.ctx.none()) + } + } + #[pyfunction] fn gettrace(vm: &VirtualMachine) -> PyObjectRef { vm.trace_func.borrow().clone() diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index 20c816100..e3409587a 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -449,7 +449,26 @@ impl VirtualMachine { } pub fn run_code_obj(&self, code: PyRef, scope: Scope) -> PyResult { - let frame = Frame::new(code, scope, self.builtins.dict(), &[], self).into_ref(&self.ctx); + use crate::builtins::PyFunction; + + // Create a function object for module code, similar to CPython's PyEval_EvalCode + let qualname = self.ctx.new_str(code.obj_name.as_str()); + let func = PyFunction::new( + code.clone(), + scope.globals.clone(), + None, // closure + None, // defaults + None, // kwdefaults + qualname, + self.ctx.new_tuple(vec![]), // type_params + self.ctx.new_dict(), // annotations + self.ctx.none(), // doc + self, + )?; + let func_obj = func.into_ref(&self.ctx).into(); + + let frame = Frame::new(code, scope, self.builtins.dict(), &[], Some(func_obj), self) + .into_ref(&self.ctx); self.run_frame(frame) }