rustpython_vm/vm/
vm_object.rs

1use super::PyMethod;
2use crate::{
3    builtins::{pystr::AsPyStr, PyBaseExceptionRef, PyList, PyStrInterned},
4    function::IntoFuncArgs,
5    identifier,
6    object::{AsObject, PyObject, PyObjectRef, PyResult},
7    stdlib::sys,
8    vm::VirtualMachine,
9};
10
11/// PyObject support
12impl VirtualMachine {
13    #[track_caller]
14    #[cold]
15    fn _py_panic_failed(&self, exc: PyBaseExceptionRef, msg: &str) -> ! {
16        #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
17        {
18            self.print_exception(exc);
19            self.flush_std();
20            panic!("{msg}")
21        }
22        #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
23        {
24            use wasm_bindgen::prelude::*;
25            #[wasm_bindgen]
26            extern "C" {
27                #[wasm_bindgen(js_namespace = console)]
28                fn error(s: &str);
29            }
30            let mut s = String::new();
31            self.write_exception(&mut s, &exc).unwrap();
32            error(&s);
33            panic!("{}; exception backtrace above", msg)
34        }
35    }
36
37    pub(crate) fn flush_std(&self) {
38        let vm = self;
39        if let Ok(stdout) = sys::get_stdout(vm) {
40            let _ = vm.call_method(&stdout, identifier!(vm, flush).as_str(), ());
41        }
42        if let Ok(stderr) = sys::get_stderr(vm) {
43            let _ = vm.call_method(&stderr, identifier!(vm, flush).as_str(), ());
44        }
45    }
46
47    #[track_caller]
48    pub fn unwrap_pyresult<T>(&self, result: PyResult<T>) -> T {
49        match result {
50            Ok(x) => x,
51            Err(exc) => {
52                self._py_panic_failed(exc, "called `vm.unwrap_pyresult()` on an `Err` value")
53            }
54        }
55    }
56    #[track_caller]
57    pub fn expect_pyresult<T>(&self, result: PyResult<T>, msg: &str) -> T {
58        match result {
59            Ok(x) => x,
60            Err(exc) => self._py_panic_failed(exc, msg),
61        }
62    }
63
64    /// Test whether a python object is `None`.
65    pub fn is_none(&self, obj: &PyObject) -> bool {
66        obj.is(&self.ctx.none)
67    }
68    pub fn option_if_none(&self, obj: PyObjectRef) -> Option<PyObjectRef> {
69        if self.is_none(&obj) {
70            None
71        } else {
72            Some(obj)
73        }
74    }
75    pub fn unwrap_or_none(&self, obj: Option<PyObjectRef>) -> PyObjectRef {
76        obj.unwrap_or_else(|| self.ctx.none())
77    }
78
79    pub fn call_get_descriptor_specific(
80        &self,
81        descr: &PyObject,
82        obj: Option<PyObjectRef>,
83        cls: Option<PyObjectRef>,
84    ) -> Option<PyResult> {
85        let descr_get = descr
86            .class()
87            .mro_find_map(|cls| cls.slots.descr_get.load())?;
88        Some(descr_get(descr.to_owned(), obj, cls, self))
89    }
90
91    pub fn call_get_descriptor(&self, descr: &PyObject, obj: PyObjectRef) -> Option<PyResult> {
92        let cls = obj.class().to_owned().into();
93        self.call_get_descriptor_specific(descr, Some(obj), Some(cls))
94    }
95
96    pub fn call_if_get_descriptor(&self, attr: &PyObject, obj: PyObjectRef) -> PyResult {
97        self.call_get_descriptor(attr, obj)
98            .unwrap_or_else(|| Ok(attr.to_owned()))
99    }
100
101    #[inline]
102    pub fn call_method<T>(&self, obj: &PyObject, method_name: &str, args: T) -> PyResult
103    where
104        T: IntoFuncArgs,
105    {
106        flame_guard!(format!("call_method({:?})", method_name));
107
108        let dynamic_name;
109        let name = match self.ctx.interned_str(method_name) {
110            Some(name) => name.as_pystr(&self.ctx),
111            None => {
112                dynamic_name = self.ctx.new_str(method_name);
113                &dynamic_name
114            }
115        };
116        PyMethod::get(obj.to_owned(), name, self)?.invoke(args, self)
117    }
118
119    pub fn dir(&self, obj: Option<PyObjectRef>) -> PyResult<PyList> {
120        let seq = match obj {
121            Some(obj) => self
122                .get_special_method(&obj, identifier!(self, __dir__))?
123                .ok_or_else(|| self.new_type_error("object does not provide __dir__".to_owned()))?
124                .invoke((), self)?,
125            None => self.call_method(
126                self.current_locals()?.as_object(),
127                identifier!(self, keys).as_str(),
128                (),
129            )?,
130        };
131        let items: Vec<_> = seq.try_to_value(self)?;
132        let lst = PyList::from(items);
133        lst.sort(Default::default(), self)?;
134        Ok(lst)
135    }
136
137    #[inline]
138    pub(crate) fn get_special_method(
139        &self,
140        obj: &PyObject,
141        method: &'static PyStrInterned,
142    ) -> PyResult<Option<PyMethod>> {
143        PyMethod::get_special::<false>(obj, method, self)
144    }
145
146    /// NOT PUBLIC API
147    #[doc(hidden)]
148    pub fn call_special_method(
149        &self,
150        obj: &PyObject,
151        method: &'static PyStrInterned,
152        args: impl IntoFuncArgs,
153    ) -> PyResult {
154        self.get_special_method(obj, method)?
155            .ok_or_else(|| self.new_attribute_error(method.as_str().to_owned()))?
156            .invoke(args, self)
157    }
158
159    /// Same as __builtins__.print in Python.
160    /// A convenience function to provide a simple way to print objects for debug purpose.
161    // NOTE: Keep the interface simple.
162    pub fn print(&self, args: impl IntoFuncArgs) -> PyResult<()> {
163        let ret = self.builtins.get_attr("print", self)?.call(args, self)?;
164        debug_assert!(self.is_none(&ret));
165        Ok(())
166    }
167
168    #[deprecated(note = "in favor of `obj.call(args, vm)`")]
169    pub fn invoke(&self, obj: &impl AsObject, args: impl IntoFuncArgs) -> PyResult {
170        obj.as_object().call(args, self)
171    }
172}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.