Skip to content

Commit 048a954

Browse files
Merge pull request #652 from palaviv/__import__
Add __import__
2 parents 336aa53 + 6a3e82e commit 048a954

File tree

7 files changed

+96
-68
lines changed

7 files changed

+96
-68
lines changed

tests/snippets/import.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,28 @@
2222
except ImportError:
2323
pass
2424

25+
26+
test = __import__("import_target")
27+
assert test.X == import_target.X
28+
29+
import builtins
30+
class OverrideImportContext():
31+
32+
def __enter__(self):
33+
self.original_import = builtins.__import__
34+
35+
def __exit__(self, exc_type, exc_val, exc_tb):
36+
builtins.__import__ = self.original_import
37+
38+
with OverrideImportContext():
39+
def fake_import(name, globals=None, locals=None, fromlist=(), level=0):
40+
return len(name)
41+
42+
builtins.__import__ = fake_import
43+
import test
44+
assert test == 4
45+
46+
2547
# TODO: Once we can determine current directory, use that to construct this
2648
# path:
2749
#import sys

vm/src/builtins.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
// use std::ops::Deref;
66
use std::char;
77
use std::io::{self, Write};
8+
use std::path::PathBuf;
89

910
use crate::compile;
11+
use crate::import::import_module;
1012
use crate::obj::objbool;
1113
use crate::obj::objdict;
1214
use crate::obj::objint;
@@ -713,8 +715,27 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
713715
Ok(sum)
714716
}
715717

718+
// Should be renamed to builtin___import__?
719+
fn builtin_import(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
720+
arg_check!(
721+
vm,
722+
args,
723+
required = [(name, Some(vm.ctx.str_type()))],
724+
optional = [
725+
(_globals, Some(vm.ctx.dict_type())),
726+
(_locals, Some(vm.ctx.dict_type()))
727+
]
728+
);
729+
let current_path = {
730+
let mut source_pathbuf = PathBuf::from(&vm.current_frame().code.source_path);
731+
source_pathbuf.pop();
732+
source_pathbuf
733+
};
734+
735+
import_module(vm, current_path, &objstr::get_value(name))
736+
}
737+
716738
// builtin_vars
717-
// builtin___import__
718739

