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