pyo3/src/python.rs

273 lines
10 KiB
Rust
Raw Normal View History

2015-04-19 03:22:03 +00:00
// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2015-01-05 16:05:53 +00:00
use std;
use std::ffi::CString;
2015-03-08 14:29:44 +00:00
use std::marker::PhantomData;
use libc::c_int;
2015-01-05 16:05:53 +00:00
use ffi;
use objects::{PyObject, PyType, PyBool, PyDict, PyModule};
use err::{self, PyErr, PyResult};
use pythonrun::GILGuard;
2015-03-08 14:29:44 +00:00
2015-04-18 22:39:04 +00:00
/// Marker type that indicates that the GIL is currently held.
///
2015-06-27 20:45:35 +00:00
/// The 'Python' struct is a zero-size marker struct that is required for most Python operations.
/// This is used to indicate that the operation accesses/modifies the Python interpreter state,
/// and thus can only be called if the Python interpreter is initialized and the
/// Python global interpreter lock (GIL) is acquired.
/// The lifetime `'p` represents the lifetime of the Python interpreter.
2015-04-18 22:39:04 +00:00
///
/// You can imagine the GIL to be a giant `Mutex<PythonInterpreterState>`.
/// The type `Python<'p>` then acts like a reference `&'p PythonInterpreterState`.
#[derive(Copy, Clone)]
2015-06-27 20:45:35 +00:00
pub struct Python<'p>(PhantomData<&'p GILGuard>);
2015-01-05 16:05:53 +00:00
2015-06-27 20:45:35 +00:00
/// This trait allows retrieving the underlying FFI pointer from Python objects.
pub trait ToPythonPointer {
/// Retrieves the underlying FFI pointer (as a borrowed pointer).
fn as_ptr(&self) -> *mut ffi::PyObject;
2015-06-27 20:45:35 +00:00
2015-04-18 22:39:04 +00:00
/// Retrieves the underlying FFI pointer as a "stolen pointer".
fn steal_ptr(self) -> *mut ffi::PyObject;
}
2015-06-27 20:45:35 +00:00
/// Trait implemented by all Python object types.
pub trait PythonObject<'p> : 'p + Clone + ::conversion::ToPyObject<'p> {
2015-06-27 20:45:35 +00:00
/// Casts the Python object to PyObject.
fn as_object(&self) -> &PyObject<'p>;
2015-06-27 17:08:28 +00:00
2015-06-27 20:45:35 +00:00
/// Casts the Python object to PyObject.
fn into_object(self) -> PyObject<'p>;
/// Unchecked downcast from PyObject to Self.
2015-01-05 15:34:12 +00:00
/// Undefined behavior if the input object does not have the expected type.
unsafe fn unchecked_downcast_from(PyObject<'p>) -> Self;
2015-01-05 15:34:12 +00:00
/// Unchecked downcast from PyObject to Self.
/// Undefined behavior if the input object does not have the expected type.
unsafe fn unchecked_downcast_borrow_from<'a>(&'a PyObject<'p>) -> &'a Self;
2015-06-27 20:45:35 +00:00
/// Retrieve Python instance from an existing Python object.
#[inline]
fn python(&self) -> Python<'p> {
self.as_object().python()
}
}
2015-04-18 22:39:04 +00:00
// Marker type that indicates an error while downcasting
pub struct PythonObjectDowncastError<'p>(pub Python<'p>);
2015-06-27 20:45:35 +00:00
/// Trait implemented by Python object types that allow a checked downcast.
2015-01-05 15:34:12 +00:00
pub trait PythonObjectWithCheckedDowncast<'p> : PythonObject<'p> {
2015-06-27 20:45:35 +00:00
/// Cast from PyObject to a concrete Python object type.
fn downcast_from(PyObject<'p>) -> Result<Self, PythonObjectDowncastError<'p>>;
2015-06-27 20:45:35 +00:00
/// Cast from PyObject to a concrete Python object type.
fn downcast_borrow_from<'a>(&'a PyObject<'p>) -> Result<&'a Self, PythonObjectDowncastError<'p>>;
2015-01-05 15:34:12 +00:00
}
2015-06-27 20:45:35 +00:00
/// Trait implemented by Python object types that have a corresponding type object.
2015-01-05 15:34:12 +00:00
pub trait PythonObjectWithTypeObject<'p> : PythonObjectWithCheckedDowncast<'p> {
2015-06-27 20:45:35 +00:00
/// Retrieves the type object for this Python object type.
fn type_object(Python<'p>) -> PyType<'p>;
}
2015-06-27 20:45:35 +00:00
/// ToPythonPointer for borrowed Python pointers.
2015-01-11 03:21:05 +00:00
impl <'a, 'p, T> ToPythonPointer for &'a T where T: PythonObject<'p> {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
2015-06-27 17:08:28 +00:00
self.as_object().as_ptr()
2015-01-11 03:21:05 +00:00
}
#[inline]
fn steal_ptr(self) -> *mut ffi::PyObject {
2015-06-27 17:08:28 +00:00
self.as_object().clone().steal_ptr()
2015-01-11 03:21:05 +00:00
}
}
/// Convert None into a null pointer.
impl <T> ToPythonPointer for Option<T> where T: ToPythonPointer {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
match *self {
Some(ref t) => t.as_ptr(),
None => std::ptr::null_mut()
}
}
#[inline]
fn steal_ptr(self) -> *mut ffi::PyObject {
match self {
Some(t) => t.steal_ptr(),
None => std::ptr::null_mut()
}
}
}
2015-01-05 16:05:53 +00:00
impl<'p> Python<'p> {
2015-06-27 20:45:35 +00:00
/// Retrieve Python instance under the assumption that the GIL is already acquired at this point,
/// and stays acquired for the lifetime `'p`.
///
/// Because the output lifetime `'p` is not connected to any input parameter,
/// care must be taken that the compiler infers an appropriate lifetime for `'p`
/// when calling this function.
#[inline]
2015-01-05 16:05:53 +00:00
pub unsafe fn assume_gil_acquired() -> Python<'p> {
2015-03-08 14:29:44 +00:00
Python(PhantomData)
2015-01-05 16:05:53 +00:00
}
2015-06-27 20:45:35 +00:00
/// Acquires the global interpreter lock, which allows access to the Python runtime.
2015-06-27 20:45:35 +00:00
///
/// If the Python runtime is not already initialized, this function will initialize it.
/// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
#[inline]
pub fn acquire_gil() -> GILGuard {
GILGuard::acquire()
}
2015-06-27 20:45:35 +00:00
/// Releases the GIL and allows the use of Python on other threads.
/// Unsafe because we do not ensure that existing references to Python objects
/// are not accessed within the closure.
pub unsafe fn allow_threads<T, F>(self, f: F) -> T where F : FnOnce() -> T {
// TODO: we should use a type with destructor to be panic-safe, and avoid the unnecessary closure
let save = ffi::PyEval_SaveThread();
let result = f();
ffi::PyEval_RestoreThread(save);
result
}
2015-06-27 20:45:35 +00:00
/// Evaluates a Python expression in the given context and returns the result.
///
/// If `globals` is `None`, it defaults to Python module `__main__`.
/// If `locals` is `None`, it defaults to the value of `globals`.
pub fn eval(self, code: &str, globals: Option<&PyDict<'p>>,
locals: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>> {
self.run_code(code, ffi::Py_eval_input, globals, locals)
}
/// Executes one or more Python statements in the given context.
///
/// If `globals` is `None`, it defaults to Python module `__main__`.
/// If `locals` is `None`, it defaults to the value of `globals`.
pub fn run(self, code: &str, globals: Option<&PyDict<'p>>,
locals: Option<&PyDict<'p>>) -> PyResult<'p, ()> {
try!(self.run_code(code, ffi::Py_file_input, globals, locals));
Ok(())
}
/// Runs code in the given context.
/// `start` indicates the type of input expected:
/// one of `Py_single_input`, `Py_file_input`, or `Py_eval_input`.
///
/// If `globals` is `None`, it defaults to Python module `__main__`.
/// If `locals` is `None`, it defaults to the value of `globals`.
fn run_code(self, code: &str, start: c_int,
globals: Option<&PyDict<'p>>, locals: Option<&PyDict<'p>>)
-> PyResult<'p, PyObject<'p>> {
let code = CString::new(code).unwrap();
unsafe {
let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _);
if mptr.is_null() {
return Err(PyErr::fetch(self));
}
let mdict = ffi::PyModule_GetDict(mptr);
let globals = match globals {
Some(g) => g.as_ptr(),
None => mdict,
};
let locals = match locals {
Some(l) => l.as_ptr(),
None => globals
};
let res_ptr = ffi::PyRun_StringFlags(code.as_ptr(),
start, globals, locals, 0 as *mut _);
err::result_from_owned_ptr(self, res_ptr)
}
}
2015-06-27 20:45:35 +00:00
/// Gets the Python builtin value `None`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
2015-01-05 16:05:53 +00:00
#[inline]
pub fn None(self) -> PyObject<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) }
2015-01-05 16:05:53 +00:00
}
2015-06-27 20:45:35 +00:00
/// Gets the Python builtin value `True`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
2015-01-05 16:05:53 +00:00
#[inline]
pub fn True(self) -> PyBool<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_True()).unchecked_cast_into::<PyBool>() }
2015-01-05 16:05:53 +00:00
}
2015-06-27 20:45:35 +00:00
/// Gets the Python builtin value `False`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
2015-01-05 16:05:53 +00:00
#[inline]
pub fn False(self) -> PyBool<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_False()).unchecked_cast_into::<PyBool>() }
2015-01-05 16:05:53 +00:00
}
2015-04-18 20:20:19 +00:00
2015-06-27 20:45:35 +00:00
/// Gets the Python type object for type T.
pub fn get_type<T>(self) -> PyType<'p> where T: PythonObjectWithTypeObject<'p> {
T::type_object(self)
2015-01-04 04:50:28 +00:00
}
2015-01-11 17:56:59 +00:00
2015-06-27 20:45:35 +00:00
/// Import the Python module with the specified name.
2015-04-18 20:20:19 +00:00
pub fn import(self, name : &str) -> PyResult<'p, PyModule<'p>> {
2015-01-11 17:56:59 +00:00
PyModule::import(self, name)
}
2015-01-04 04:50:28 +00:00
}
2015-01-05 16:05:53 +00:00
2015-04-18 22:39:04 +00:00
impl <'p> std::fmt::Debug for PythonObjectDowncastError<'p> {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.write_str("PythonObjectDowncastError")
}
}
#[cfg(test)]
mod test {
use {Python, PyDict};
#[test]
fn test_eval() {
let gil = Python::acquire_gil();
let py = gil.python();
// Make sure builtin names are accessible
let v: i32 = py.eval("min(1, 2)", None, None).unwrap().extract().unwrap();
assert_eq!(v, 1);
let d = PyDict::new(py);
d.set_item("foo", 13).unwrap();
// Inject our own local namespace
let v: i32 = py.eval("foo + 29", None, Some(&d)).unwrap().extract().unwrap();
assert_eq!(v, 42);
}
}