719740
pub fn make_module(ctx: &PyContext) -> PyObjectRef {
720741
let py_mod = py_module!(ctx, "__builtins__", {
@@ -783,6 +804,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
783804
"tuple" => ctx.tuple_type(),
784805
"type" => ctx.type_type(),
785806
"zip" => ctx.zip_type(),
807+
"__import__" => ctx.new_rustfunc(builtin_import),
786808

787809
// Constants
788810
"NotImplemented" => ctx.not_implemented.clone(),

vm/src/frame.rs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::cell::RefCell;
22
use std::fmt;
3-
use std::path::PathBuf;
43
use std::rc::Rc;
54

65
use num_bigint::BigInt;
@@ -9,7 +8,6 @@ use rustpython_parser::ast;
98

109
use crate::builtins;
1110
use crate::bytecode;
12-
use crate::import::{import, import_module};
1311
use crate::obj::objbool;
1412
use crate::obj::objbuiltinfunc::PyBuiltinFunction;
1513
use crate::obj::objcode;
@@ -22,8 +20,8 @@ use crate::obj::objslice::PySlice;
2220
use crate::obj::objstr;
2321
use crate::obj::objtype;
2422
use crate::pyobject::{
25-
DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue,
26-
TryFromObject, TypeProtocol,
23+
AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef,
24+
PyResult, PyValue, TryFromObject, TypeProtocol,
2725
};
2826
use crate::vm::VirtualMachine;
2927

@@ -806,30 +804,31 @@ impl Frame {
806804
module: &str,
807805
symbol: &Option<String>,
808806
) -> FrameResult {
809-
let current_path = {
810-
let mut source_pathbuf = PathBuf::from(&self.code.source_path);
811-
source_pathbuf.pop();
812-
source_pathbuf
807+
let module = vm.import(module)?;
808+
809+
// If we're importing a symbol, look it up and use it, otherwise construct a module and return
810+
// that
811+
let obj = match symbol {
812+
Some(symbol) => module.get_attr(symbol).map_or_else(
813+
|| {
814+
let import_error = vm.context().exceptions.import_error.clone();
815+
Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol)))
816+
},
817+
Ok,
818+
),
819+
None => Ok(module),
813820
};
814821

815-
let obj = import(vm, current_path, module, symbol)?;
816-
817822
// Push module on stack:
818-
self.push_value(obj);
823+
self.push_value(obj?);
819824
Ok(None)
820825
}
821826

822827
fn import_star(&self, vm: &mut VirtualMachine, module: &str) -> FrameResult {
823-
let current_path = {
824-
let mut source_pathbuf = PathBuf::from(&self.code.source_path);
825-
source_pathbuf.pop();
826-
source_pathbuf
827-
};
828+
let module = vm.import(module)?;
828829

829830
// Grab all the names from the module and put them in the context
830-
let obj = import_module(vm, current_path, module)?;
831-
832-
for (k, v) in obj.get_key_value_pairs().iter() {
831+
for (k, v) in module.get_key_value_pairs().iter() {
833832
self.scope.store_name(&vm, &objstr::get_value(k), v.clone());
834833
}
835834
Ok(None)

vm/src/import.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,6 @@ pub fn import_module(
6363
Ok(module)
6464
}
6565

66-
pub fn import(
67-
vm: &mut VirtualMachine,
68-
current_path: PathBuf,
69-
module_name: &str,
70-
symbol: &Option<String>,
71-
) -> PyResult {
72-
let module = import_module(vm, current_path, module_name)?;
73-
// If we're importing a symbol, look it up and use it, otherwise construct a module and return
74-
// that
75-
if let Some(symbol) = symbol {
76-
module.get_attr(symbol).map_or_else(
77-
|| {
78-
let import_error = vm.context().exceptions.import_error.clone();
79-
Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol)))
80-
},
81-
Ok,
82-
)
83-
} else {
84-
Ok(module)
85-
}
86-
}
87-
8866
fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result<PathBuf, String> {
8967
let sys_path = vm.sys_module.get_attr("path").unwrap();
9068
let mut paths: Vec<PathBuf> = objsequence::get_elements(&sys_path)

vm/src/vm.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ use std::sync::{Mutex, MutexGuard};
1313

1414
use crate::builtins;
1515
use crate::bytecode;
16-
use crate::frame::ExecutionResult;
17-
use crate::frame::Scope;
16+
use crate::frame::{ExecutionResult, Frame, Scope};
1817
use crate::obj::objbool;
1918
use crate::obj::objbuiltinfunc::PyBuiltinFunction;
2019
use crate::obj::objcode;
@@ -94,9 +93,13 @@ impl VirtualMachine {
9493
result
9594
}
9695

97-
pub fn current_scope(&self) -> &Scope {
96+
pub fn current_frame(&self) -> &Frame {
9897
let current_frame = &self.frames[self.frames.len() - 1];
99-
let frame = objframe::get_value(current_frame);
98+
objframe::get_value(current_frame)
99+
}
100+
101+
pub fn current_scope(&self) -> &Scope {
102+
let frame = self.current_frame();
100103
&frame.scope
101104
}
102105

@@ -231,6 +234,17 @@ impl VirtualMachine {
231234
self.call_method(obj, "__repr__", vec![])
232235
}
233236

237+
pub fn import(&mut self, module: &str) -> PyResult {
238+
let builtins_import = self.builtins.get_item("__import__");
239+
match builtins_import {
240+
Some(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]),
241+
None => Err(self.new_exception(
242+
self.ctx.exceptions.import_error.clone(),
243+
"__import__ not found".to_string(),
244+
)),
245+
}
246+
}
247+
234248
/// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via
235249
/// the __instancecheck__ magic method.
236250
pub fn isinstance(&mut self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult<bool> {

wasm/lib/src/browser_module.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ use js_sys::Promise;
44
use num_traits::cast::ToPrimitive;
55
use rustpython_vm::obj::{objint, objstr};
66
use rustpython_vm::pyobject::{
7-
PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol,
7+
AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectRef, PyResult, PyValue,
8+
TypeProtocol,
89
};
9-
use rustpython_vm::{import::import, VirtualMachine};
10+
use rustpython_vm::{import::import_module, VirtualMachine};
1011
use std::path::PathBuf;
1112
use wasm_bindgen::{prelude::*, JsCast};
1213
use wasm_bindgen_futures::{future_to_promise, JsFuture};
@@ -177,12 +178,10 @@ pub fn get_promise_value(obj: &PyObjectRef) -> Promise {
177178
}
178179

179180
pub fn import_promise_type(vm: &mut VirtualMachine) -> PyResult {
180-
import(
181-
vm,
182-
PathBuf::default(),
183-
BROWSER_NAME,
184-
&Some("Promise".into()),
185-
)
181+
match import_module(vm, PathBuf::default(), BROWSER_NAME)?.get_attr("Promise".into()) {
182+
Some(promise) => Ok(promise),
183+
None => Err(vm.new_not_implemented_error("No Promise".to_string())),
184+
}
186185
}
187186

188187
fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

wasm/lib/src/convert.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,10 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue {
128128
}
129129
arr.into()
130130
} else {
131-
let dumps = rustpython_vm::import::import(
132-
vm,
133-
std::path::PathBuf::default(),
134-
"json",
135-
&Some("dumps".into()),
136-
)
137-
.expect("Couldn't get json.dumps function");
131+
let dumps = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json")
132+
.expect("Couldn't get json module")
133+
.get_attr("dumps".into())
134+
.expect("Couldn't get json dumps");
138135
match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) {
139136
Ok(value) => {
140137
let json = vm.to_pystr(&value).unwrap();
@@ -231,13 +228,10 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef {
231228
// Because `JSON.stringify(undefined)` returns undefined
232229
vm.get_none()
233230
} else {
234-
let loads = rustpython_vm::import::import(
235-
vm,
236-
std::path::PathBuf::default(),
237-
"json",
238-
&Some("loads".into()),
239-
)
240-
.expect("json.loads function to be available");
231+
let loads = rustpython_vm::import::import_module(vm, std::path::PathBuf::default(), "json")
232+
.expect("Couldn't get json module")
233+
.get_attr("loads".into())
234+
.expect("Couldn't get json dumps");
241235

242236
let json = match js_sys::JSON::stringify(&js_val) {
243237
Ok(json) => String::from(json),

0 commit comments

Comments
 (0)