diff --git a/crates/vm/src/stdlib/_imp.rs b/crates/vm/src/stdlib/_imp.rs index a2bb2a0ab..b2b278311 100644 --- a/crates/vm/src/stdlib/_imp.rs +++ b/crates/vm/src/stdlib/_imp.rs @@ -193,7 +193,7 @@ fn find_frozen(name: &str, vm: &VirtualMachine) -> Result PyResult<()> { + // Even an empty set checks the hash of a str subclass. + name.as_object().hash(vm)?; + Ok(()) + } + #[pyfunction] fn is_builtin(name: PyUtf8StrRef, vm: &VirtualMachine) -> bool { vm.state.module_defs.contains_key(name.as_str()) diff --git a/extra_tests/snippets/stdlib_imp.py b/extra_tests/snippets/stdlib_imp.py index 9fd5f8a36..8153790c3 100644 --- a/extra_tests/snippets/stdlib_imp.py +++ b/extra_tests/snippets/stdlib_imp.py @@ -1,7 +1,7 @@ import _imp import time as import_time -from testutils import assert_raises +from testutils import assert_raises, skip_if_unsupported assert _imp.is_builtin("time") == True assert _imp.is_builtin("os") == False @@ -42,3 +42,45 @@ data, ispkg, origname = _imp.find_frozen("__hello__", withdata=True) assert ispkg is False assert origname == "__hello__" assert _imp.get_frozen_object("__hello__", data).co_name == "" + + +def test_lazy_attributes_eager_registry(): + # CPython accepts any first argument and a string name. In an eager-only + # interpreter there are no pending lazy module names to remove. + for modobj in (None, object(), {}, 42): + assert _imp._set_lazy_attributes(modobj, "package.module") is None + assert _imp._set_lazy_attributes(modobj, "\ud800") is None + + for name in (None, 1, b"package.module", [], {}): + with assert_raises(TypeError): + _imp._set_lazy_attributes(None, name) + + with assert_raises(TypeError): + _imp._set_lazy_attributes(modobj=None, name="package.module") + + class ModuleName(str): + hashes = 0 + + def __hash__(self): + self.hashes += 1 + return 42 + + name = ModuleName("package.module") + assert _imp._set_lazy_attributes(None, name) is None + assert name.hashes == 1 + + class UnhashableName(str): + __hash__ = None + + with assert_raises(TypeError): + _imp._set_lazy_attributes(None, UnhashableName("package.module")) + + class HashErrorName(str): + def __hash__(self): + raise RuntimeError("name hash failed") + + with assert_raises(RuntimeError): + _imp._set_lazy_attributes(None, HashErrorName("package.module")) + + +skip_if_unsupported(3, 15, test_lazy_attributes_eager_registry)