forked from Rust-related/RustPython
Merge pull request #1246 from RustPython/coolreader18/import-submodules
Fix weirdness with importing submodules
This commit is contained in:
@@ -2,8 +2,7 @@
|
||||
|
||||
import sys
|
||||
if sys.argv[0].endswith("__main__.py"):
|
||||
# FIXME change to `import os.path` as it was for cpython once `import os.path` works
|
||||
import os
|
||||
import os.path
|
||||
# We change sys.argv[0] to make help message more useful
|
||||
# use executable without path, unquoted
|
||||
# (it's just a hint anyway)
|
||||
|
||||
@@ -84,10 +84,7 @@ pub enum Instruction {
|
||||
symbols: Vec<String>,
|
||||
level: usize,
|
||||
},
|
||||
ImportStar {
|
||||
name: Option<String>,
|
||||
level: usize,
|
||||
},
|
||||
ImportStar,
|
||||
ImportFrom {
|
||||
name: String,
|
||||
},
|
||||
@@ -429,7 +426,7 @@ impl Instruction {
|
||||
format!("{:?}", symbols),
|
||||
level
|
||||
),
|
||||
ImportStar { name, level } => w!(ImportStar, format!("{:?}", name), level),
|
||||
ImportStar => w!(ImportStar),
|
||||
ImportFrom { name } => w!(ImportFrom, name),
|
||||
LoadName { name, scope } => w!(LoadName, name, format!("{:?}", scope)),
|
||||
StoreName { name, scope } => w!(StoreName, name, format!("{:?}", scope)),
|
||||
|
||||
@@ -305,11 +305,15 @@ impl<O: OutputStream> Compiler<O> {
|
||||
symbols: vec![],
|
||||
level: 0,
|
||||
});
|
||||
|
||||
if let Some(alias) = &name.alias {
|
||||
for part in name.symbol.split('.').skip(1) {
|
||||
self.emit(Instruction::LoadAttr {
|
||||
name: part.to_owned(),
|
||||
});
|
||||
}
|
||||
self.store_name(alias);
|
||||
} else {
|
||||
self.store_name(&name.symbol);
|
||||
self.store_name(name.symbol.split('.').next().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,10 +326,12 @@ impl<O: OutputStream> Compiler<O> {
|
||||
|
||||
if import_star {
|
||||
// from .... import *
|
||||
self.emit(Instruction::ImportStar {
|
||||
self.emit(Instruction::Import {
|
||||
name: module.clone(),
|
||||
symbols: vec!["*".to_owned()],
|
||||
level: *level,
|
||||
});
|
||||
self.emit(Instruction::ImportStar);
|
||||
} else {
|
||||
// from mod import a, b as c
|
||||
// First, determine the fromlist (for import lib):
|
||||
|
||||
@@ -388,7 +388,10 @@ impl SymbolTableBuilder {
|
||||
self.register_name(alias, SymbolUsage::Assigned)?;
|
||||
} else {
|
||||
// `import module`
|
||||
self.register_name(&name.symbol, SymbolUsage::Assigned)?;
|
||||
self.register_name(
|
||||
name.symbol.split('.').next().unwrap(),
|
||||
SymbolUsage::Assigned,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,10 +198,7 @@ impl Frame {
|
||||
ref symbols,
|
||||
ref level,
|
||||
} => self.import(vm, name, symbols, *level),
|
||||
bytecode::Instruction::ImportStar {
|
||||
ref name,
|
||||
ref level,
|
||||
} => self.import_star(vm, name, *level),
|
||||
bytecode::Instruction::ImportStar => self.import_star(vm),
|
||||
bytecode::Instruction::ImportFrom { ref name } => self.import_from(vm, name),
|
||||
bytecode::Instruction::LoadName {
|
||||
ref name,
|
||||
@@ -740,25 +737,23 @@ impl Frame {
|
||||
// Load attribute, and transform any error into import error.
|
||||
let obj = vm
|
||||
.get_attribute(module, name)
|
||||
.map_err(|_| vm.new_import_error(format!("cannot import name '{}'", name)));
|
||||
self.push_value(obj?);
|
||||
.map_err(|_| vm.new_import_error(format!("cannot import name '{}'", name)))?;
|
||||
self.push_value(obj);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "flame-it", flame("Frame"))]
|
||||
fn import_star(
|
||||
&self,
|
||||
vm: &VirtualMachine,
|
||||
module: &Option<String>,
|
||||
level: usize,
|
||||
) -> FrameResult {
|
||||
let module = module.clone().unwrap_or_default();
|
||||
let module = vm.import(&module, &vm.ctx.new_tuple(vec![]), level)?;
|
||||
fn import_star(&self, vm: &VirtualMachine) -> FrameResult {
|
||||
let module = self.pop_value();
|
||||
|
||||
// Grab all the names from the module and put them in the context
|
||||
if let Some(dict) = &module.dict {
|
||||
for (k, v) in dict {
|
||||
self.scope.store_name(&vm, &objstr::get_value(&k), v);
|
||||
let k = vm.to_str(&k)?;
|
||||
let k = k.as_str();
|
||||
if !k.starts_with('_') {
|
||||
self.scope.store_name(&vm, k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
@@ -18,14 +18,36 @@ impl PyValue for PyModule {
|
||||
}
|
||||
|
||||
impl PyModuleRef {
|
||||
fn init(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm.set_attr(&self.into_object(), "__name__", name)?;
|
||||
Ok(vm.get_none())
|
||||
fn new(cls: PyClassRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult<PyModuleRef> {
|
||||
let zelf = PyModule {
|
||||
name: name.as_str().to_owned(),
|
||||
}
|
||||
.into_ref_with_type(vm, cls)?;
|
||||
vm.set_attr(zelf.as_object(), "__name__", name)?;
|
||||
Ok(zelf)
|
||||
}
|
||||
|
||||
fn getattribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm.generic_getattribute(self.as_object().clone(), name.clone())?
|
||||
.ok_or_else(|| {
|
||||
vm.new_attribute_error(format!(
|
||||
"module '{}' has no attribute '{}'",
|
||||
self.name, name,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn repr(self, vm: &VirtualMachine) -> PyResult {
|
||||
let importlib = vm.import("_frozen_importlib", &vm.ctx.new_tuple(vec![]), 0)?;
|
||||
let module_repr = vm.get_attribute(importlib, "_module_repr")?;
|
||||
vm.invoke(&module_repr, vec![self.into_object()])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
extend_class!(&context, &context.types.module_type, {
|
||||
"__init__" => context.new_rustfunc(PyModuleRef::init),
|
||||
"__new__" => context.new_rustfunc(PyModuleRef::new),
|
||||
"__getattribute__" => context.new_rustfunc(PyModuleRef::getattribute),
|
||||
"__repr__" => context.new_rustfunc(PyModuleRef::repr),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -223,39 +223,8 @@ fn object_dict_setter(
|
||||
))
|
||||
}
|
||||
|
||||
fn object_getattribute(obj: PyObjectRef, name_str: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
let name = &name_str.value;
|
||||
fn object_getattribute(obj: PyObjectRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("object.__getattribute__({:?}, {:?})", obj, name);
|
||||
let cls = obj.class();
|
||||
|
||||
if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
let attr_class = attr.class();
|
||||
if objtype::class_has_attr(&attr_class, "__set__") {
|
||||
if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") {
|
||||
return vm.invoke(&descriptor, vec![attr, obj, cls.into_object()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(obj_attr) = object_getattr(&obj, &name, &vm)? {
|
||||
Ok(obj_attr)
|
||||
} else if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
vm.call_get_descriptor(attr, obj)
|
||||
} else if let Some(ref getter) = objtype::class_get_attr(&cls, "__getattr__") {
|
||||
vm.invoke(getter, vec![obj, name_str.into_object()])
|
||||
} else {
|
||||
Err(vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name)))
|
||||
}
|
||||
}
|
||||
|
||||
fn object_getattr(
|
||||
obj: &PyObjectRef,
|
||||
attr_name: &str,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<PyObjectRef>> {
|
||||
if let Some(ref dict) = obj.dict {
|
||||
dict.get_item_option(attr_name, vm)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
vm.generic_getattribute(obj.clone(), name.clone())?
|
||||
.ok_or_else(|| vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name)))
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::bytecode;
|
||||
use crate::obj::objbytes::{PyBytes, PyBytesRef};
|
||||
use crate::obj::objcode::{PyCode, PyCodeRef};
|
||||
use crate::pyobject::{IntoPyObject, PyObjectRef, PyResult};
|
||||
use crate::pyobject::{PyObjectRef, PyResult};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
fn marshal_dumps(co: PyCodeRef, vm: &VirtualMachine) -> PyResult {
|
||||
PyBytes::new(bincode::serialize(&co.code).unwrap()).into_pyobject(vm)
|
||||
fn marshal_dumps(co: PyCodeRef, _vm: &VirtualMachine) -> PyBytes {
|
||||
PyBytes::new(bincode::serialize(&co.code).unwrap())
|
||||
}
|
||||
|
||||
fn marshal_loads(code_bytes: PyBytesRef, vm: &VirtualMachine) -> PyResult {
|
||||
let code = bincode::deserialize::<bytecode::CodeObject>(&code_bytes).unwrap();
|
||||
let pycode = PyCode { code };
|
||||
pycode.into_pyobject(vm)
|
||||
fn marshal_loads(code_bytes: PyBytesRef, vm: &VirtualMachine) -> PyResult<PyCode> {
|
||||
let code = bincode::deserialize::<bytecode::CodeObject>(&code_bytes)
|
||||
.map_err(|_| vm.new_value_error("Couldn't deserialize python bytecode".to_owned()))?;
|
||||
Ok(PyCode { code })
|
||||
}
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
@@ -19,6 +19,6 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
|
||||
py_module!(vm, "marshal", {
|
||||
"loads" => ctx.new_rustfunc(marshal_loads),
|
||||
"dumps" => ctx.new_rustfunc(marshal_dumps)
|
||||
"dumps" => ctx.new_rustfunc(marshal_dumps),
|
||||
})
|
||||
}
|
||||
|
||||
65
vm/src/vm.rs
65
vm/src/vm.rs
@@ -407,10 +407,23 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn import(&self, module: &str, from_list: &PyObjectRef, level: usize) -> PyResult {
|
||||
let sys_modules = self.get_attribute(self.sys_module.clone(), "modules")?;
|
||||
sys_modules
|
||||
.get_item(module.to_string(), self)
|
||||
.or_else(|_| {
|
||||
// if the import inputs seem weird, e.g a package import or something, rather than just
|
||||
// a straight `import ident`
|
||||
let weird = module.contains('.')
|
||||
|| level != 0
|
||||
|| objbool::boolval(self, from_list.clone()).unwrap_or(true);
|
||||
|
||||
let module = self.new_str(module.to_owned());
|
||||
|
||||
let cached_module = if weird {
|
||||
None
|
||||
} else {
|
||||
let sys_modules = self.get_attribute(self.sys_module.clone(), "modules")?;
|
||||
sys_modules.get_item(module.clone(), self).ok()
|
||||
};
|
||||
match cached_module {
|
||||
Some(module) => Ok(module),
|
||||
None => {
|
||||
let import_func = self
|
||||
.get_attribute(self.builtins.clone(), "__import__")
|
||||
.map_err(|_| self.new_import_error("__import__ not found".to_string()))?;
|
||||
@@ -426,15 +439,16 @@ impl VirtualMachine {
|
||||
self.invoke(
|
||||
&import_func,
|
||||
vec![
|
||||
self.ctx.new_str(module.to_string()),
|
||||
module,
|
||||
globals,
|
||||
locals,
|
||||
from_list.clone(),
|
||||
self.ctx.new_int(level),
|
||||
],
|
||||
)
|
||||
})
|
||||
.map_err(|exc| import::remove_importlib_frames(self, &exc))
|
||||
.map_err(|exc| import::remove_importlib_frames(self, &exc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via
|
||||
@@ -863,6 +877,43 @@ impl VirtualMachine {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generic_getattribute(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
name_str: PyStringRef,
|
||||
) -> PyResult<Option<PyObjectRef>> {
|
||||
let name = name_str.as_str();
|
||||
let cls = obj.class();
|
||||
|
||||
if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
let attr_class = attr.class();
|
||||
if objtype::class_has_attr(&attr_class, "__set__") {
|
||||
if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") {
|
||||
return self
|
||||
.invoke(&descriptor, vec![attr, obj, cls.into_object()])
|
||||
.map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let attr = if let Some(ref dict) = obj.dict {
|
||||
dict.get_item_option(name_str.clone(), self)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(obj_attr) = attr {
|
||||
Ok(Some(obj_attr))
|
||||
} else if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
self.call_get_descriptor(attr, obj).map(Some)
|
||||
} else if let Some(getter) = objtype::class_get_attr(&cls, "__getattr__") {
|
||||
self.invoke(&getter, vec![obj, name_str.into_object()])
|
||||
.map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_callable(&self, obj: &PyObjectRef) -> bool {
|
||||
match_class!(obj,
|
||||
PyFunction => true,
|
||||
|
||||
Reference in New Issue
Block a user