diff --git a/benches/execution.rs b/benches/execution.rs index 9c0bdb024..d286365b8 100644 --- a/benches/execution.rs +++ b/benches/execution.rs @@ -23,7 +23,7 @@ fn bench_cpython_code(b: &mut Bencher, source: &str) { fn bench_rustpy_code(b: &mut Bencher, name: &str, source: &str) { // NOTE: Take long time. - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { // Note: bench_cpython is both compiling and executing the code. // As such we compile the code in the benchmark loop as well. b.iter(|| { diff --git a/benches/microbenchmarks.rs b/benches/microbenchmarks.rs index 0b3996f3c..a0e0ba491 100644 --- a/benches/microbenchmarks.rs +++ b/benches/microbenchmarks.rs @@ -3,7 +3,7 @@ use criterion::{ Criterion, Throughput, }; use rustpython_compiler::Mode; -use rustpython_vm::{common::ascii, InitParameter, Interpreter, PyResult, Settings}; +use rustpython_vm::{common::ascii, Interpreter, PyResult, Settings}; use std::{ ffi, fs, io, path::{Path, PathBuf}, @@ -114,11 +114,10 @@ fn bench_rustpy_code(group: &mut BenchmarkGroup, bench: &MicroBenchmar settings.dont_write_bytecode = true; settings.no_user_site = true; - Interpreter::new_with_init(settings, |vm| { + Interpreter::with_init(settings, |vm| { for (name, init) in rustpython_stdlib::get_module_inits().into_iter() { vm.add_native_module(name, init); } - InitParameter::External }) .enter(|vm| { let setup_code = vm diff --git a/examples/freeze/main.rs b/examples/freeze/main.rs index fec904a94..2395b3301 100644 --- a/examples/freeze/main.rs +++ b/examples/freeze/main.rs @@ -1,7 +1,7 @@ use rustpython_vm as vm; fn main() -> vm::PyResult<()> { - vm::Interpreter::default().enter(run) + vm::Interpreter::without_stdlib(Default::default()).enter(run) } fn run(vm: &vm::VirtualMachine) -> vm::PyResult<()> { diff --git a/examples/hello_embed.rs b/examples/hello_embed.rs index 9948a84b8..26047d6c8 100644 --- a/examples/hello_embed.rs +++ b/examples/hello_embed.rs @@ -1,7 +1,7 @@ use rustpython_vm as vm; fn main() -> vm::PyResult<()> { - vm::Interpreter::default().enter(|vm| { + vm::Interpreter::without_stdlib(Default::default()).enter(|vm| { let scope = vm.new_scope_with_builtins(); let code_obj = vm diff --git a/examples/mini_repl.rs b/examples/mini_repl.rs index 7e4bf2098..19a4113e5 100644 --- a/examples/mini_repl.rs +++ b/examples/mini_repl.rs @@ -29,7 +29,7 @@ fn on(b: bool) { } fn main() -> vm::PyResult<()> { - vm::Interpreter::default().enter(run) + vm::Interpreter::without_stdlib(Default::default()).enter(run) } fn run(vm: &vm::VirtualMachine) -> vm::PyResult<()> { diff --git a/src/lib.rs b/src/lib.rs index aef9f7282..c3f0e1abd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,14 +47,13 @@ mod shell; use clap::{App, AppSettings, Arg, ArgMatches}; use rustpython_vm::{ - builtins::{PyDictRef, PyInt}, - compile, match_class, + builtins::PyInt, + match_class, scope::Scope, stdlib::{atexit, sys}, - AsObject, InitParameter, Interpreter, PyObjectRef, PyResult, Settings, TryFromObject, - VirtualMachine, + AsObject, Interpreter, PyResult, Settings, VirtualMachine, }; -use std::{env, path::Path, process, str::FromStr}; +use std::{env, process, str::FromStr}; pub use rustpython_vm as vm; @@ -84,10 +83,9 @@ where } } - let interp = Interpreter::new_with_init(settings, |vm| { + let interp = Interpreter::with_init(settings, |vm| { add_stdlib(vm); init(vm); - InitParameter::External }); let exitcode = interp.enter(move |vm| { @@ -574,7 +572,7 @@ __import__("io").TextIOWrapper( .downcast() .expect("TextIOWrapper.read() should return str"); eprintln!("running get-pip.py..."); - _run_string(vm, scope, getpip_code.as_str(), "get-pip.py".to_owned()) + vm.run_code_string(scope, getpip_code.as_str(), "get-pip.py".to_owned()) } #[cfg(not(feature = "ssl"))] @@ -599,13 +597,16 @@ fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> { // Figure out if a -c option was given: if let Some(command) = matches.value_of("c") { - run_command(vm, scope, command.to_owned())?; + debug!("Running command {}", command); + vm.run_code_string(scope, command, "".to_owned())?; } else if let Some(module) = matches.value_of("m") { - run_module(vm, module)?; + debug!("Running module {}", module); + vm.run_module(module)?; } else if matches.is_present("install_pip") { install_pip(scope, vm)?; } else if let Some(filename) = matches.value_of("script") { - run_script(vm, scope.clone(), filename)?; + debug!("Running file {}", filename); + vm.run_script(scope.clone(), filename)?; if matches.is_present("inspect") { shell::run_shell(vm, scope)?; } @@ -620,97 +621,13 @@ fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> { Ok(()) } -fn _run_string(vm: &VirtualMachine, scope: Scope, source: &str, source_path: String) -> PyResult { - let code_obj = vm - .compile(source, compile::Mode::Exec, source_path.clone()) - .map_err(|err| vm.new_syntax_error(&err))?; - // trace!("Code object: {:?}", code_obj.borrow()); - scope - .globals - .set_item("__file__", vm.new_pyobj(source_path), vm)?; - vm.run_code_obj(code_obj, scope) -} - -fn run_command(vm: &VirtualMachine, scope: Scope, source: String) -> PyResult<()> { - debug!("Running command {}", source); - _run_string(vm, scope, &source, "".to_owned())?; - Ok(()) -} - -fn run_module(vm: &VirtualMachine, module: &str) -> PyResult<()> { - debug!("Running module {}", module); - let runpy = vm.import("runpy", None, 0)?; - let run_module_as_main = runpy.get_attr("_run_module_as_main", vm)?; - vm.invoke(&run_module_as_main, (module,))?; - Ok(()) -} - -fn get_importer(path: &str, vm: &VirtualMachine) -> PyResult> { - let path_importer_cache = vm.sys_module.get_attr("path_importer_cache", vm)?; - let path_importer_cache = PyDictRef::try_from_object(vm, path_importer_cache)?; - if let Some(importer) = path_importer_cache.get_item_opt(path, vm)? { - return Ok(Some(importer)); - } - let path = vm.ctx.new_str(path); - let path_hooks = vm.sys_module.get_attr("path_hooks", vm)?; - let mut importer = None; - let path_hooks: Vec = path_hooks.try_into_value(vm)?; - for path_hook in path_hooks { - match vm.invoke(&path_hook, (path.clone(),)) { - Ok(imp) => { - importer = Some(imp); - break; - } - Err(e) if e.fast_isinstance(&vm.ctx.exceptions.import_error) => continue, - Err(e) => return Err(e), - } - } - Ok(if let Some(imp) = importer { - let imp = path_importer_cache.get_or_insert(vm, path.into(), || imp.clone())?; - Some(imp) - } else { - None - }) -} - -fn insert_sys_path(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<()> { - let sys_path = vm.sys_module.get_attr("path", vm).unwrap(); - vm.call_method(&sys_path, "insert", (0, obj))?; - Ok(()) -} - -fn run_script(vm: &VirtualMachine, scope: Scope, script_file: &str) -> PyResult<()> { - debug!("Running file {}", script_file); - if get_importer(script_file, vm)?.is_some() { - insert_sys_path(vm, vm.ctx.new_str(script_file).into())?; - let runpy = vm.import("runpy", None, 0)?; - let run_module_as_main = runpy.get_attr("_run_module_as_main", vm)?; - vm.invoke(&run_module_as_main, (vm.ctx.new_str("__main__"), false))?; - return Ok(()); - } - let dir = Path::new(script_file).parent().unwrap().to_str().unwrap(); - insert_sys_path(vm, vm.ctx.new_str(dir).into())?; - - match std::fs::read_to_string(script_file) { - Ok(source) => { - _run_string(vm, scope, &source, script_file.to_owned())?; - } - Err(err) => { - error!("Failed reading file '{}': {}", script_file, err); - process::exit(1); - } - } - Ok(()) -} - #[cfg(test)] mod tests { use super::*; fn interpreter() -> Interpreter { - Interpreter::new_with_init(Settings::default(), |vm| { + Interpreter::with_init(Settings::default(), |vm| { add_stdlib(vm); - InitParameter::External }) } @@ -720,11 +637,11 @@ mod tests { vm.unwrap_pyresult((|| { let scope = setup_main_module(vm)?; // test file run - run_script(vm, scope, "extra_tests/snippets/dir_main/__main__.py")?; + vm.run_script(scope, "extra_tests/snippets/dir_main/__main__.py")?; let scope = setup_main_module(vm)?; // test module run - run_script(vm, scope, "extra_tests/snippets/dir_main")?; + vm.run_script(scope, "extra_tests/snippets/dir_main")?; Ok(()) })()); diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 3eac4ea26..4d1411248 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1559,7 +1559,7 @@ mod tests { #[test] fn str_maketrans_and_translate() { - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { let table = vm.ctx.new_dict(); table .set_item("a", vm.ctx.new_str("🎅").into(), &vm) diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index fd457a28f..c94d5cbf2 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -872,7 +872,7 @@ mod tests { #[test] fn test_insert() { - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { let dict = Dict::default(); assert_eq!(0, dict.len()); @@ -921,7 +921,7 @@ mod tests { } fn check_hash_equivalence(text: &str) { - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { let value1 = text; let value2 = vm.new_pyobj(value1.to_owned()); diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 35751e02a..39637323d 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -20,7 +20,7 @@ mod tests { #[test] fn test_print_42() { - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { let source = String::from("print('Hello world')"); let vars = vm.new_scope_with_builtins(); let result = eval(&vm, &source, vars, "").expect("this should pass"); diff --git a/vm/src/import.rs b/vm/src/import.rs index 932acfd6e..ad68e2a27 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -7,16 +7,23 @@ use crate::{ builtins::{code, code::CodeObject, list, traceback::PyTraceback, PyBaseExceptionRef}, scope::Scope, version::get_git_revision, - vm::{InitParameter, VirtualMachine}, + vm::{thread, VirtualMachine}, AsObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; use rand::Rng; pub(crate) fn init_importlib( vm: &mut VirtualMachine, - initialize_parameter: InitParameter, + allow_external_library: bool, ) -> PyResult<()> { - use crate::vm::thread::enter_vm; + let importlib = init_importlib_base(vm)?; + if allow_external_library && cfg!(feature = "rustpython-compiler") { + init_importlib_package(vm, importlib)?; + } + Ok(()) +} + +pub(crate) fn init_importlib_base(vm: &mut VirtualMachine) -> PyResult { flame_guard!("init importlib"); // importlib_bootstrap needs these and it inlines checks to sys.modules before calling into @@ -26,7 +33,7 @@ pub(crate) fn init_importlib( import_builtin(vm, "_warnings")?; import_builtin(vm, "_weakref")?; - let importlib = enter_vm(vm, || { + let importlib = thread::enter_vm(vm, || { let importlib = import_frozen(vm, "_frozen_importlib")?; let impmod = import_builtin(vm, "_imp")?; let install = importlib.get_attr("_install", vm)?; @@ -34,45 +41,48 @@ pub(crate) fn init_importlib( Ok(importlib) })?; vm.import_func = importlib.get_attr("__import__", vm)?; + Ok(importlib) +} - if initialize_parameter == InitParameter::External && cfg!(feature = "rustpython-compiler") { - enter_vm(vm, || { - flame_guard!("install_external"); +pub(crate) fn init_importlib_package( + vm: &mut VirtualMachine, + importlib: PyObjectRef, +) -> PyResult<()> { + thread::enter_vm(vm, || { + flame_guard!("install_external"); - // same deal as imports above - #[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] - import_builtin(vm, crate::stdlib::os::MODULE_NAME)?; - #[cfg(windows)] - import_builtin(vm, "winreg")?; - import_builtin(vm, "_io")?; - import_builtin(vm, "marshal")?; + // same deal as imports above + #[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] + import_builtin(vm, crate::stdlib::os::MODULE_NAME)?; + #[cfg(windows)] + import_builtin(vm, "winreg")?; + import_builtin(vm, "_io")?; + import_builtin(vm, "marshal")?; - let install_external = importlib.get_attr("_install_external_importers", vm)?; - vm.invoke(&install_external, ())?; - // Set pyc magic number to commit hash. Should be changed when bytecode will be more stable. - let importlib_external = vm.import("_frozen_importlib_external", None, 0)?; - let mut magic = get_git_revision().into_bytes(); - magic.truncate(4); - if magic.len() != 4 { - magic = rand::thread_rng().gen::<[u8; 4]>().to_vec(); - } - let magic: PyObjectRef = vm.ctx.new_bytes(magic).into(); - importlib_external.set_attr("MAGIC_NUMBER", magic, vm)?; - let zipimport_res = (|| -> PyResult<()> { - let zipimport = vm.import("zipimport", None, 0)?; - let zipimporter = zipimport.get_attr("zipimporter", vm)?; - let path_hooks = vm.sys_module.get_attr("path_hooks", vm)?; - let path_hooks = list::PyListRef::try_from_object(vm, path_hooks)?; - path_hooks.insert(0, zipimporter); - Ok(()) - })(); - if zipimport_res.is_err() { - warn!("couldn't init zipimport") - } + let install_external = importlib.get_attr("_install_external_importers", vm)?; + vm.invoke(&install_external, ())?; + // Set pyc magic number to commit hash. Should be changed when bytecode will be more stable. + let importlib_external = vm.import("_frozen_importlib_external", None, 0)?; + let mut magic = get_git_revision().into_bytes(); + magic.truncate(4); + if magic.len() != 4 { + magic = rand::thread_rng().gen::<[u8; 4]>().to_vec(); + } + let magic: PyObjectRef = vm.ctx.new_bytes(magic).into(); + importlib_external.set_attr("MAGIC_NUMBER", magic, vm)?; + let zipimport_res = (|| -> PyResult<()> { + let zipimport = vm.import("zipimport", None, 0)?; + let zipimporter = zipimport.get_attr("zipimporter", vm)?; + let path_hooks = vm.sys_module.get_attr("path_hooks", vm)?; + let path_hooks = list::PyListRef::try_from_object(vm, path_hooks)?; + path_hooks.insert(0, zipimporter); Ok(()) - })? - } - Ok(()) + })(); + if zipimport_res.is_err() { + warn!("couldn't init zipimport") + } + Ok(()) + }) } pub fn import_frozen(vm: &VirtualMachine, module_name: &str) -> PyResult { diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 944ba7cad..7e9b167e8 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -81,7 +81,7 @@ pub use self::convert::{TryFromBorrowedObject, TryFromObject}; pub use self::object::{ AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult, PyWeakRef, }; -pub use self::vm::{Context, InitParameter, Interpreter, Settings, VirtualMachine}; +pub use self::vm::{Context, Interpreter, Settings, VirtualMachine}; pub use rustpython_bytecode as bytecode; pub use rustpython_common as common; diff --git a/vm/src/macros.rs b/vm/src/macros.rs index a12272c09..eff891749 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -71,7 +71,7 @@ macro_rules! py_namespace { /// use rustpython_vm::builtins::{PyFloat, PyInt}; /// use rustpython_vm::{PyPayload}; /// -/// # rustpython_vm::Interpreter::default().enter(|vm| { +/// # rustpython_vm::Interpreter::without_stdlib(Default::default()).enter(|vm| { /// let obj = PyInt::from(0).into_pyobject(vm); /// assert_eq!( /// "int", @@ -95,7 +95,7 @@ macro_rules! py_namespace { /// use rustpython_vm::builtins::{PyFloat, PyInt}; /// use rustpython_vm::{ PyPayload}; /// -/// # rustpython_vm::Interpreter::default().enter(|vm| { +/// # rustpython_vm::Interpreter::without_stdlib(Default::default()).enter(|vm| { /// let obj = PyInt::from(0).into_pyobject(vm); /// /// let int_value = match_class!(match obj { diff --git a/vm/src/prelude.rs b/vm/src/prelude.rs index f562d0656..415e7da79 100644 --- a/vm/src/prelude.rs +++ b/vm/src/prelude.rs @@ -2,5 +2,5 @@ pub use crate::{ object::{ AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult, PyWeakRef, }, - vm::{Context, InitParameter, Interpreter, Settings, VirtualMachine}, + vm::{Context, Interpreter, Settings, VirtualMachine}, }; diff --git a/vm/src/vm/interpreter.rs b/vm/src/vm/interpreter.rs index 313c34cef..45cb23fbf 100644 --- a/vm/src/vm/interpreter.rs +++ b/vm/src/vm/interpreter.rs @@ -1,4 +1,4 @@ -use super::{setting::Settings, thread, InitParameter, VirtualMachine}; +use super::{setting::Settings, thread, VirtualMachine}; /// The general interface for the VM /// @@ -7,7 +7,7 @@ use super::{setting::Settings, thread, InitParameter, VirtualMachine}; /// ``` /// use rustpython_vm::Interpreter; /// use rustpython_vm::compile::Mode; -/// Interpreter::default().enter(|vm| { +/// Interpreter::without_stdlib(Default::default()).enter(|vm| { /// let scope = vm.new_scope_with_builtins(); /// let code_obj = vm.compile(r#"print("Hello World!")"#, /// Mode::Exec, @@ -21,17 +21,28 @@ pub struct Interpreter { } impl Interpreter { - pub fn new(settings: Settings, init: InitParameter) -> Self { - Self::new_with_init(settings, |_| init) + /// To create with stdlib, use `with_init` + pub fn without_stdlib(settings: Settings) -> Self { + Self::with_init(settings, |_| {}) } - pub fn new_with_init(settings: Settings, init: F) -> Self + /// Create with initialize function taking mutable vm reference. + /// ``` + /// use rustpython_vm::Interpreter; + /// Interpreter::with_init(Default::default(), |vm| { + /// // put this line to add stdlib to the vm + /// // vm.add_native_modules(rustpython_stdlib::get_module_inits()); + /// }).enter(|vm| { + /// vm.run_code_string(vm.new_scope_with_builtins(), "print(1)", "<...>".to_owned()); + /// }); + /// ``` + pub fn with_init(settings: Settings, init: F) -> Self where - F: FnOnce(&mut VirtualMachine) -> InitParameter, + F: FnOnce(&mut VirtualMachine), { let mut vm = VirtualMachine::new(settings); - let init = init(&mut vm); - vm.initialize(init); + init(&mut vm); + vm.initialize(); Self { vm } } @@ -54,12 +65,6 @@ impl Interpreter { // pub fn shutdown(self) {} } -impl Default for Interpreter { - fn default() -> Self { - Self::new(Settings::default(), InitParameter::External) - } -} - #[cfg(test)] mod tests { use super::*; @@ -71,7 +76,7 @@ mod tests { #[test] fn test_add_py_integers() { - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { let a: PyObjectRef = vm.ctx.new_int(33_i32).into(); let b: PyObjectRef = vm.ctx.new_int(12_i32).into(); let res = vm._add(&a, &b).unwrap(); @@ -82,7 +87,7 @@ mod tests { #[test] fn test_multiply_str() { - Interpreter::default().enter(|vm| { + Interpreter::without_stdlib(Default::default()).enter(|vm| { let a = vm.new_pyobj(crate::common::ascii!("Hello ")); let b = vm.new_pyobj(4_i32); let res = vm._mul(&a, &b).unwrap(); diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index 0c0bb67db..1ff7d74ca 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -24,7 +24,7 @@ use crate::{ bytecode, codecs::CodecsRegistry, common::{ascii, hash::HashSecret, lock::PyMutex, rc::PyRc}, - convert::ToPyObject, + convert::{ToPyObject, TryFromObject}, frame::{ExecutionResult, Frame, FrameRef}, frozen, function::{ArgMapping, FuncArgs}, @@ -88,12 +88,6 @@ pub struct PyGlobalState { pub codec_registry: CodecsRegistry, } -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum InitParameter { - Internal, - External, -} - impl VirtualMachine { /// Create a new `VirtualMachine` structure. fn new(settings: Settings) -> VirtualMachine { @@ -177,7 +171,7 @@ impl VirtualMachine { vm } - fn initialize(&mut self, initialize_parameter: InitParameter) { + fn initialize(&mut self) { flame_guard!("init VirtualMachine"); if self.initialized { @@ -190,8 +184,7 @@ impl VirtualMachine { let mut inner_init = || -> PyResult<()> { #[cfg(not(target_arch = "wasm32"))] import::import_builtin(self, "_signal")?; - - import::init_importlib(self, initialize_parameter)?; + import::init_importlib(self, self.state.settings.allow_external_library)?; // set up the encodings search function self.import("encodings", None, 0).map_err(|import_err| { @@ -249,7 +242,7 @@ impl VirtualMachine { .expect("there should not be multiple threads while a user has a mut ref to a vm") } - /// Can only be used in the initialization closure passed to [`Interpreter::new_with_init`] + /// Can only be used in the initialization closure passed to [`Interpreter::with_init`] pub fn add_native_module(&mut self, name: S, module: stdlib::StdlibInitFunc) where S: Into>, @@ -264,7 +257,7 @@ impl VirtualMachine { self.state_mut().module_inits.extend(iter); } - /// Can only be used in the initialization closure passed to [`Interpreter::new_with_init`] + /// Can only be used in the initialization closure passed to [`Interpreter::with_init`] pub fn add_frozen(&mut self, frozen: I) where I: IntoIterator, @@ -681,4 +674,84 @@ impl VirtualMachine { let val = attr_value.into(); module.generic_setattr(attr_name.into_pystr_ref(self), Some(val), self) } + + pub fn insert_sys_path(&self, obj: PyObjectRef) -> PyResult<()> { + let sys_path = self.sys_module.get_attr("path", self).unwrap(); + self.call_method(&sys_path, "insert", (0, obj))?; + Ok(()) + } + + pub fn run_script(&self, scope: Scope, path: &str) -> PyResult<()> { + if get_importer(path, self)?.is_some() { + self.insert_sys_path(self.new_pyobj(path))?; + let runpy = self.import("runpy", None, 0)?; + let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?; + self.invoke(&run_module_as_main, (self.ctx.new_str("__main__"), false))?; + return Ok(()); + } + + let dir = std::path::Path::new(path) + .parent() + .unwrap() + .to_str() + .unwrap(); + self.insert_sys_path(self.new_pyobj(dir))?; + + match std::fs::read_to_string(path) { + Ok(source) => { + self.run_code_string(scope, &source, path.to_owned())?; + } + Err(err) => { + error!("Failed reading file '{}': {}", path, err); + std::process::exit(1); + } + } + Ok(()) + } + + pub fn run_code_string(&self, scope: Scope, source: &str, source_path: String) -> PyResult { + let code_obj = self + .compile(source, crate::compile::Mode::Exec, source_path.clone()) + .map_err(|err| self.new_syntax_error(&err))?; + // trace!("Code object: {:?}", code_obj.borrow()); + scope + .globals + .set_item("__file__", self.new_pyobj(source_path), self)?; + self.run_code_obj(code_obj, scope) + } + + pub fn run_module(&self, module: &str) -> PyResult<()> { + let runpy = self.import("runpy", None, 0)?; + let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?; + self.invoke(&run_module_as_main, (module,))?; + Ok(()) + } +} + +fn get_importer(path: &str, vm: &VirtualMachine) -> PyResult> { + let path_importer_cache = vm.sys_module.get_attr("path_importer_cache", vm)?; + let path_importer_cache = PyDictRef::try_from_object(vm, path_importer_cache)?; + if let Some(importer) = path_importer_cache.get_item_opt(path, vm)? { + return Ok(Some(importer)); + } + let path = vm.ctx.new_str(path); + let path_hooks = vm.sys_module.get_attr("path_hooks", vm)?; + let mut importer = None; + let path_hooks: Vec = path_hooks.try_into_value(vm)?; + for path_hook in path_hooks { + match vm.invoke(&path_hook, (path.clone(),)) { + Ok(imp) => { + importer = Some(imp); + break; + } + Err(e) if e.fast_isinstance(&vm.ctx.exceptions.import_error) => continue, + Err(e) => return Err(e), + } + } + Ok(if let Some(imp) = importer { + let imp = path_importer_cache.get_or_insert(vm, path.into(), || imp.clone())?; + Some(imp) + } else { + None + }) } diff --git a/vm/src/vm/setting.rs b/vm/src/vm/setting.rs index 9ec22e86f..2ae33fc5a 100644 --- a/vm/src/vm/setting.rs +++ b/vm/src/vm/setting.rs @@ -61,6 +61,9 @@ pub struct Settings { /// -u, PYTHONUNBUFFERED=x // TODO: use this; can TextIOWrapper even work with a non-buffered? pub stdio_unbuffered: bool, + + /// false for wasm. Not a command-line option + pub allow_external_library: bool, } /// Sensible default settings. @@ -90,6 +93,7 @@ impl Default for Settings { argv: vec![], hash_seed: None, stdio_unbuffered: false, + allow_external_library: true, } } } diff --git a/vm/src/vm/thread.rs b/vm/src/vm/thread.rs index 1e6987c40..54c72e697 100644 --- a/vm/src/vm/thread.rs +++ b/vm/src/vm/thread.rs @@ -107,7 +107,7 @@ impl VirtualMachine { /// # Usage /// /// ``` - /// # rustpython_vm::Interpreter::default().enter(|vm| { + /// # rustpython_vm::Interpreter::without_stdlib(Default::default()).enter(|vm| { /// use std::thread::Builder; /// let handle = Builder::new() /// .name("my thread :)".into()) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index a7e972f01..51ea69e2d 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -8,7 +8,7 @@ use rustpython_vm::{ builtins::PyWeak, compile::{self, Mode}, scope::Scope, - InitParameter, Interpreter, PyObjectRef, PyPayload, PyRef, PyResult, Settings, VirtualMachine, + Interpreter, PyObjectRef, PyPayload, PyRef, PyResult, Settings, VirtualMachine, }; use std::{ cell::RefCell, @@ -41,7 +41,9 @@ fn init_window_module(vm: &VirtualMachine) -> PyObjectRef { impl StoredVirtualMachine { fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine { let mut scope = None; - let interp = Interpreter::new_with_init(Settings::default(), |vm| { + let mut settings = Settings::default(); + settings.allow_external_library = false; + let interp = Interpreter::with_init(settings, |vm| { vm.wasm_id = Some(id); js_module::setup_js_module(vm); @@ -57,8 +59,6 @@ impl StoredVirtualMachine { }); scope = Some(vm.new_scope_with_builtins()); - - InitParameter::Internal }); StoredVirtualMachine {