pyo3_ffi/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
2//! Raw FFI declarations for Python's C API.
3//!
4//! PyO3 can be used to write native Python modules or run Python code and modules from Rust.
5//!
6//! This crate just provides low level bindings to the Python interpreter.
7//! It is meant for advanced users only - regular PyO3 users shouldn't
8//! need to interact with this crate at all.
9//!
10//! The contents of this crate are not documented here, as it would entail
11//! basically copying the documentation from CPython. Consult the [Python/C API Reference
12//! Manual][capi] for up-to-date documentation.
13//!
14//! # Safety
15//!
16//! The functions in this crate lack individual safety documentation, but
17//! generally the following apply:
18//! - Pointer arguments have to point to a valid Python object of the correct type,
19//! although null pointers are sometimes valid input.
20//! - The vast majority can only be used safely while the GIL is held.
21//! - Some functions have additional safety requirements, consult the
22//! [Python/C API Reference Manual][capi]
23//! for more information.
24//!
25//!
26//! # Feature flags
27//!
28//! PyO3 uses [feature flags] to enable you to opt-in to additional functionality. For a detailed
29//! description, see the [Features chapter of the guide].
30//!
31//! ## Optional feature flags
32//!
33//! The following features customize PyO3's behavior:
34//!
35//! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by
36//! [PEP 384] to be forward-compatible with future Python versions.
37//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that
38//! your module can also be used with statically linked Python interpreters. Use this feature when
39//! building an extension module.
40//!
41//! ## `rustc` environment flags
42//!
43//! PyO3 uses `rustc`'s `--cfg` flags to enable or disable code used for different Python versions.
44//! If you want to do this for your own crate, you can do so with the [`pyo3-build-config`] crate.
45//!
46//! - `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when
47//! compiling for a given minimum Python version.
48//! - `Py_LIMITED_API`: Marks code enabled when the `abi3` feature flag is enabled.
49//! - `PyPy` - Marks code enabled when compiling for PyPy.
50//!
51//! # Minimum supported Rust and Python versions
52//!
53//! PyO3 supports the following software versions:
54//! - Python 3.7 and up (CPython and PyPy)
55//! - Rust 1.63 and up
56//!
57//! # Example: Building Python Native modules
58//!
59//! PyO3 can be used to generate a native Python module. The easiest way to try this out for the
60//! first time is to use [`maturin`]. `maturin` is a tool for building and publishing Rust-based
61//! Python packages with minimal configuration. The following steps set up some files for an example
62//! Python module, install `maturin`, and then show how to build and import the Python module.
63//!
64//! First, create a new folder (let's call it `string_sum`) containing the following two files:
65//!
66//! **`Cargo.toml`**
67//!
68//! ```toml
69//! [lib]
70//! name = "string_sum"
71//! # "cdylib" is necessary to produce a shared library for Python to import from.
72//! #
73//! # Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
74//! # to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
75//! # crate-type = ["cdylib", "rlib"]
76//! crate-type = ["cdylib"]
77//!
78//! [dependencies.pyo3-ffi]
79#![doc = concat!("version = \"", env!("CARGO_PKG_VERSION"), "\"")]
80//! features = ["extension-module"]
81//! ```
82//!
83//! **`src/lib.rs`**
84//! ```rust
85//! use std::os::raw::c_char;
86//! use std::ptr;
87//!
88//! use pyo3_ffi::*;
89//!
90//! static mut MODULE_DEF: PyModuleDef = PyModuleDef {
91//! m_base: PyModuleDef_HEAD_INIT,
92//! m_name: c_str!("string_sum").as_ptr(),
93//! m_doc: c_str!("A Python module written in Rust.").as_ptr(),
94//! m_size: 0,
95//! m_methods: unsafe { METHODS.as_mut_ptr().cast() },
96//! m_slots: std::ptr::null_mut(),
97//! m_traverse: None,
98//! m_clear: None,
99//! m_free: None,
100//! };
101//!
102//! static mut METHODS: [PyMethodDef; 2] = [
103//! PyMethodDef {
104//! ml_name: c_str!("sum_as_string").as_ptr(),
105//! ml_meth: PyMethodDefPointer {
106//! _PyCFunctionFast: sum_as_string,
107//! },
108//! ml_flags: METH_FASTCALL,
109//! ml_doc: c_str!("returns the sum of two integers as a string").as_ptr(),
110//! },
111//! // A zeroed PyMethodDef to mark the end of the array.
112//! PyMethodDef::zeroed()
113//! ];
114//!
115//! // The module initialization function, which must be named `PyInit_<your_module>`.
116//! #[allow(non_snake_case)]
117//! #[no_mangle]
118//! pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
119//! PyModule_Create(ptr::addr_of_mut!(MODULE_DEF))
120//! }
121//!
122//! pub unsafe extern "C" fn sum_as_string(
123//! _self: *mut PyObject,
124//! args: *mut *mut PyObject,
125//! nargs: Py_ssize_t,
126//! ) -> *mut PyObject {
127//! if nargs != 2 {
128//! PyErr_SetString(
129//! PyExc_TypeError,
130//! c_str!("sum_as_string() expected 2 positional arguments").as_ptr(),
131//! );
132//! return std::ptr::null_mut();
133//! }
134//!
135//! let arg1 = *args;
136//! if PyLong_Check(arg1) == 0 {
137//! PyErr_SetString(
138//! PyExc_TypeError,
139//! c_str!("sum_as_string() expected an int for positional argument 1").as_ptr(),
140//! );
141//! return std::ptr::null_mut();
142//! }
143//!
144//! let arg1 = PyLong_AsLong(arg1);
145//! if !PyErr_Occurred().is_null() {
146//! return ptr::null_mut();
147//! }
148//!
149//! let arg2 = *args.add(1);
150//! if PyLong_Check(arg2) == 0 {
151//! PyErr_SetString(
152//! PyExc_TypeError,
153//! c_str!("sum_as_string() expected an int for positional argument 2").as_ptr(),
154//! );
155//! return std::ptr::null_mut();
156//! }
157//!
158//! let arg2 = PyLong_AsLong(arg2);
159//! if !PyErr_Occurred().is_null() {
160//! return ptr::null_mut();
161//! }
162//!
163//! match arg1.checked_add(arg2) {
164//! Some(sum) => {
165//! let string = sum.to_string();
166//! PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)
167//! }
168//! None => {
169//! PyErr_SetString(
170//! PyExc_OverflowError,
171//! c_str!("arguments too large to add").as_ptr(),
172//! );
173//! std::ptr::null_mut()
174//! }
175//! }
176//! }
177//! ```
178//!
179//! With those two files in place, now `maturin` needs to be installed. This can be done using
180//! Python's package manager `pip`. First, load up a new Python `virtualenv`, and install `maturin`
181//! into it:
182//! ```bash
183//! $ cd string_sum
184//! $ python -m venv .env
185//! $ source .env/bin/activate
186//! $ pip install maturin
187//! ```
188//!
189//! Now build and execute the module:
190//! ```bash
191//! $ maturin develop
192//! # lots of progress output as maturin runs the compilation...
193//! $ python
194//! >>> import string_sum
195//! >>> string_sum.sum_as_string(5, 20)
196//! '25'
197//! ```
198//!
199//! As well as with `maturin`, it is possible to build using [setuptools-rust] or
200//! [manually][manual_builds]. Both offer more flexibility than `maturin` but require further
201//! configuration.
202//!
203//!
204//! # Using Python from Rust
205//!
206//! To embed Python into a Rust binary, you need to ensure that your Python installation contains a
207//! shared library. The following steps demonstrate how to ensure this (for Ubuntu).
208//!
209//! To install the Python shared library on Ubuntu:
210//! ```bash
211//! sudo apt install python3-dev
212//! ```
213//!
214//! While most projects use the safe wrapper provided by pyo3,
215//! you can take a look at the [`orjson`] library as an example on how to use `pyo3-ffi` directly.
216//! For those well versed in C and Rust the [tutorials] from the CPython documentation
217//! can be easily converted to rust as well.
218//!
219//! [tutorials]: https://docs.python.org/3/extending/
220//! [`orjson`]: https://github.com/ijl/orjson
221//! [capi]: https://docs.python.org/3/c-api/index.html
222//! [`maturin`]: https://github.com/PyO3/maturin "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages"
223//! [`pyo3-build-config`]: https://docs.rs/pyo3-build-config
224//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html "Features - The Cargo Book"
225#![doc = concat!("[manual_builds]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/building-and-distribution.html#manual-builds \"Manual builds - Building and Distribution - PyO3 user guide\"")]
226//! [setuptools-rust]: https://github.com/PyO3/setuptools-rust "Setuptools plugin for Rust extensions"
227//! [PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI"
228#![doc = concat!("[Features chapter of the guide]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#features-reference \"Features Reference - PyO3 user guide\"")]
229#![allow(
230 missing_docs,
231 non_camel_case_types,
232 non_snake_case,
233 non_upper_case_globals,
234 clippy::upper_case_acronyms,
235 clippy::missing_safety_doc
236)]
237#![warn(elided_lifetimes_in_paths, unused_lifetimes)]
238
239// Until `extern type` is stabilized, use the recommended approach to
240// model opaque types:
241// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
242macro_rules! opaque_struct {
243 ($name:ident) => {
244 #[repr(C)]
245 pub struct $name([u8; 0]);
246 };
247}
248
249/// This is a helper macro to create a `&'static CStr`.
250///
251/// It can be used on all Rust versions supported by PyO3, unlike c"" literals which
252/// were stabilised in Rust 1.77.
253///
254/// Due to the nature of PyO3 making heavy use of C FFI interop with Python, it is
255/// common for PyO3 to use CStr.
256///
257/// Examples:
258///
259/// ```rust
260/// use std::ffi::CStr;
261///
262/// const HELLO: &CStr = pyo3_ffi::c_str!("hello");
263/// static WORLD: &CStr = pyo3_ffi::c_str!("world");
264/// ```
265#[macro_export]
266macro_rules! c_str {
267 ($s:expr) => {
268 $crate::_cstr_from_utf8_with_nul_checked(concat!($s, "\0"))
269 };
270}
271
272/// Private helper for `c_str!` macro.
273#[doc(hidden)]
274pub const fn _cstr_from_utf8_with_nul_checked(s: &str) -> &CStr {
275 // TODO: Replace this implementation with `CStr::from_bytes_with_nul` when MSRV above 1.72.
276 let bytes = s.as_bytes();
277 let len = bytes.len();
278 assert!(
279 !bytes.is_empty() && bytes[bytes.len() - 1] == b'\0',
280 "string is not nul-terminated"
281 );
282 let mut i = 0;
283 let non_null_len = len - 1;
284 while i < non_null_len {
285 assert!(bytes[i] != b'\0', "string contains null bytes");
286 i += 1;
287 }
288
289 unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
290}
291
292use std::ffi::CStr;
293
294pub mod compat;
295
296pub use self::abstract_::*;
297pub use self::bltinmodule::*;
298pub use self::boolobject::*;
299pub use self::bytearrayobject::*;
300pub use self::bytesobject::*;
301pub use self::ceval::*;
302#[cfg(Py_LIMITED_API)]
303pub use self::code::*;
304pub use self::codecs::*;
305pub use self::compile::*;
306pub use self::complexobject::*;
307#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
308pub use self::context::*;
309#[cfg(not(Py_LIMITED_API))]
310pub use self::datetime::*;
311pub use self::descrobject::*;
312pub use self::dictobject::*;
313pub use self::enumobject::*;
314pub use self::fileobject::*;
315pub use self::fileutils::*;
316pub use self::floatobject::*;
317pub use self::import::*;
318pub use self::intrcheck::*;
319pub use self::iterobject::*;
320pub use self::listobject::*;
321pub use self::longobject::*;
322#[cfg(not(Py_LIMITED_API))]
323pub use self::marshal::*;
324pub use self::memoryobject::*;
325pub use self::methodobject::*;
326pub use self::modsupport::*;
327pub use self::moduleobject::*;
328pub use self::object::*;
329pub use self::objimpl::*;
330pub use self::osmodule::*;
331#[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))]
332pub use self::pyarena::*;
333#[cfg(Py_3_11)]
334pub use self::pybuffer::*;
335pub use self::pycapsule::*;
336pub use self::pyerrors::*;
337pub use self::pyframe::*;
338pub use self::pyhash::*;
339pub use self::pylifecycle::*;
340pub use self::pymem::*;
341pub use self::pyport::*;
342pub use self::pystate::*;
343pub use self::pystrtod::*;
344pub use self::pythonrun::*;
345pub use self::rangeobject::*;
346pub use self::setobject::*;
347pub use self::sliceobject::*;
348pub use self::structseq::*;
349pub use self::sysmodule::*;
350pub use self::traceback::*;
351pub use self::tupleobject::*;
352pub use self::typeslots::*;
353pub use self::unicodeobject::*;
354pub use self::warnings::*;
355pub use self::weakrefobject::*;
356
357mod abstract_;
358// skipped asdl.h
359// skipped ast.h
360mod bltinmodule;
361mod boolobject;
362mod bytearrayobject;
363mod bytesobject;
364// skipped cellobject.h
365mod ceval;
366// skipped classobject.h
367#[cfg(Py_LIMITED_API)]
368mod code;
369mod codecs;
370mod compile;
371mod complexobject;
372#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
373mod context; // It's actually 3.7.1, but no cfg for patches.
374#[cfg(not(Py_LIMITED_API))]
375pub(crate) mod datetime;
376mod descrobject;
377mod dictobject;
378// skipped dynamic_annotations.h
379mod enumobject;
380// skipped errcode.h
381// skipped exports.h
382mod fileobject;
383mod fileutils;
384mod floatobject;
385// skipped empty frameobject.h
386// skipped genericaliasobject.h
387mod import;
388// skipped interpreteridobject.h
389mod intrcheck;
390mod iterobject;
391mod listobject;
392// skipped longintrepr.h
393mod longobject;
394#[cfg(not(Py_LIMITED_API))]
395pub mod marshal;
396mod memoryobject;
397mod methodobject;
398mod modsupport;
399mod moduleobject;
400// skipped namespaceobject.h
401mod object;
402mod objimpl;
403// skipped odictobject.h
404// skipped opcode.h
405// skipped osdefs.h
406mod osmodule;
407// skipped parser_interface.h
408// skipped patchlevel.h
409// skipped picklebufobject.h
410// skipped pyctype.h
411// skipped py_curses.h
412#[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))]
413mod pyarena;
414#[cfg(Py_3_11)]
415mod pybuffer;
416mod pycapsule;
417// skipped pydtrace.h
418mod pyerrors;
419// skipped pyexpat.h
420// skipped pyfpe.h
421mod pyframe;
422mod pyhash;
423mod pylifecycle;
424// skipped pymacconfig.h
425// skipped pymacro.h
426// skipped pymath.h
427mod pymem;
428mod pyport;
429mod pystate;
430// skipped pystats.h
431mod pythonrun;
432// skipped pystrhex.h
433// skipped pystrcmp.h
434mod pystrtod;
435// skipped pythread.h
436// skipped pytime.h
437mod rangeobject;
438mod setobject;
439mod sliceobject;
440mod structseq;
441mod sysmodule;
442mod traceback;
443// skipped tracemalloc.h
444mod tupleobject;
445mod typeslots;
446mod unicodeobject;
447mod warnings;
448mod weakrefobject;
449
450// Additional headers that are not exported by Python.h
451#[deprecated(note = "Python 3.12")]
452pub mod structmember;
453
454// "Limited API" definitions matching Python's `include/cpython` directory.
455#[cfg(not(Py_LIMITED_API))]
456mod cpython;
457
458#[cfg(not(Py_LIMITED_API))]
459pub use self::cpython::*;