1use libc::c_int;
20use std::ffi::CString;
21use std::marker::PhantomData;
22
23use crate::err::{self, PyErr, PyResult};
24use crate::ffi;
25use crate::objects::{PyBool, PyDict, PyModule, PyObject, PyType};
26use crate::pythonrun::GILGuard;
27
28#[derive(Copy, Clone)]
39pub struct Python<'p>(PhantomData<&'p GILGuard>);
40
41pub trait PythonObject: crate::conversion::ToPyObject + Send + Sized + 'static {
43 fn as_object(&self) -> &PyObject;
45
46 fn into_object(self) -> PyObject;
48
49 unsafe fn unchecked_downcast_from(obj: PyObject) -> Self;
52
53 unsafe fn unchecked_downcast_borrow_from(obj: &PyObject) -> &Self;
56}
57
58pub struct PythonObjectDowncastError<'p> {
60 pub(crate) py: Python<'p>,
61 pub(crate) expected_type_name: String,
62 pub(crate) received_type: PyType,
63}
64
65impl<'p> PythonObjectDowncastError<'p> {
66 pub fn new(
67 py: Python<'p>,
68 expected_type_name: impl Into<String>,
69 received_type: PyType,
70 ) -> Self {
71 let expected_type_name = expected_type_name.into();
72 PythonObjectDowncastError {
73 py,
74 expected_type_name,
75 received_type,
76 }
77 }
78}
79
80pub trait PythonObjectWithCheckedDowncast: PythonObject {
82 fn downcast_from(py: Python<'_>, obj: PyObject) -> Result<Self, PythonObjectDowncastError<'_>>;
84
85 fn downcast_borrow_from<'a, 'p>(
87 py: Python<'p>,
88 obj: &'a PyObject,
89 ) -> Result<&'a Self, PythonObjectDowncastError<'p>>;
90}
91
92pub trait PythonObjectWithTypeObject: PythonObjectWithCheckedDowncast {
94 fn type_object(py: Python) -> PyType;
96}
97
98pub trait PyClone: Sized {
99 fn clone_ref(&self, py: Python) -> Self;
100}
101
102impl<T> PyClone for T
103where
104 T: PythonObject,
105{
106 #[inline]
107 fn clone_ref(&self, py: Python) -> T {
108 let ptr = self.as_object().as_ptr();
109 unsafe { T::unchecked_downcast_from(PyObject::from_borrowed_ptr(py, ptr)) }
110 }
111}
112
113impl<T> PyClone for Option<T>
114where
115 T: PyClone,
116{
117 #[inline]
118 fn clone_ref(&self, py: Python) -> Option<T> {
119 self.as_ref().map(|v| v.clone_ref(py))
120 }
121}
122
123pub trait PyDrop: Sized {
124 fn release_ref(self, py: Python);
125}
126
127impl<T> PyDrop for T
128where
129 T: PythonObject,
130{
131 #[inline]
132 fn release_ref(self, _py: Python) {
133 let ptr = self.into_object().steal_ptr();
134 unsafe {
135 ffi::Py_DECREF(ptr);
136 }
137 }
138}
139
140impl<T> PyDrop for Option<T>
141where
142 T: PyDrop,
143{
144 #[inline]
145 fn release_ref(self, py: Python) {
146 if let Some(v) = self {
147 v.release_ref(py)
148 }
149 }
150}
151
152pub trait ToPythonPointer {
154 fn as_ptr(&self) -> *mut ffi::PyObject;
156
157 fn steal_ptr(self, py: Python) -> *mut ffi::PyObject;
159}
160
161impl ToPythonPointer for PyObject {
163 #[inline]
164 fn as_ptr(&self) -> *mut ffi::PyObject {
165 self.as_ptr()
166 }
167
168 #[inline]
169 fn steal_ptr(self, _py: Python) -> *mut ffi::PyObject {
170 self.steal_ptr()
171 }
172}
173
174impl<'a, T> ToPythonPointer for &'a T
176where
177 T: PythonObject,
178{
179 #[inline]
180 fn as_ptr(&self) -> *mut ffi::PyObject {
181 self.as_object().as_ptr()
182 }
183
184 #[inline]
185 fn steal_ptr(self, py: Python) -> *mut ffi::PyObject {
186 self.as_object().clone_ref(py).steal_ptr()
187 }
188}
189
190impl<T> ToPythonPointer for Option<T>
192where
193 T: ToPythonPointer,
194{
195 #[inline]
196 fn as_ptr(&self) -> *mut ffi::PyObject {
197 match *self {
198 Some(ref t) => t.as_ptr(),
199 None => std::ptr::null_mut(),
200 }
201 }
202
203 #[inline]
204 fn steal_ptr(self, py: Python) -> *mut ffi::PyObject {
205 match self {
206 Some(t) => t.steal_ptr(py),
207 None => std::ptr::null_mut(),
208 }
209 }
210}
211
212impl<'p> Python<'p> {
213 #[inline]
220 pub unsafe fn assume_gil_acquired() -> Python<'p> {
221 Python(PhantomData)
222 }
223
224 #[inline]
229 pub fn acquire_gil() -> GILGuard {
230 GILGuard::acquire()
231 }
232
233 pub fn allow_threads<T, F>(self, f: F) -> T
235 where
236 F: Send + FnOnce() -> T,
237 {
238 unsafe {
241 let save = ffi::PyEval_SaveThread();
242 let result = f();
243 ffi::PyEval_RestoreThread(save);
244 result
245 }
246 }
247
248 pub fn eval(
253 self,
254 code: &str,
255 globals: Option<&PyDict>,
256 locals: Option<&PyDict>,
257 ) -> PyResult<PyObject> {
258 self.run_code(code, ffi::Py_eval_input, globals, locals)
259 }
260
261 pub fn run(
266 self,
267 code: &str,
268 globals: Option<&PyDict>,
269 locals: Option<&PyDict>,
270 ) -> PyResult<()> {
271 self.run_code(code, ffi::Py_file_input, globals, locals)?;
272 Ok(())
273 }
274
275 fn run_code(
282 self,
283 code: &str,
284 start: c_int,
285 globals: Option<&PyDict>,
286 locals: Option<&PyDict>,
287 ) -> PyResult<PyObject> {
288 let code = CString::new(code).unwrap();
289
290 unsafe {
291 let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _);
292
293 if mptr.is_null() {
294 return Err(PyErr::fetch(self));
295 }
296
297 let mdict = ffi::PyModule_GetDict(mptr);
298
299 let globals = match globals {
300 Some(g) => g.as_ptr(),
301 None => mdict,
302 };
303
304 let locals = match locals {
305 Some(l) => l.as_ptr(),
306 None => globals,
307 };
308
309 let res_ptr =
310 ffi::PyRun_StringFlags(code.as_ptr(), start, globals, locals, std::ptr::null_mut());
311
312 err::result_from_owned_ptr(self, res_ptr)
313 }
314 }
315
316 #[allow(non_snake_case)] #[inline]
319 pub fn None(self) -> PyObject {
320 unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) }
321 }
322
323 #[allow(non_snake_case)] #[inline]
326 pub fn True(self) -> PyBool {
327 unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_True()).unchecked_cast_into::<PyBool>() }
328 }
329
330 #[allow(non_snake_case)] #[inline]
333 pub fn False(self) -> PyBool {
334 unsafe {
335 PyObject::from_borrowed_ptr(self, ffi::Py_False()).unchecked_cast_into::<PyBool>()
336 }
337 }
338
339 #[allow(non_snake_case)] #[inline]
342 pub fn NotImplemented(self) -> PyObject {
343 unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) }
344 }
345
346 pub fn get_type<T>(self) -> PyType
348 where
349 T: PythonObjectWithTypeObject,
350 {
351 T::type_object(self)
352 }
353
354 pub fn import(self, name: &str) -> PyResult<PyModule> {
356 PyModule::import(self, name)
357 }
358}
359
360impl<'p> std::fmt::Debug for PythonObjectDowncastError<'p> {
361 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
362 f.write_str("PythonObjectDowncastError")
363 }
364}
365
366#[cfg(test)]
367mod test {
368 use crate::{PyDict, Python};
369
370 #[test]
371 fn test_eval() {
372 let gil = Python::acquire_gil();
373 let py = gil.python();
374
375 let v: i32 = py
377 .eval("min(1, 2)", None, None)
378 .unwrap()
379 .extract(py)
380 .unwrap();
381 assert_eq!(v, 1);
382
383 let d = PyDict::new(py);
384 d.set_item(py, "foo", 13).unwrap();
385
386 let v: i32 = py
388 .eval("foo + 29", None, Some(&d))
389 .unwrap()
390 .extract(py)
391 .unwrap();
392 assert_eq!(v, 42);
393
394 let v: i32 = py
396 .eval("min(foo, 2)", None, Some(&d))
397 .unwrap()
398 .extract(py)
399 .unwrap();
400 assert_eq!(v, 2);
401 }
402}