Remove Interpreter::default() not to trap users init without stdlib

This commit is contained in:
Jeong Yunwon
2022-04-30 03:28:29 +09:00
parent b74a5a6a92
commit 6fd5094c05
18 changed files with 102 additions and 93 deletions

View File

@@ -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(|| {

View File

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

View File

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

View File

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

View File

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

View File

@@ -51,7 +51,7 @@ use rustpython_vm::{
match_class,
scope::Scope,
stdlib::{atexit, sys},
AsObject, InitParameter, Interpreter, PyResult, Settings, VirtualMachine,
AsObject, Interpreter, PyResult, Settings, VirtualMachine,
};
use std::{env, process, str::FromStr};
@@ -83,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| {
@@ -573,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,7 +598,7 @@ fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> {
// Figure out if a -c option was given:
if let Some(command) = matches.value_of("c") {
debug!("Running command {}", command);
vm.run_code_string(scope, &command, "<stdin>".to_owned())?;
vm.run_code_string(scope, command, "<stdin>".to_owned())?;
} else if let Some(module) = matches.value_of("m") {
debug!("Running module {}", module);
vm.run_module(module)?;
@@ -627,9 +626,8 @@ 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
})
}

View File

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

View File

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

View File

@@ -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, "<unittest>").expect("this should pass");

View File

@@ -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<PyObjectRef> {
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 {

View File

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

View File

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

View File

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

View File

@@ -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<F>(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<F>(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();

View File

@@ -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<S>(&mut self, name: S, module: stdlib::StdlibInitFunc)
where
S: Into<Cow<'static, str>>,
@@ -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<I>(&mut self, frozen: I)
where
I: IntoIterator<Item = (String, bytecode::FrozenModule)>,

View File

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

View File

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

View File

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