rustpython_vm/vm/
interpreter.rs

1use super::{setting::Settings, thread, Context, VirtualMachine};
2use crate::{stdlib::atexit, vm::PyBaseExceptionRef, PyResult};
3use std::sync::atomic::Ordering;
4
5/// The general interface for the VM
6///
7/// # Examples
8/// Runs a simple embedded hello world program.
9/// ```
10/// use rustpython_vm::Interpreter;
11/// use rustpython_vm::compiler::Mode;
12/// Interpreter::without_stdlib(Default::default()).enter(|vm| {
13///     let scope = vm.new_scope_with_builtins();
14///     let source = r#"print("Hello World!")"#;
15///     let code_obj = vm.compile(
16///             source,
17///             Mode::Exec,
18///             "<embedded>".to_owned(),
19///     ).map_err(|err| vm.new_syntax_error(&err, Some(source))).unwrap();
20///     vm.run_code_obj(code_obj, scope).unwrap();
21/// });
22/// ```
23pub struct Interpreter {
24    vm: VirtualMachine,
25}
26
27impl Interpreter {
28    /// This is a bare unit to build up an interpreter without the standard library.
29    /// To create an interpreter with the standard library with the `rustpython` crate, use `rustpython::InterpreterConfig`.
30    /// To create an interpreter without the `rustpython` crate, but only with `rustpython-vm`,
31    /// try to build one from the source code of `InterpreterConfig`. It will not be a one-liner but it also will not be too hard.
32    pub fn without_stdlib(settings: Settings) -> Self {
33        Self::with_init(settings, |_| {})
34    }
35
36    /// Create with initialize function taking mutable vm reference.
37    /// ```
38    /// use rustpython_vm::Interpreter;
39    /// Interpreter::with_init(Default::default(), |vm| {
40    ///     // put this line to add stdlib to the vm
41    ///     // vm.add_native_modules(rustpython_stdlib::get_module_inits());
42    /// }).enter(|vm| {
43    ///     vm.run_code_string(vm.new_scope_with_builtins(), "print(1)", "<...>".to_owned());
44    /// });
45    /// ```
46    pub fn with_init<F>(settings: Settings, init: F) -> Self
47    where
48        F: FnOnce(&mut VirtualMachine),
49    {
50        let ctx = Context::genesis();
51        crate::types::TypeZoo::extend(ctx);
52        crate::exceptions::ExceptionZoo::extend(ctx);
53        let mut vm = VirtualMachine::new(settings, ctx.clone());
54        init(&mut vm);
55        vm.initialize();
56        Self { vm }
57    }
58
59    /// Run a function with the main virtual machine and return a PyResult of the result.
60    ///
61    /// To enter vm context multiple times or to avoid buffer/exception management, this function is preferred.
62    /// `enter` is lightweight and it returns a python object in PyResult.
63    /// You can stop or continue the execution multiple times by calling `enter`.
64    ///
65    /// To finalize the vm once all desired `enter`s are called, calling `finalize` will be helpful.
66    ///
67    /// See also [`run`] for managed way to run the interpreter.
68    pub fn enter<F, R>(&self, f: F) -> R
69    where
70        F: FnOnce(&VirtualMachine) -> R,
71    {
72        thread::enter_vm(&self.vm, || f(&self.vm))
73    }
74
75    /// Run [`enter`] and call `expect_pyresult` for the result.
76    ///
77    /// This function is useful when you want to expect a result from the function,
78    /// but also print useful panic information when exception raised.
79    ///
80    /// See [`enter`] for more information.
81    /// See [`expect_pyresult`] for more information.
82    pub fn enter_and_expect<F, R>(&self, f: F, msg: &str) -> R
83    where
84        F: FnOnce(&VirtualMachine) -> PyResult<R>,
85    {
86        self.enter(|vm| {
87            let result = f(vm);
88            vm.expect_pyresult(result, msg)
89        })
90    }
91
92    /// Run a function with the main virtual machine and return exit code.
93    ///
94    /// To enter vm context only once and safely terminate the vm, this function is preferred.
95    /// Unlike [`enter`], `run` calls finalize and returns exit code.
96    /// You will not be able to obtain Python exception in this way.
97    ///
98    /// See [`finalize`] for the finalization steps.
99    /// See also [`enter`] for pure function call to obtain Python exception.
100    pub fn run<F>(self, f: F) -> u8
101    where
102        F: FnOnce(&VirtualMachine) -> PyResult<()>,
103    {
104        let res = self.enter(|vm| f(vm));
105        self.finalize(res.err())
106    }
107
108    /// Finalize vm and turns an exception to exit code.
109    ///
110    /// Finalization steps including 4 steps:
111    /// 1. Flush stdout and stderr.
112    /// 1. Handle exit exception and turn it to exit code.
113    /// 1. Run atexit exit functions.
114    /// 1. Mark vm as finalized.
115    ///
116    /// Note that calling `finalize` is not necessary by purpose though.
117    pub fn finalize(self, exc: Option<PyBaseExceptionRef>) -> u8 {
118        self.enter(|vm| {
119            vm.flush_std();
120
121            // See if any exception leaked out:
122            let exit_code = if let Some(exc) = exc {
123                vm.handle_exit_exception(exc)
124            } else {
125                0
126            };
127
128            atexit::_run_exitfuncs(vm);
129
130            vm.state.finalizing.store(true, Ordering::Release);
131
132            vm.flush_std();
133
134            exit_code
135        })
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::{
143        builtins::{int, PyStr},
144        PyObjectRef,
145    };
146    use malachite_bigint::ToBigInt;
147
148    #[test]
149    fn test_add_py_integers() {
150        Interpreter::without_stdlib(Default::default()).enter(|vm| {
151            let a: PyObjectRef = vm.ctx.new_int(33_i32).into();
152            let b: PyObjectRef = vm.ctx.new_int(12_i32).into();
153            let res = vm._add(&a, &b).unwrap();
154            let value = int::get_value(&res);
155            assert_eq!(*value, 45_i32.to_bigint().unwrap());
156        })
157    }
158
159    #[test]
160    fn test_multiply_str() {
161        Interpreter::without_stdlib(Default::default()).enter(|vm| {
162            let a = vm.new_pyobj(crate::common::ascii!("Hello "));
163            let b = vm.new_pyobj(4_i32);
164            let res = vm._mul(&a, &b).unwrap();
165            let value = res.payload::<PyStr>().unwrap();
166            assert_eq!(value.as_ref(), "Hello Hello Hello Hello ")
167        })
168    }
169}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.