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}