1use super::PyMethod;
2use crate::{
3 builtins::{PyBaseExceptionRef, PyList, PyStrInterned, pystr::AsPyStr},
4 function::IntoFuncArgs,
5 object::{AsObject, PyObject, PyObjectRef, PyResult},
6 stdlib::sys,
7 vm::VirtualMachine,
8};
9
10impl VirtualMachine {
12 #[track_caller]
13 #[cold]
14 fn _py_panic_failed(&self, exc: PyBaseExceptionRef, msg: &str) -> ! {
15 #[cfg(not(all(
16 target_arch = "wasm32",
17 not(any(target_os = "emscripten", target_os = "wasi")),
18 )))]
19 {
20 self.print_exception(exc);
21 self.flush_std();
22 panic!("{msg}")
23 }
24 #[cfg(all(
25 target_arch = "wasm32",
26 feature = "wasmbind",
27 not(any(target_os = "emscripten", target_os = "wasi")),
28 ))]
29 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
30 {
31 use wasm_bindgen::prelude::*;
32 #[wasm_bindgen]
33 extern "C" {
34 #[wasm_bindgen(js_namespace = console)]
35 fn error(s: &str);
36 }
37 let mut s = String::new();
38 self.write_exception(&mut s, &exc).unwrap();
39 error(&s);
40 panic!("{msg}; exception backtrace above")
41 }
42 #[cfg(all(
43 target_arch = "wasm32",
44 not(feature = "wasmbind"),
45 not(any(target_os = "emscripten", target_os = "wasi")),
46 ))]
47 {
48 use crate::convert::ToPyObject;
49 let err_string: String = exc.to_pyobject(self).repr(self).unwrap().to_string();
50 eprintln!("{err_string}");
51 panic!("{msg}; python exception not available")
52 }
53 }
54
55 fn file_is_closed(&self, file: &PyObject) -> bool {
57 file.get_attr("closed", self)
58 .ok()
59 .is_some_and(|v| v.try_to_bool(self).unwrap_or(false))
60 }
61
62 pub(crate) fn flush_std(&self) -> i32 {
63 let vm = self;
64 let mut status = 0;
65 if let Ok(stdout) = sys::get_stdout(vm)
66 && !vm.is_none(&stdout)
67 && !vm.file_is_closed(&stdout)
68 && let Err(e) = vm.call_method(&stdout, identifier!(vm, flush).as_str(), ())
69 {
70 vm.run_unraisable(e, None, stdout);
71 status = -1;
72 }
73 if let Ok(stderr) = sys::get_stderr(vm)
74 && !vm.is_none(&stderr)
75 && !vm.file_is_closed(&stderr)
76 {
77 let _ = vm.call_method(&stderr, identifier!(vm, flush).as_str(), ());
78 }
79 status
80 }
81
82 #[track_caller]
83 pub fn unwrap_pyresult<T>(&self, result: PyResult<T>) -> T {
84 match result {
85 Ok(x) => x,
86 Err(exc) => {
87 self._py_panic_failed(exc, "called `vm.unwrap_pyresult()` on an `Err` value")
88 }
89 }
90 }
91 #[track_caller]
92 pub fn expect_pyresult<T>(&self, result: PyResult<T>, msg: &str) -> T {
93 match result {
94 Ok(x) => x,
95 Err(exc) => self._py_panic_failed(exc, msg),
96 }
97 }
98
99 pub fn is_none(&self, obj: &PyObject) -> bool {
101 obj.is(&self.ctx.none)
102 }
103 pub fn option_if_none(&self, obj: PyObjectRef) -> Option<PyObjectRef> {
104 if self.is_none(&obj) { None } else { Some(obj) }
105 }
106 pub fn unwrap_or_none(&self, obj: Option<PyObjectRef>) -> PyObjectRef {
107 obj.unwrap_or_else(|| self.ctx.none())
108 }
109
110 pub fn call_get_descriptor_specific(
111 &self,
112 descr: &PyObject,
113 obj: Option<PyObjectRef>,
114 cls: Option<PyObjectRef>,
115 ) -> Option<PyResult> {
116 let descr_get = descr.class().slots.descr_get.load()?;
117 Some(descr_get(descr.to_owned(), obj, cls, self))
118 }
119
120 pub fn call_get_descriptor(&self, descr: &PyObject, obj: PyObjectRef) -> Option<PyResult> {
121 let cls = obj.class().to_owned().into();
122 self.call_get_descriptor_specific(descr, Some(obj), Some(cls))
123 }
124
125 pub fn call_if_get_descriptor(&self, attr: &PyObject, obj: PyObjectRef) -> PyResult {
126 self.call_get_descriptor(attr, obj)
127 .unwrap_or_else(|| Ok(attr.to_owned()))
128 }
129
130 #[inline]
131 pub fn call_method<T>(&self, obj: &PyObject, method_name: &str, args: T) -> PyResult
132 where
133 T: IntoFuncArgs,
134 {
135 flame_guard!(format!("call_method({:?})", method_name));
136
137 let dynamic_name;
138 let name = match self.ctx.interned_str(method_name) {
139 Some(name) => name.as_pystr(&self.ctx),
140 None => {
141 dynamic_name = self.ctx.new_str(method_name);
142 &dynamic_name
143 }
144 };
145 PyMethod::get(obj.to_owned(), name, self)?.invoke(args, self)
146 }
147
148 pub fn dir(&self, obj: Option<PyObjectRef>) -> PyResult<PyList> {
149 let seq = match obj {
150 Some(obj) => self
151 .get_special_method(&obj, identifier!(self, __dir__))?
152 .ok_or_else(|| self.new_type_error("object does not provide __dir__"))?
153 .invoke((), self)?,
154 None => self.call_method(
155 self.current_locals()?.as_object(),
156 identifier!(self, keys).as_str(),
157 (),
158 )?,
159 };
160 let items: Vec<_> = seq.try_to_value(self)?;
161 let lst = PyList::from(items);
162 lst.sort(Default::default(), self)?;
163 Ok(lst)
164 }
165
166 #[inline]
167 pub(crate) fn get_special_method(
168 &self,
169 obj: &PyObject,
170 method: &'static PyStrInterned,
171 ) -> PyResult<Option<PyMethod>> {
172 PyMethod::get_special::<false>(obj, method, self)
173 }
174
175 #[doc(hidden)]
177 pub fn call_special_method(
178 &self,
179 obj: &PyObject,
180 method: &'static PyStrInterned,
181 args: impl IntoFuncArgs,
182 ) -> PyResult {
183 self.get_special_method(obj, method)?
184 .ok_or_else(|| self.new_attribute_error(method.as_str().to_owned()))?
185 .invoke(args, self)
186 }
187
188 pub fn print(&self, args: impl IntoFuncArgs) -> PyResult<()> {
192 let ret = self.builtins.get_attr("print", self)?.call(args, self)?;
193 debug_assert!(self.is_none(&ret));
194 Ok(())
195 }
196
197 #[deprecated(note = "in favor of `obj.call(args, vm)`")]
198 pub fn invoke(&self, obj: &impl AsObject, args: impl IntoFuncArgs) -> PyResult {
199 obj.as_object().call(args, self)
200 }
201}