diff --git a/crates/vm/src/stdlib/_warnings.rs b/crates/vm/src/stdlib/_warnings.rs index 35aca82d176..9387c16ebe3 100644 --- a/crates/vm/src/stdlib/_warnings.rs +++ b/crates/vm/src/stdlib/_warnings.rs @@ -21,7 +21,7 @@ pub fn warn( mod _warnings { use crate::{ AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine, - builtins::{PyDictRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef}, + builtins::{PyDict, PyDictRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef}, convert::TryFromObject, function::OptionalArg, }; @@ -180,7 +180,7 @@ mod _warnings { let source_line = if let Some(mg) = args.module_globals.into_option() { if vm.is_none(&mg) { None - } else if !mg.class().is(vm.ctx.types.dict_type) { + } else if mg.downcast_ref::().is_none() { return Err(vm.new_type_error(format!( "module_globals must be a dict, not '{}'", mg.class().name() diff --git a/crates/vm/src/warn.rs b/crates/vm/src/warn.rs index 3d8e7cb6fb1..14ae7199a9b 100644 --- a/crates/vm/src/warn.rs +++ b/crates/vm/src/warn.rs @@ -201,7 +201,7 @@ pub(crate) fn get_source_line( }; let module_name = if let Some(dict) = module_globals.downcast_ref::() { - match dict.get_item_opt(identifier!(vm, __name__), vm)? { + match dict.inner_getitem_opt(identifier!(vm, __name__), vm)? { Some(name) => name, None => return Ok(None), } diff --git a/extra_tests/snippets/stdlib_warnings.py b/extra_tests/snippets/stdlib_warnings.py index 73ccc512ea7..974899d8b9e 100644 --- a/extra_tests/snippets/stdlib_warnings.py +++ b/extra_tests/snippets/stdlib_warnings.py @@ -18,3 +18,50 @@ class CustomWarning(UserWarning): assert len(caught) == 1 assert caught[0].message is message assert caught[0].category is CustomWarning + + +from importlib.machinery import ModuleSpec + +events = [] + + +class WarningLoader: + def get_source(self, name): + events.append(("source", name)) + return "source line\n" + + +class WarningGlobals(dict): + def get(self, key, default=None): + events.append(("get", key)) + return super().get(key, default) + + def __getitem__(self, key): + raise AssertionError("module name must use stored dict lookup") + + __missing__ = __getitem__ + + +loader = WarningLoader() +module_globals = WarningGlobals( + __name__="stored_name", + __loader__=loader, + __spec__=ModuleSpec("stored_name", loader), +) +for has_name in (True, False): + events.clear() + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + warnings.warn_explicit( + "subclass globals", + UserWarning, + "fixed_ascii.py", + 1, + module_globals=module_globals, + ) + assert len(caught) == 1 + expected = [("get", "__loader__"), ("get", "__spec__")] + if has_name: + expected.append(("source", "stored_name")) + del module_globals["__name__"] + assert events == expected