diff --git a/src/interpreter.rs b/src/interpreter.rs index b4fd319cdae..51667e724f1 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -26,15 +26,18 @@ pub type InitHook = Box; /// ``` /// /// 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)] @@ -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| { @@ -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, @@ -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 { + // 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![] } } }