Repository navigation
Align debugger hook dispatch and callback lifetimes #9722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
82dad85
98edaac
c477dde
28d0c74
3bab1bf
1c6426a
95e58e2
e5ce631
da3b961
b607f65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,61 @@ use crate::{ | |
| use core::{cell::Cell, sync::atomic::Ordering, time::Duration}; | ||
| use std::{io::Write, time::Instant}; | ||
|
|
||
| /// Import for side effects and return the exact named module (PyImport_Import). | ||
| /// Unlike ordinary imports, this re-reads globals' builtins and passes globals | ||
| /// as both namespace arguments to the selected importer. | ||
| pub fn import_module(vm: &VirtualMachine, module_name: &Py<PyStr>) -> PyResult { | ||
| let from_list = vm.ctx.new_list(vec![]); | ||
| let mut importer: Option<PyObjectRef> = None; | ||
| let mut builtins: Option<PyObjectRef> = None; | ||
| let mut globals: Option<PyObjectRef> = crate::frame::current_globals().map(Into::into); | ||
| let has_globals = globals.is_some(); | ||
| // Keep the selected module/error alive across callback-capable cleanup on | ||
| // every path, including lookup and invocation failures. | ||
| (|| { | ||
| let globals = globals.get_or_insert_with(|| vm.ctx.new_dict().into()); | ||
| let builtins = builtins.insert(if has_globals { | ||
| globals.get_item(identifier!(vm, __builtins__), vm)? | ||
| } else { | ||
| // Bootstrap without calling this helper or the frame's importer. | ||
| let builtins = import_module_level(vm.ctx.intern_str("builtins"), None, None, 0, vm)?; | ||
| globals.set_item(identifier!(vm, __builtins__), builtins.clone(), vm)?; | ||
| builtins | ||
| }); | ||
| let importer = importer.insert(if builtins.downcast_ref::<PyDict>().is_some() { | ||
| // Dict subclasses keep their subscription callbacks, but every | ||
| // lookup failure is replaced, not just KeyError. | ||
| builtins | ||
| .get_item(identifier!(vm, __import__), vm) | ||
| .map_err(|_err| vm.new_key_error(identifier!(vm, __import__).to_owned().into()))? | ||
| } else { | ||
| builtins.get_attr(identifier!(vm, __import__), vm)? | ||
| }); | ||
| let imported = importer.call( | ||
| ( | ||
| module_name.to_owned(), | ||
| globals.clone(), | ||
| globals.clone(), | ||
| from_list.clone(), | ||
| 0, | ||
| ), | ||
| vm, | ||
| )?; | ||
| drop(imported); | ||
|
|
||
| // RustPython currently exposes the registry through sys' dictionary; | ||
| // unlike CPython's interpreter-owned registry, reassignment is visible. | ||
| let modules = vm.sys_module.dict().get_item("modules", vm)?; | ||
| let selected = match modules.get_item(module_name, vm) { | ||
|
Comment on lines
+58
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Ok(module) => Ok(Some(module)), | ||
| Err(err) if err.fast_isinstance(vm.ctx.exceptions.key_error) => Ok(None), | ||
| Err(err) => Err(err), | ||
| }; | ||
| drop(modules); | ||
| selected?.ok_or_else(|| vm.new_key_error(module_name.to_owned().into())) | ||
| })() | ||
| } | ||
|
|
||
| /// Return a module from `sys.modules`, creating it if absent or not a module. | ||
| /// | ||
| /// Exact dictionaries use conditional mutations so concurrent callers agree on | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,7 @@ mod builtins { | |
| common::hash::PyHash, | ||
| function::{ | ||
| ArgCallable, ArgIndex, ArgIntoBool, ArgMapping, ArgSpec, ArgStrOrBytesLike, Either, | ||
| FsPath, FuncArgs, KwArgs, NameKws, NameObjects, OptionalArg, PosArgs, | ||
| FsPath, FuncArgs, KwArgs, NameObjects, OptionalArg, PosArgs, | ||
| }, | ||
| protocol::{PyIter, PyIterReturn}, | ||
| py_io, | ||
|
|
@@ -800,22 +800,21 @@ mod builtins { | |
| } | ||
|
|
||
| #[pyfunction] | ||
| fn breakpoint( | ||
| args: PosArgs, | ||
| kws: KwArgs<PyObjectRef, NameKws>, | ||
| vm: &VirtualMachine, | ||
| ) -> PyResult { | ||
| let args = FuncArgs { | ||
| args: args.into_vec(), | ||
| kwargs: kws.into_default(), | ||
| }; | ||
| match vm | ||
| fn breakpoint(args: FuncArgs, vm: &VirtualMachine) -> PyResult { | ||
| // PySys_GetAttr reads the dictionary, bypassing module attribute hooks. | ||
| // A present None is still selected and audited before the call fails. | ||
| let hook = vm | ||
| .sys_module | ||
| .get_attr(vm.ctx.intern_str("breakpointhook"), vm) | ||
| { | ||
| Ok(hook) => hook.as_ref().call(args, vm), | ||
| Err(_) => Err(vm.new_runtime_error("lost sys.breakpointhook")), | ||
| } | ||
| .dict() | ||
| .get_item_opt("breakpointhook", vm)? | ||
| .ok_or_else(|| vm.new_runtime_error("lost sys.breakpointhook"))?; | ||
| vm.audit("builtins.breakpoint", || (hook.clone(),))?; | ||
|
|
||
| // Audit callbacks can replace sys.breakpointhook. Invoke the selected | ||
| // hook, retaining the original arguments until that hook is released. | ||
| // Scope cleanup drops the later hook binding before the retained args. | ||
| let (_retained_args, hook) = (args.clone(), hook); | ||
|
Comment on lines
+813
to
+816
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it doesn't make sense. if there is no bug, retaining args here must not have any meaning inside the call |
||
| hook.call(args, vm) | ||
| } | ||
|
|
||
| #[pyfunction] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: RustPython/RustPython
Length of output: 3440
Script executed:
Repository: RustPython/RustPython
Length of output: 43392
Script executed:
Repository: RustPython/RustPython
Length of output: 41981
Script executed:
Repository: RustPython/RustPython
Length of output: 14679
Web query:
CPython d220409a13b77de7a6c30abe16ea90db01528edc PyImport_Import builtins frozendict _PyAnyDict_Check PyDict_Check source import.cResult:
Script executed:
Repository: RustPython/RustPython
Length of output: 13217
Script executed:
Repository: RustPython/RustPython
Length of output: 4228
Web query:
Python 3.15 C API PyImport_Import current import hooks __import__ import module documentationResult:
Script executed:
Repository: RustPython/RustPython
Length of output: 8043
Recognize frozendict builtins as a mapping.
When
__builtins__is a frozendict,import_modulelooks for__import__as an attribute instead of a mapping entry. Consequently,sys.breakpointhookraisesAttributeErrorbefore calling a valid importer. Use mapping lookup for frozendict builtins too.