Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 77 additions & 43 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@ pub type InitHook = Box<dyn FnOnce(&mut VirtualMachine)>;
/// ```
///
/// To add native modules:
/// ```compile_fail
/// ```
/// use rustpython_vm::pymodule;
///
/// #[pymodule]
/// mod your_module {}
///
/// let interpreter = rustpython::InterpreterConfig::new()
/// .init_stdlib()
/// .init_hook(Box::new(|vm| {
/// vm.add_native_module(
/// "your_module_name".to_owned(),
/// Box::new(your_module::make_module),
/// );
/// }))
/// .add_native_module(
/// "your_module_name".to_owned(),
/// your_module::make_module,
/// )
/// .interpreter();
/// ```
#[derive(Default)]
Expand All @@ -44,9 +47,12 @@ pub struct InterpreterConfig {
}

impl InterpreterConfig {
/// Creates a new interpreter configuration with default settings.
pub fn new() -> Self {
Self::default()
}

/// Builds the interpreter with the current configuration.
pub fn interpreter(self) -> Interpreter {
let settings = self.settings.unwrap_or_default();
Interpreter::with_init(settings, |vm| {
Expand All @@ -56,14 +62,23 @@ impl InterpreterConfig {
})
}

/// Sets custom settings for the interpreter.
///
/// If called multiple times, only the last settings will be used.
pub fn settings(mut self, settings: Settings) -> Self {
self.settings = Some(settings);
self
}

/// Adds a custom initialization hook.
///
/// Hooks are executed in the order they are added during interpreter creation.
pub fn init_hook(mut self, hook: InitHook) -> Self {
self.init_hooks.push(hook);
self
}

/// Adds a native module to the interpreter.
pub fn add_native_module(
self,
name: String,
Expand All @@ -73,56 +88,75 @@ impl InterpreterConfig {
vm.add_native_module(name, Box::new(make_module))
}))
}

/// Initializes the Python standard library.
///
/// Requires the `stdlib` feature to be enabled.
#[cfg(feature = "stdlib")]
pub fn init_stdlib(self) -> Self {
self.init_hook(Box::new(init_stdlib))
}
}

/// Initializes all standard library modules for the given VM.
#[cfg(feature = "stdlib")]
pub fn init_stdlib(vm: &mut VirtualMachine) {
vm.add_native_modules(rustpython_stdlib::get_module_inits());

// if we're on freeze-stdlib, the core stdlib modules will be included anyway
#[cfg(feature = "freeze-stdlib")]
{
vm.add_frozen(rustpython_pylib::FROZEN_STDLIB);

// FIXME: Remove this hack once sys._stdlib_dir is properly implemented or _frozen_importlib doesn't depend on it anymore.
assert!(vm.sys_module.get_attr("_stdlib_dir", vm).is_err());
vm.sys_module
.set_attr(
"_stdlib_dir",
vm.new_pyobj(rustpython_pylib::LIB_PATH.to_owned()),
vm,
)
.unwrap();
}
setup_frozen_stdlib(vm);

#[cfg(not(feature = "freeze-stdlib"))]
{
use rustpython_vm::common::rc::PyRc;

let state = PyRc::get_mut(&mut vm.state).unwrap();

// Collect additional paths to add
let mut additional_paths = Vec::new();

// BUILDTIME_RUSTPYTHONPATH should be set when distributing
if let Some(paths) = option_env!("BUILDTIME_RUSTPYTHONPATH") {
additional_paths.extend(
crate::settings::split_paths(paths)
.map(|path| path.into_os_string().into_string().unwrap()),
)
} else {
#[cfg(feature = "rustpython-pylib")]
additional_paths.push(rustpython_pylib::LIB_PATH.to_owned())
}
setup_dynamic_stdlib(vm);
}

// Add to both path_list (for compatibility) and module_search_paths (for sys.path)
// Insert at the beginning so stdlib comes before user paths
for path in additional_paths.into_iter().rev() {
state.config.paths.module_search_paths.insert(0, path);
/// Setup frozen standard library (compiled into the binary)
#[cfg(all(feature = "stdlib", feature = "freeze-stdlib"))]
fn setup_frozen_stdlib(vm: &mut VirtualMachine) {
vm.add_frozen(rustpython_pylib::FROZEN_STDLIB);

// FIXME: Remove this hack once sys._stdlib_dir is properly implemented
// or _frozen_importlib doesn't depend on it anymore.
assert!(vm.sys_module.get_attr("_stdlib_dir", vm).is_err());
vm.sys_module
.set_attr(
"_stdlib_dir",
vm.new_pyobj(rustpython_pylib::LIB_PATH.to_owned()),
vm,
)
.unwrap();
}

/// Setup dynamic standard library loading from filesystem
#[cfg(all(feature = "stdlib", not(feature = "freeze-stdlib")))]
fn setup_dynamic_stdlib(vm: &mut VirtualMachine) {
use rustpython_vm::common::rc::PyRc;

let state = PyRc::get_mut(&mut vm.state).unwrap();
let paths = collect_stdlib_paths();

// Insert at the beginning so stdlib comes before user paths
for path in paths.into_iter().rev() {
state.config.paths.module_search_paths.insert(0, path);
}
}

/// Collect standard library paths from build-time configuration
#[cfg(all(feature = "stdlib", not(feature = "freeze-stdlib")))]
fn collect_stdlib_paths() -> Vec<String> {
// BUILDTIME_RUSTPYTHONPATH should be set when distributing
if let Some(paths) = option_env!("BUILDTIME_RUSTPYTHONPATH") {
crate::settings::split_paths(paths)
.map(|path| path.into_os_string().into_string().unwrap())
.collect()
} else {
#[cfg(feature = "rustpython-pylib")]
{
vec![rustpython_pylib::LIB_PATH.to_owned()]
}
#[cfg(not(feature = "rustpython-pylib"))]
{
vec![]
}
}
}
Loading