Skip to content

Commit a9dcb1b

Browse files
committed
Accept dict subclasses as warning module globals
Preserve overridable loader lookup while reading module names from stored dictionary data. Assisted-by: OpenAI Codex:model identifier unavailable
1 parent c0694d9 commit a9dcb1b

3 files changed

Lines changed: 50 additions & 3 deletions

File tree

‎crates/vm/src/stdlib/_warnings.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub fn warn(
2121
mod _warnings {
2222
use crate::{
2323
AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine,
24-
builtins::{PyDictRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
24+
builtins::{PyDict, PyDictRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
2525
convert::TryFromObject,
2626
function::OptionalArg,
2727
};
@@ -180,7 +180,7 @@ mod _warnings {
180180
let source_line = if let Some(mg) = args.module_globals.into_option() {
181181
if vm.is_none(&mg) {
182182
None
183-
} else if !mg.class().is(vm.ctx.types.dict_type) {
183+
} else if mg.downcast_ref::<PyDict>().is_none() {
184184
return Err(vm.new_type_error(format!(
185185
"module_globals must be a dict, not '{}'",
186186
mg.class().name()

‎crates/vm/src/warn.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ pub(crate) fn get_source_line(
201201
};
202202

203203
let module_name = if let Some(dict) = module_globals.downcast_ref::<PyDict>() {
204-
match dict.get_item_opt(identifier!(vm, __name__), vm)? {
204+
match dict.inner_getitem_opt(identifier!(vm, __name__), vm)? {
205205
Some(name) => name,
206206
None => return Ok(None),
207207
}

‎extra_tests/snippets/stdlib_warnings.py‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,50 @@ class CustomWarning(UserWarning):
1818
assert len(caught) == 1
1919
assert caught[0].message is message
2020
assert caught[0].category is CustomWarning
21+
22+
23+
from importlib.machinery import ModuleSpec
24+
25+
events = []
26+
27+
28+
class WarningLoader:
29+
def get_source(self, name):
30+
events.append(("source", name))
31+
return "source line\n"
32+
33+
34+
class WarningGlobals(dict):
35+
def get(self, key, default=None):
36+
events.append(("get", key))
37+
return super().get(key, default)
38+
39+
def __getitem__(self, key):
40+
raise AssertionError("module name must use stored dict lookup")
41+
42+
__missing__ = __getitem__
43+
44+
45+
loader = WarningLoader()
46+
module_globals = WarningGlobals(
47+
__name__="stored_name",
48+
__loader__=loader,
49+
__spec__=ModuleSpec("stored_name", loader),
50+
)
51+
for has_name in (True, False):
52+
events.clear()
53+
with warnings.catch_warnings(record=True) as caught:
54+
warnings.simplefilter("always")
55+
warnings.warn_explicit(
56+
"subclass globals",
57+
UserWarning,
58+
"fixed_ascii.py",
59+
1,
60+
module_globals=module_globals,
61+
)
62+
assert len(caught) == 1
63+
expected = [("get", "__loader__"), ("get", "__spec__")]
64+
if has_name:
65+
expected.append(("source", "stored_name"))
66+
del module_globals["__name__"]
67+
assert events == expected

0 commit comments

Comments
 (0)