cpython/
python.rs

1// Copyright (c) 2015 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19use 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/// Marker type that indicates that the GIL is currently held.
29///
30/// The 'Python' struct is a zero-size marker struct that is required for most Python operations.
31/// This is used to indicate that the operation accesses/modifies the Python interpreter state,
32/// and thus can only be called if the Python interpreter is initialized and the
33/// Python global interpreter lock (GIL) is acquired.
34/// The lifetime `'p` represents the lifetime of the Python interpreter.
35///
36/// You can imagine the GIL to be a giant `Mutex<PythonInterpreterState>`.
37/// The type `Python<'p>` then acts like a reference `&'p PythonInterpreterState`.
38#[derive(Copy, Clone)]
39pub struct Python<'p>(PhantomData<&'p GILGuard>);
40
41/// Trait implemented by all Python object types.
42pub trait PythonObject: crate::conversion::ToPyObject + Send + Sized + 'static {
43    /// Casts the Python object to PyObject.
44    fn as_object(&self) -> &PyObject;
45
46    /// Casts the Python object to PyObject.
47    fn into_object(self) -> PyObject;
48
49    /// Unchecked downcast from PyObject to Self.
50    /// Undefined behavior if the input object does not have the expected type.
51    unsafe fn unchecked_downcast_from(obj: PyObject) -> Self;
52
53    /// Unchecked downcast from PyObject to Self.
54    /// Undefined behavior if the input object does not have the expected type.
55    unsafe fn unchecked_downcast_borrow_from(obj: &PyObject) -> &Self;
56}
57
58// Marker type that indicates an error while downcasting
59pub 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
80/// Trait implemented by Python object types that allow a checked downcast.
81pub trait PythonObjectWithCheckedDowncast: PythonObject {
82    /// Cast from PyObject to a concrete Python object type.
83    fn downcast_from(py: Python<'_>, obj: PyObject) -> Result<Self, PythonObjectDowncastError<'_>>;
84
85    /// Cast from PyObject to a concrete Python object type.
86    fn downcast_borrow_from<'a, 'p>(
87        py: Python<'p>,
88        obj: &'a PyObject,
89    ) -> Result<&'a Self, PythonObjectDowncastError<'p>>;
90}
91
92/// Trait implemented by Python object types that have a corresponding type object.
93pub trait PythonObjectWithTypeObject: PythonObjectWithCheckedDowncast {
94    /// Retrieves the type object for this Python object type.
95    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
152/// This trait allows retrieving the underlying FFI pointer from Python objects.
153pub trait ToPythonPointer {
154    /// Retrieves the underlying FFI pointer (as a borrowed pointer).
155    fn as_ptr(&self) -> *mut ffi::PyObject;
156
157    /// Retrieves the underlying FFI pointer as a "stolen pointer".
158    fn steal_ptr(self, py: Python) -> *mut ffi::PyObject;
159}
160
161/// ToPythonPointer for borrowed Python pointers.
162impl 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
174/// ToPythonPointer for borrowed Python pointers.
175impl<'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
190/// Convert None into a null pointer.
191impl<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    /// Retrieve Python instance under the assumption that the GIL is already acquired at this point,
214    /// and stays acquired for the lifetime `'p`.
215    ///
216    /// Because the output lifetime `'p` is not connected to any input parameter,
217    /// care must be taken that the compiler infers an appropriate lifetime for `'p`
218    /// when calling this function.
219    #[inline]
220    pub unsafe fn assume_gil_acquired() -> Python<'p> {
221        Python(PhantomData)
222    }
223
224    /// Acquires the global interpreter lock, which allows access to the Python runtime.
225    ///
226    /// If the Python runtime is not already initialized, this function will initialize it.
227    /// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
228    #[inline]
229    pub fn acquire_gil() -> GILGuard {
230        GILGuard::acquire()
231    }
232
233    /// Temporarily releases the GIL, thus allowing other Python threads to run.
234    pub fn allow_threads<T, F>(self, f: F) -> T
235    where
236        F: Send + FnOnce() -> T,
237    {
238        // The `Send` bound on the closure prevents the user from
239        // transferring the `Python` token into the closure.
240        unsafe {
241            let save = ffi::PyEval_SaveThread();
242            let result = f();
243            ffi::PyEval_RestoreThread(save);
244            result
245        }
246    }
247
248    /// Evaluates a Python expression in the given context and returns the result.
249    ///
250    /// If `globals` is `None`, it defaults to Python module `__main__`.
251    /// If `locals` is `None`, it defaults to the value of `globals`.
252    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    /// Executes one or more Python statements in the given context.
262    ///
263    /// If `globals` is `None`, it defaults to Python module `__main__`.
264    /// If `locals` is `None`, it defaults to the value of `globals`.
265    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    /// Runs code in the given context.
276    /// `start` indicates the type of input expected:
277    /// one of `Py_single_input`, `Py_file_input`, or `Py_eval_input`.
278    ///
279    /// If `globals` is `None`, it defaults to Python module `__main__`.
280    /// If `locals` is `None`, it defaults to the value of `globals`.
281    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    /// Gets the Python builtin value `None`.
317    #[allow(non_snake_case)] // the Python keyword starts with uppercase
318    #[inline]
319    pub fn None(self) -> PyObject {
320        unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) }
321    }
322
323    /// Gets the Python builtin value `True`.
324    #[allow(non_snake_case)] // the Python keyword starts with uppercase
325    #[inline]
326    pub fn True(self) -> PyBool {
327        unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_True()).unchecked_cast_into::<PyBool>() }
328    }
329
330    /// Gets the Python builtin value `False`.
331    #[allow(non_snake_case)] // the Python keyword starts with uppercase
332    #[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    /// Gets the Python builtin value `NotImplemented`.
340    #[allow(non_snake_case)] // the Python keyword starts with uppercase
341    #[inline]
342    pub fn NotImplemented(self) -> PyObject {
343        unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) }
344    }
345
346    /// Gets the Python type object for type T.
347    pub fn get_type<T>(self) -> PyType
348    where
349        T: PythonObjectWithTypeObject,
350    {
351        T::type_object(self)
352    }
353
354    /// Import the Python module with the specified name.
355    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        // Make sure builtin names are accessible
376        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        // Inject our own local namespace
387        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        // Make sure builtin names are still accessible when using a local namespace
395        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}