cpython - Rust (original) (raw)

Expand description

Rust bindings to the Python interpreter.

§Ownership and Lifetimes

In Python, all objects are implicitly reference counted. In rust, we will use the PyObject type to represent a reference to a Python object.

The method clone_ref() (from trait PyClone) can be used to create additional references to the same Python object.

Because all Python objects potentially have multiple owners, the concept of Rust mutability does not apply to Python objects. As a result, this API will allow mutating Python objects even if they are not stored in a mutable Rust variable.

The Python interpreter uses a global interpreter lock (GIL) to ensure thread-safety. This API uses a zero-sized struct Python<'p> as a token to indicate that a function can assume that the GIL is held.

You obtain a Python instance by acquiring the GIL, and have to pass it into all operations that call into the Python runtime.

§Python 2.7

The library will use the python3 bindings by default. To use the python2 bindings you must specific the python27 feature explicitly in your Cargo.toml.

[dependencies.cpython]
version = "*"
default-features = false
features = ["python27-sys"]

§Error Handling

The vast majority of operations in this library will return PyResult<...>. This is an alias for the type Result<..., PyErr>.

A PyErr represents a Python exception. Errors within the rust-cpython library are also exposed as Python exceptions.

§Example

use cpython::{Python, PyDict, PyResult};

fn main() {
    let gil = Python::acquire_gil();
    hello(gil.python()).unwrap();
}

fn hello(py: Python) -> PyResult<()> {
    let sys = py.import("sys")?;
    let version: String = sys.get(py, "version")?.extract(py)?;

    let locals = PyDict::new(py);
    locals.set_item(py, "os", py.import("os")?)?;
    let user: String = py.eval("os.getenv('USER') or os.getenv('USERNAME')", None, Some(&locals))?.extract(py)?;

    println!("Hello {}, I'm Python {}", user, version);
    Ok(())
}