Merge pull request #1246 from RustPython/coolreader18/import-submodules

Fix weirdness with importing submodules
This commit is contained in:
Noah
2019-08-15 18:32:45 -05:00
committed by GitHub
9 changed files with 121 additions and 79 deletions

View File

@@ -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)

View File

@@ -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)),

View File

@@ -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):

View File

@@ -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,
)?;
}
}
}

View File

@@ -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)

View File

@@ -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),
});
}

View File

@@ -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)))
}

View File

@@ -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),
})
}

View File

@@ -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,