rustpython_vm/vm/
vm_new.rs

1use crate::{
2    builtins::{
3        builtin_func::PyNativeFunction,
4        descriptor::PyMethodDescriptor,
5        tuple::{IntoPyTuple, PyTupleRef},
6        PyBaseException, PyBaseExceptionRef, PyDictRef, PyModule, PyStrRef, PyType, PyTypeRef,
7    },
8    convert::ToPyObject,
9    function::{IntoPyNativeFn, PyMethodFlags},
10    scope::Scope,
11    vm::VirtualMachine,
12    AsObject, Py, PyObject, PyObjectRef, PyRef,
13};
14
15/// Collection of object creation helpers
16impl VirtualMachine {
17    /// Create a new python object
18    pub fn new_pyobj(&self, value: impl ToPyObject) -> PyObjectRef {
19        value.to_pyobject(self)
20    }
21
22    pub fn new_tuple(&self, value: impl IntoPyTuple) -> PyTupleRef {
23        value.into_pytuple(self)
24    }
25
26    pub fn new_module(
27        &self,
28        name: &str,
29        dict: PyDictRef,
30        doc: Option<PyStrRef>,
31    ) -> PyRef<PyModule> {
32        let module = PyRef::new_ref(
33            PyModule::new(),
34            self.ctx.types.module_type.to_owned(),
35            Some(dict),
36        );
37        module.init_dict(self.ctx.intern_str(name), doc, self);
38        module
39    }
40
41    pub fn new_scope_with_builtins(&self) -> Scope {
42        Scope::with_builtins(None, self.ctx.new_dict(), self)
43    }
44
45    pub fn new_function<F, FKind>(&self, name: &'static str, f: F) -> PyRef<PyNativeFunction>
46    where
47        F: IntoPyNativeFn<FKind>,
48    {
49        let def = self
50            .ctx
51            .new_method_def(name, f, PyMethodFlags::empty(), None);
52        def.build_function(self)
53    }
54
55    pub fn new_method<F, FKind>(
56        &self,
57        name: &'static str,
58        class: &'static Py<PyType>,
59        f: F,
60    ) -> PyRef<PyMethodDescriptor>
61    where
62        F: IntoPyNativeFn<FKind>,
63    {
64        let def = self
65            .ctx
66            .new_method_def(name, f, PyMethodFlags::METHOD, None);
67        def.build_method(class, self)
68    }
69
70    /// Instantiate an exception with arguments.
71    /// This function should only be used with builtin exception types; if a user-defined exception
72    /// type is passed in, it may not be fully initialized; try using
73    /// [`vm.invoke_exception()`][Self::invoke_exception] or
74    /// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
75    pub fn new_exception(&self, exc_type: PyTypeRef, args: Vec<PyObjectRef>) -> PyBaseExceptionRef {
76        // TODO: add repr of args into logging?
77
78        PyRef::new_ref(
79            // TODO: this constructor might be invalid, because multiple
80            // exception (even builtin ones) are using custom constructors,
81            // see `OSError` as an example:
82            PyBaseException::new(args, self),
83            exc_type,
84            Some(self.ctx.new_dict()),
85        )
86    }
87
88    /// Instantiate an exception with no arguments.
89    /// This function should only be used with builtin exception types; if a user-defined exception
90    /// type is passed in, it may not be fully initialized; try using
91    /// [`vm.invoke_exception()`][Self::invoke_exception] or
92    /// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
93    pub fn new_exception_empty(&self, exc_type: PyTypeRef) -> PyBaseExceptionRef {
94        self.new_exception(exc_type, vec![])
95    }
96
97    /// Instantiate an exception with `msg` as the only argument.
98    /// This function should only be used with builtin exception types; if a user-defined exception
99    /// type is passed in, it may not be fully initialized; try using
100    /// [`vm.invoke_exception()`][Self::invoke_exception] or
101    /// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
102    pub fn new_exception_msg(&self, exc_type: PyTypeRef, msg: String) -> PyBaseExceptionRef {
103        self.new_exception(exc_type, vec![self.ctx.new_str(msg).into()])
104    }
105
106    /// Instantiate an exception with `msg` as the only argument and `dict` for object
107    /// This function should only be used with builtin exception types; if a user-defined exception
108    /// type is passed in, it may not be fully initialized; try using
109    /// [`vm.invoke_exception()`][Self::invoke_exception] or
110    /// [`exceptions::ExceptionCtor`][crate::exceptions::ExceptionCtor] instead.
111    pub fn new_exception_msg_dict(
112        &self,
113        exc_type: PyTypeRef,
114        msg: String,
115        dict: PyDictRef,
116    ) -> PyBaseExceptionRef {
117        PyRef::new_ref(
118            // TODO: this constructor might be invalid, because multiple
119            // exception (even builtin ones) are using custom constructors,
120            // see `OSError` as an example:
121            PyBaseException::new(vec![self.ctx.new_str(msg).into()], self),
122            exc_type,
123            Some(dict),
124        )
125    }
126
127    pub fn new_lookup_error(&self, msg: String) -> PyBaseExceptionRef {
128        let lookup_error = self.ctx.exceptions.lookup_error.to_owned();
129        self.new_exception_msg(lookup_error, msg)
130    }
131
132    pub fn new_attribute_error(&self, msg: String) -> PyBaseExceptionRef {
133        let attribute_error = self.ctx.exceptions.attribute_error.to_owned();
134        self.new_exception_msg(attribute_error, msg)
135    }
136
137    pub fn new_type_error(&self, msg: String) -> PyBaseExceptionRef {
138        let type_error = self.ctx.exceptions.type_error.to_owned();
139        self.new_exception_msg(type_error, msg)
140    }
141
142    pub fn new_name_error(&self, msg: String, name: PyStrRef) -> PyBaseExceptionRef {
143        let name_error_type = self.ctx.exceptions.name_error.to_owned();
144        let name_error = self.new_exception_msg(name_error_type, msg);
145        name_error.as_object().set_attr("name", name, self).unwrap();
146        name_error
147    }
148
149    pub fn new_unsupported_unary_error(&self, a: &PyObject, op: &str) -> PyBaseExceptionRef {
150        self.new_type_error(format!(
151            "bad operand type for {}: '{}'",
152            op,
153            a.class().name()
154        ))
155    }
156
157    pub fn new_unsupported_binop_error(
158        &self,
159        a: &PyObject,
160        b: &PyObject,
161        op: &str,
162    ) -> PyBaseExceptionRef {
163        self.new_type_error(format!(
164            "'{}' not supported between instances of '{}' and '{}'",
165            op,
166            a.class().name(),
167            b.class().name()
168        ))
169    }
170
171    pub fn new_unsupported_ternop_error(
172        &self,
173        a: &PyObject,
174        b: &PyObject,
175        c: &PyObject,
176        op: &str,
177    ) -> PyBaseExceptionRef {
178        self.new_type_error(format!(
179            "Unsupported operand types for '{}': '{}', '{}', and '{}'",
180            op,
181            a.class().name(),
182            b.class().name(),
183            c.class().name()
184        ))
185    }
186
187    pub fn new_os_error(&self, msg: String) -> PyBaseExceptionRef {
188        let os_error = self.ctx.exceptions.os_error.to_owned();
189        self.new_exception_msg(os_error, msg)
190    }
191
192    pub fn new_errno_error(&self, errno: i32, msg: String) -> PyBaseExceptionRef {
193        let vm = self;
194        let exc_type =
195            crate::exceptions::errno_to_exc_type(errno, vm).unwrap_or(vm.ctx.exceptions.os_error);
196
197        let errno_obj = vm.new_pyobj(errno);
198        vm.new_exception(exc_type.to_owned(), vec![errno_obj, vm.new_pyobj(msg)])
199    }
200
201    pub fn new_system_error(&self, msg: String) -> PyBaseExceptionRef {
202        let sys_error = self.ctx.exceptions.system_error.to_owned();
203        self.new_exception_msg(sys_error, msg)
204    }
205
206    pub fn new_unicode_decode_error(&self, msg: String) -> PyBaseExceptionRef {
207        let unicode_decode_error = self.ctx.exceptions.unicode_decode_error.to_owned();
208        self.new_exception_msg(unicode_decode_error, msg)
209    }
210
211    pub fn new_unicode_encode_error(&self, msg: String) -> PyBaseExceptionRef {
212        let unicode_encode_error = self.ctx.exceptions.unicode_encode_error.to_owned();
213        self.new_exception_msg(unicode_encode_error, msg)
214    }
215
216    /// Create a new python ValueError object. Useful for raising errors from
217    /// python functions implemented in rust.
218    pub fn new_value_error(&self, msg: String) -> PyBaseExceptionRef {
219        let value_error = self.ctx.exceptions.value_error.to_owned();
220        self.new_exception_msg(value_error, msg)
221    }
222
223    pub fn new_buffer_error(&self, msg: String) -> PyBaseExceptionRef {
224        let buffer_error = self.ctx.exceptions.buffer_error.to_owned();
225        self.new_exception_msg(buffer_error, msg)
226    }
227
228    // TODO: don't take ownership should make the success path faster
229    pub fn new_key_error(&self, obj: PyObjectRef) -> PyBaseExceptionRef {
230        let key_error = self.ctx.exceptions.key_error.to_owned();
231        self.new_exception(key_error, vec![obj])
232    }
233
234    pub fn new_index_error(&self, msg: String) -> PyBaseExceptionRef {
235        let index_error = self.ctx.exceptions.index_error.to_owned();
236        self.new_exception_msg(index_error, msg)
237    }
238
239    pub fn new_not_implemented_error(&self, msg: String) -> PyBaseExceptionRef {
240        let not_implemented_error = self.ctx.exceptions.not_implemented_error.to_owned();
241        self.new_exception_msg(not_implemented_error, msg)
242    }
243
244    pub fn new_recursion_error(&self, msg: String) -> PyBaseExceptionRef {
245        let recursion_error = self.ctx.exceptions.recursion_error.to_owned();
246        self.new_exception_msg(recursion_error, msg)
247    }
248
249    pub fn new_zero_division_error(&self, msg: String) -> PyBaseExceptionRef {
250        let zero_division_error = self.ctx.exceptions.zero_division_error.to_owned();
251        self.new_exception_msg(zero_division_error, msg)
252    }
253
254    pub fn new_overflow_error(&self, msg: String) -> PyBaseExceptionRef {
255        let overflow_error = self.ctx.exceptions.overflow_error.to_owned();
256        self.new_exception_msg(overflow_error, msg)
257    }
258
259    #[cfg(any(feature = "rustpython-parser", feature = "rustpython-codegen"))]
260    pub fn new_syntax_error(
261        &self,
262        error: &crate::compiler::CompileError,
263        source: Option<&str>,
264    ) -> PyBaseExceptionRef {
265        use crate::source_code::SourceLocation;
266
267        let syntax_error_type = match &error.error {
268            #[cfg(feature = "rustpython-parser")]
269            crate::compiler::CompileErrorType::Parse(p) if p.is_indentation_error() => {
270                self.ctx.exceptions.indentation_error
271            }
272            #[cfg(feature = "rustpython-parser")]
273            crate::compiler::CompileErrorType::Parse(p) if p.is_tab_error() => {
274                self.ctx.exceptions.tab_error
275            }
276            _ => self.ctx.exceptions.syntax_error,
277        }
278        .to_owned();
279
280        // TODO: replace to SourceCode
281        fn get_statement(source: &str, loc: Option<SourceLocation>) -> Option<String> {
282            let line = source
283                .split('\n')
284                .nth(loc?.row.to_zero_indexed_usize())?
285                .to_owned();
286            Some(line + "\n")
287        }
288
289        let statement = if let Some(source) = source {
290            get_statement(source, error.location)
291        } else {
292            None
293        };
294
295        let syntax_error = self.new_exception_msg(syntax_error_type, error.error.to_string());
296        let (lineno, offset) = error.python_location();
297        let lineno = self.ctx.new_int(lineno);
298        let offset = self.ctx.new_int(offset);
299        syntax_error
300            .as_object()
301            .set_attr("lineno", lineno, self)
302            .unwrap();
303        syntax_error
304            .as_object()
305            .set_attr("offset", offset, self)
306            .unwrap();
307
308        syntax_error
309            .as_object()
310            .set_attr("text", statement.to_pyobject(self), self)
311            .unwrap();
312        syntax_error
313            .as_object()
314            .set_attr(
315                "filename",
316                self.ctx.new_str(error.source_path.clone()),
317                self,
318            )
319            .unwrap();
320        syntax_error
321    }
322
323    pub fn new_import_error(&self, msg: String, name: PyStrRef) -> PyBaseExceptionRef {
324        let import_error = self.ctx.exceptions.import_error.to_owned();
325        let exc = self.new_exception_msg(import_error, msg);
326        exc.as_object().set_attr("name", name, self).unwrap();
327        exc
328    }
329
330    pub fn new_runtime_error(&self, msg: String) -> PyBaseExceptionRef {
331        let runtime_error = self.ctx.exceptions.runtime_error.to_owned();
332        self.new_exception_msg(runtime_error, msg)
333    }
334
335    pub fn new_memory_error(&self, msg: String) -> PyBaseExceptionRef {
336        let memory_error_type = self.ctx.exceptions.memory_error.to_owned();
337        self.new_exception_msg(memory_error_type, msg)
338    }
339
340    pub fn new_stop_iteration(&self, value: Option<PyObjectRef>) -> PyBaseExceptionRef {
341        let dict = self.ctx.new_dict();
342        let args = if let Some(value) = value {
343            // manually set `value` attribute like StopIteration.__init__
344            dict.set_item("value", value.clone(), self)
345                .expect("dict.__setitem__ never fails");
346            vec![value]
347        } else {
348            Vec::new()
349        };
350
351        PyRef::new_ref(
352            PyBaseException::new(args, self),
353            self.ctx.exceptions.stop_iteration.to_owned(),
354            Some(dict),
355        )
356    }
357
358    fn new_downcast_error(
359        &self,
360        msg: &'static str,
361        error_type: &'static Py<PyType>,
362        class: &Py<PyType>,
363        obj: &PyObject, // the impl Borrow allows to pass PyObjectRef or &PyObject
364    ) -> PyBaseExceptionRef {
365        let actual_class = obj.class();
366        let actual_type = &*actual_class.name();
367        let expected_type = &*class.name();
368        let msg = format!("Expected {msg} '{expected_type}' but '{actual_type}' found");
369        self.new_exception_msg(error_type.to_owned(), msg)
370    }
371
372    pub(crate) fn new_downcast_runtime_error(
373        &self,
374        class: &Py<PyType>,
375        obj: &impl AsObject,
376    ) -> PyBaseExceptionRef {
377        self.new_downcast_error(
378            "payload",
379            self.ctx.exceptions.runtime_error,
380            class,
381            obj.as_object(),
382        )
383    }
384
385    pub(crate) fn new_downcast_type_error(
386        &self,
387        class: &Py<PyType>,
388        obj: &impl AsObject,
389    ) -> PyBaseExceptionRef {
390        self.new_downcast_error(
391            "type",
392            self.ctx.exceptions.type_error,
393            class,
394            obj.as_object(),
395        )
396    }
397}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.