Accept dict subclasses as warning module globals (#9062)

Preserve overridable loader lookup while reading module names from stored dictionary data.

Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-13 10:47:23 +09:00
1 parent c3e1276229
commit 140761e80b
3 files changed
+50 -3

No files matched your search

+2 -2
View File
@@ -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::<PyDict>().is_none() {
return Err(vm.new_type_error(format!(
"module_globals must be a dict, not '{}'",
mg.class().name()
+1 -1
View File
@@ -201,7 +201,7 @@ pub(crate) fn get_source_line(
};
let module_name = if let Some(dict) = module_globals.downcast_ref::<PyDict>() {
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),
}
+47
View File
@@ -18,3 +18,50 @@ for category in (None, DeprecationWarning, int, 42):
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