cpython/
lib.rs

1// Copyright (c) 2016 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
19#![cfg_attr(feature="nightly", allow(incomplete_features))]
20#![cfg_attr(feature="nightly", feature(
21    specialization, // for impl FromPyObject<'s> for Vec<...> (#31844)
22))]
23#![allow(
24    unused_imports, // because some imports are only necessary with python 2.x or 3.x
25    clippy::missing_safety_doc,
26    clippy::manual_strip,
27    clippy::match_like_matches_macro
28)]
29
30//! Rust bindings to the Python interpreter.
31//!
32//! # Ownership and Lifetimes
33//! In Python, all objects are implicitly reference counted.
34//! In rust, we will use the `PyObject` type to represent a reference to a Python object.
35//!
36//! The method `clone_ref()` (from trait `PyClone`) can be used to create additional
37//! references to the same Python object.
38//!
39//! Because all Python objects potentially have multiple owners, the
40//! concept of Rust mutability does not apply to Python objects.
41//! As a result, this API will allow mutating Python objects even if they are not stored
42//! in a mutable Rust variable.
43//!
44//! The Python interpreter uses a global interpreter lock (GIL)
45//! to ensure thread-safety.
46//! This API uses a zero-sized `struct Python<'p>` as a token to indicate
47//! that a function can assume that the GIL is held.
48//!
49//! You obtain a `Python` instance by acquiring the GIL,
50//! and have to pass it into all operations that call into the Python runtime.
51//!
52//! # Python 2.7
53//! The library will use the python3 bindings by default. To use the python2 bindings
54//! you must specific the `python27` feature explicitly in your `Cargo.toml`.
55//!
56//! ```ignore
57//! [dependencies.cpython]
58//! version = "*"
59//! default-features = false
60//! features = ["python27-sys"]
61//! ```
62//!
63//! # Error Handling
64//! The vast majority of operations in this library will return `PyResult<...>`.
65//! This is an alias for the type `Result<..., PyErr>`.
66//!
67//! A `PyErr` represents a Python exception. Errors within the rust-cpython library are
68//! also exposed as Python exceptions.
69//!
70//! # Example
71//! ```
72//! use cpython::{Python, PyDict, PyResult};
73//!
74//! fn main() {
75//!     let gil = Python::acquire_gil();
76//!     hello(gil.python()).unwrap();
77//! }
78//!
79//! fn hello(py: Python) -> PyResult<()> {
80//!     let sys = py.import("sys")?;
81//!     let version: String = sys.get(py, "version")?.extract(py)?;
82//!
83//!     let locals = PyDict::new(py);
84//!     locals.set_item(py, "os", py.import("os")?)?;
85//!     let user: String = py.eval("os.getenv('USER') or os.getenv('USERNAME')", None, Some(&locals))?.extract(py)?;
86//!
87//!     println!("Hello {}, I'm Python {}", user, version);
88//!     Ok(())
89//! }
90//! ```
91
92use std::{mem, ptr};
93
94#[cfg(feature = "python27-sys")]
95pub(crate) use python27_sys as ffi;
96
97#[cfg(feature = "python3-sys")]
98pub(crate) use python3_sys as ffi;
99
100pub use ffi::Py_ssize_t;
101
102pub use crate::conversion::{FromPyObject, RefFromPyObject, ToPyObject};
103pub use crate::err::{PyErr, PyResult};
104pub use crate::objectprotocol::{ObjectProtocol, NumberProtocol};
105pub use crate::objects::*;
106pub use crate::py_class::CompareOp;
107pub use crate::python::{
108    PyClone, PyDrop, Python, PythonObject, PythonObjectDowncastError,
109    PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject,
110};
111pub use crate::pythonrun::{prepare_freethreaded_python, GILGuard, GILProtected};
112pub use crate::sharedref::{
113    PyLeakedRef, PyLeakedRefMut, PySharedRef, PySharedRefCell, UnsafePyLeaked,
114};
115
116#[cfg(feature = "python27-sys")]
117#[allow(non_camel_case_types)]
118pub type Py_hash_t = libc::c_long;
119
120#[cfg(feature = "python3-sys")]
121#[allow(non_camel_case_types)]
122pub type Py_hash_t = ffi::Py_hash_t;
123
124/// Constructs a `&'static CStr` literal.
125macro_rules! cstr(
126    ($s: tt) => (
127        // TODO: verify that $s is a string literal without nuls
128        unsafe {
129            std::ffi::CStr::from_ptr(concat!($s, "\0").as_ptr() as *const _)
130        }
131    );
132);
133
134// AST coercion macros (https://danielkeep.github.io/tlborm/book/blk-ast-coercion.html)
135#[macro_export]
136#[doc(hidden)]
137macro_rules! py_coerce_expr {
138    ($s:expr) => {
139        $s
140    };
141}
142#[macro_export]
143#[doc(hidden)]
144macro_rules! py_coerce_item {
145    ($s:item) => {
146        $s
147    };
148}
149
150#[macro_export]
151#[doc(hidden)]
152macro_rules! py_replace_expr {
153    ($_t:tt $sub:expr) => {
154        $sub
155    };
156}
157
158#[macro_export]
159#[doc(hidden)]
160macro_rules! py_impl_to_py_object_for_python_object {
161    ($T: ty) => {
162        /// Identity conversion: allows using existing `PyObject` instances where
163        /// `T: ToPyObject` is expected.
164        impl $crate::ToPyObject for $T {
165            type ObjectType = $T;
166
167            #[inline]
168            fn to_py_object(&self, py: $crate::Python) -> $T {
169                $crate::PyClone::clone_ref(self, py)
170            }
171
172            #[inline]
173            fn into_py_object(self, _py: $crate::Python) -> $T {
174                self
175            }
176
177            #[inline]
178            fn with_borrowed_ptr<F, R>(&self, _py: $crate::Python, f: F) -> R
179            where
180                F: FnOnce(*mut $crate::_detail::ffi::PyObject) -> R,
181            {
182                f($crate::PythonObject::as_object(self).as_ptr())
183            }
184        }
185    };
186}
187
188#[macro_export]
189#[doc(hidden)]
190macro_rules! py_impl_from_py_object_for_python_object {
191    ($T:ty) => {
192        impl<'s> $crate::FromPyObject<'s> for $T {
193            #[inline]
194            fn extract(py: $crate::Python, obj: &'s $crate::PyObject) -> $crate::PyResult<$T> {
195                use $crate::PyClone;
196                Ok(obj.clone_ref(py).cast_into::<$T>(py)?)
197            }
198        }
199
200        impl<'s> $crate::FromPyObject<'s> for &'s $T {
201            #[inline]
202            fn extract(py: $crate::Python, obj: &'s $crate::PyObject) -> $crate::PyResult<&'s $T> {
203                Ok(obj.cast_as::<$T>(py)?)
204            }
205        }
206    };
207}
208
209pub mod argparse;
210pub mod buffer;
211mod conversion;
212mod err;
213mod function;
214mod objectprotocol;
215mod objects;
216mod python;
217mod pythonrun;
218//pub mod rustobject;
219pub mod py_class;
220mod sharedref;
221
222#[cfg(feature = "serde-convert")]
223pub mod serde;
224
225/// Private re-exports for macros. Do not use.
226#[doc(hidden)]
227pub mod _detail {
228    pub mod ffi {
229        pub use crate::ffi::*;
230    }
231    pub mod libc {
232        pub use libc::{c_char, c_int, c_void};
233    }
234    pub use crate::err::{from_owned_ptr_or_panic, result_from_owned_ptr};
235    pub use crate::function::{
236        handle_callback, py_fn_impl, AbortOnDrop, PyObjectCallbackConverter,
237        PythonObjectCallbackConverter,
238    };
239    pub use paste;
240    pub use std::result::Result;
241}
242
243/// Expands to an `extern "C"` function that allows Python to load
244/// the rust code as a Python extension module.
245///
246/// Macro syntax: `py_module_initializer!($name, |$py, $m| $body)`
247///
248/// 1. `name`: The module name as a Rust identifier.
249/// 2. A lambda of type `Fn(Python, &PyModule) -> PyResult<()>`.
250///    This function will be called when the module is imported, and is responsible
251///    for adding the module's members.
252///
253/// For backwards compatibilty with older versions of rust-cpython,
254/// two additional name identifiers (for py2 and py3 initializer names)
255/// can be provided, but they will be ignored.
256///
257/// # Example
258/// ```
259/// use cpython::{Python, PyResult, PyObject, py_module_initializer, py_fn};
260///
261/// py_module_initializer!(hello, |py, m| {
262///     m.add(py, "__doc__", "Module documentation string")?;
263///     m.add(py, "run", py_fn!(py, run()))?;
264///     Ok(())
265/// });
266///
267/// fn run(py: Python) -> PyResult<PyObject> {
268///     println!("Rust says: Hello Python!");
269///     Ok(py.None())
270/// }
271/// # fn main() {}
272/// ```
273///
274/// In your `Cargo.toml`, use the `extension-module` feature for the `cpython` dependency:
275/// ```cargo
276/// [dependencies.cpython]
277/// version = "*"
278/// features = ["extension-module"]
279/// ```
280/// The full example project can be found at:
281///   https://github.com/dgrunwald/rust-cpython/tree/master/extensions/hello
282///
283/// Rust will compile the code into a file named `libhello.so`, but we have to
284/// rename the file in order to use it with Python:
285///
286/// ```bash
287/// cp ./target/debug/libhello.so ./hello.so
288/// ```
289/// (Note: on Mac OS you will have to rename `libhello.dynlib` to `hello.so`)
290///
291/// The extension module can then be imported into Python:
292///
293/// ```python
294/// >>> import hello
295/// >>> hello.run()
296/// Rust says: Hello Python!
297/// ```
298///
299#[macro_export]
300#[cfg(feature = "python27-sys")]
301macro_rules! py_module_initializer {
302    ($name: ident, $( $_py2: ident, $_py3: ident, )? |$py_id: ident, $m_id: ident| $body: tt) => {
303        $crate::_detail::paste::item! {
304            #[no_mangle]
305            #[allow(non_snake_case)]
306            pub unsafe extern "C" fn [< init $name >]() {
307                // Nest init function so that $body isn't in unsafe context
308                fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
309                    $body
310                }
311                let name = concat!(stringify!($name), "\0").as_ptr() as *const _;
312                $crate::py_module_initializer_impl(name, init)
313            }
314        }
315    };
316}
317
318#[doc(hidden)]
319#[cfg(feature = "python27-sys")]
320pub unsafe fn py_module_initializer_impl(
321    name: *const libc::c_char,
322    init: fn(Python, &PyModule) -> PyResult<()>,
323) {
324    let guard = function::AbortOnDrop("py_module_initializer");
325    let py = Python::assume_gil_acquired();
326    ffi::PyEval_InitThreads();
327    let module = ffi::Py_InitModule(name, ptr::null_mut());
328    if module.is_null() {
329        mem::forget(guard);
330        return;
331    }
332
333    let module = match PyObject::from_borrowed_ptr(py, module).cast_into::<PyModule>(py) {
334        Ok(m) => m,
335        Err(e) => {
336            PyErr::from(e).restore(py);
337            mem::forget(guard);
338            return;
339        }
340    };
341    let ret = match init(py, &module) {
342        Ok(()) => (),
343        Err(e) => e.restore(py),
344    };
345    mem::forget(guard);
346    ret
347}
348
349#[macro_export]
350#[cfg(feature = "python3-sys")]
351macro_rules! py_module_initializer {
352    ($name: ident, $( $_py2: ident, $_py3: ident, )? |$py_id: ident, $m_id: ident| $body: tt) => {
353        $crate::_detail::paste::item! {
354            #[no_mangle]
355            #[allow(non_snake_case)]
356            pub unsafe extern "C" fn [< PyInit_ $name >]() -> *mut $crate::_detail::ffi::PyObject {
357                // Nest init function so that $body isn't in unsafe context
358                fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
359                    $body
360                }
361                static mut MODULE_DEF: $crate::_detail::ffi::PyModuleDef =
362                    $crate::_detail::ffi::PyModuleDef_INIT;
363                // We can't convert &'static str to *const c_char within a static initializer,
364                // so we'll do it here in the module initialization:
365                MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _;
366                $crate::py_module_initializer_impl(&mut MODULE_DEF, init)
367            }
368        }
369    };
370}
371
372#[doc(hidden)]
373#[cfg(feature = "python3-sys")]
374pub unsafe fn py_module_initializer_impl(
375    def: *mut ffi::PyModuleDef,
376    init: fn(Python, &PyModule) -> PyResult<()>,
377) -> *mut ffi::PyObject {
378    let guard = function::AbortOnDrop("py_module_initializer");
379    let py = Python::assume_gil_acquired();
380    ffi::PyEval_InitThreads();
381    let module = ffi::PyModule_Create(def);
382    if module.is_null() {
383        mem::forget(guard);
384        return module;
385    }
386
387    let module = match PyObject::from_owned_ptr(py, module).cast_into::<PyModule>(py) {
388        Ok(m) => m,
389        Err(e) => {
390            PyErr::from(e).restore(py);
391            mem::forget(guard);
392            return ptr::null_mut();
393        }
394    };
395    let ret = match init(py, &module) {
396        Ok(()) => module.into_object().steal_ptr(),
397        Err(e) => {
398            e.restore(py);
399            ptr::null_mut()
400        }
401    };
402    mem::forget(guard);
403    ret
404}
405
406// Strip 'r#' prefix from stringified raw identifiers.
407#[macro_export]
408#[doc(hidden)]
409macro_rules! strip_raw {
410    ($s:expr) => {{
411        let s = $s;
412        if s.starts_with("r#") {
413            &s[2..]
414        } else {
415            s
416        }
417    }};
418}