Update documentation

This commit is contained in:
Daniel Grunwald 2015-04-19 00:39:04 +02:00
parent 5d3d03bcac
commit 1e5605036e
16 changed files with 220 additions and 84 deletions

View File

@ -1,12 +1,12 @@
#[macro_use] extern crate cpython; extern crate cpython;
use cpython::{PythonObject, ObjectProtocol, PyModule, Python};
use cpython::{PythonObject, Python};
fn main() { fn main() {
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
let sys = PyModule::import(py, "sys").unwrap(); let sys = py.import("sys").unwrap();
let path: String = sys.as_object().getattr("version").unwrap().extract().unwrap(); let version: String = sys.get("version").unwrap().extract().unwrap();
println!("Hello Python {}", path); println!("Hello Python {}", version);
} }

View File

@ -4,35 +4,28 @@ use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, ToPythonPoin
use objects::{exc, PyObject, PyBool, PyTuple}; use objects::{exc, PyObject, PyBool, PyTuple};
use err::{self, PyErr, PyResult}; use err::{self, PyErr, PyResult};
/// ToPyObject is implemented for types that can be converted into a python object. /// Conversion trait that allows various objects to be converted into python objects.
/// The goal is to allow methods that take a python object to take anything that
/// can be converted into a python object.
/// For example, compare calling the following method signatures:
/// fn m1(o: &PyObject) {}
/// fn m2<O>(o: &O) where O : ToPyObject {}
///
/// let o: &PyObject = ...;
/// m1(o);
/// m2(o);
///
/// let p: PyPtr<PyObject> = ...;
/// m1(*p)
/// m2(p)
///
/// let i: i32 = ...;
/// m1(*try!(i.to_py_object(py)))
/// m2(i)
pub trait ToPyObject<'p> { pub trait ToPyObject<'p> {
type ObjectType : PythonObject<'p> = PyObject<'p>; type ObjectType : PythonObject<'p> = PyObject<'p>;
/// Converts self into a python object.
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, Self::ObjectType>; fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, Self::ObjectType>;
/// Converts self into a python object.
///
/// May be more efficient than `to_py_object` in some cases because
/// it can move out of the input object.
#[inline] #[inline]
fn into_py_object(self, py: Python<'p>) -> PyResult<'p, Self::ObjectType> fn into_py_object(self, py: Python<'p>) -> PyResult<'p, Self::ObjectType>
where Self: Sized { where Self: Sized {
self.to_py_object(py) self.to_py_object(py)
} }
/// Converts self into a python object and calls the specified closure
/// on the native FFI pointer underlying the python object.
///
/// May be more efficient than `to_py_object` because it does not need
/// to touch any reference counts when the input object already is a python object.
#[inline] #[inline]
fn with_borrowed_ptr<F, R>(&self, py: Python<'p>, f: F) -> PyResult<'p, R> fn with_borrowed_ptr<F, R>(&self, py: Python<'p>, f: F) -> PyResult<'p, R>
where F: FnOnce(*mut ffi::PyObject) -> PyResult<'p, R> { where F: FnOnce(*mut ffi::PyObject) -> PyResult<'p, R> {
@ -47,7 +40,7 @@ pub trait ToPyObject<'p> {
// 2) input is PyObject // 2) input is PyObject
// -> with_borrowed_ptr() just forwards to the closure // -> with_borrowed_ptr() just forwards to the closure
// 3) input is &str, int, ... // 3) input is &str, int, ...
// -> to_py_object() allocates new python object; FFI call happens; PyPtr::drop() calls Py_DECREF() // -> to_py_object() allocates new python object; FFI call happens; PyObject::drop() calls Py_DECREF()
// FFI functions that steal a reference will use: // FFI functions that steal a reference will use:
// let input = try!(input.into_py_object()); ffi::Call(input.steal_ptr()) // let input = try!(input.into_py_object()); ffi::Call(input.steal_ptr())

View File

@ -5,20 +5,22 @@ use objects::oldstyle::PyClass;
use ffi; use ffi;
use libc; use libc;
use conversion::ToPyObject; use conversion::ToPyObject;
use std::ffi::CStr; use std::ffi::{CString, CStr};
/// Represents a python exception that was raised. /// Represents a python exception that was raised.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PyErr<'p> { pub struct PyErr<'p> {
/// Gets the type of the exception. This should be either a PyClass or a PyType. /// The type of the exception. This should be either a `PyClass` or a `PyType`.
pub ptype : PyObject<'p>, pub ptype : PyObject<'p>,
/// Gets the value of the exception. /// The value of the exception.
/// This can be either an instance of ptype, ///
/// a tuple of arguments to be passed to ptype's constructor, /// This can be either an instance of `ptype`,
/// or a single argument to be passed to ptype's constructor. /// a tuple of arguments to be passed to `ptype`'s constructor,
/// Call PyErr::instance() to get the exception instance in all cases. /// or a single argument to be passed to `ptype`'s constructor.
/// Call `PyErr::instance()` to get the exception instance in all cases.
pub pvalue : Option<PyObject<'p>>, pub pvalue : Option<PyObject<'p>>,
pub ptraceback : Option<PyObject<'p>> // is actually a PyTraceBack /// The `PyTraceBack` object associated with the error.
pub ptraceback : Option<PyObject<'p>>
} }
@ -34,7 +36,7 @@ impl <'p> PyErr<'p> {
/// Retrieves the current error from the python interpreter's global state. /// Retrieves the current error from the python interpreter's global state.
/// The error is cleared from the python interpreter. /// The error is cleared from the python interpreter.
/// If no error is set, returns a SystemError. /// If no error is set, returns a `SystemError`.
pub fn fetch(py : Python<'p>) -> PyErr<'p> { pub fn fetch(py : Python<'p>) -> PyErr<'p> {
unsafe { unsafe {
let mut ptype : *mut ffi::PyObject = std::mem::uninitialized(); let mut ptype : *mut ffi::PyObject = std::mem::uninitialized();
@ -60,9 +62,10 @@ impl <'p> PyErr<'p> {
} }
/// Creates a new PyErr. /// Creates a new PyErr.
/// If obj is a python exception instance, the PyErr will use that instance. ///
/// If obj is a python exception type, the PyErr will (lazily) create a new instance of that type /// If `obj` is a python exception instance, the PyErr will use that instance.
/// Otherwise, a TypeError is returned instead. /// If `obj` is a python exception type, the PyErr will (lazily) create a new instance of that type
/// Otherwise, a `TypeError` is returned instead.
pub fn new<O>(obj: O) -> PyErr<'p> where O: PythonObject<'p> { pub fn new<O>(obj: O) -> PyErr<'p> where O: PythonObject<'p> {
PyErr::new_from_object(obj.into_object()) PyErr::new_from_object(obj.into_object())
} }
@ -147,7 +150,7 @@ impl <'p> PyErr<'p> {
} }
/// Retrieves the exception type. /// Retrieves the exception type.
/// If the exception type is an old-style class, returns oldstyle::PyClass. /// If the exception type is an old-style class, returns `oldstyle::PyClass`.
pub fn get_type(&self) -> PyType<'p> { pub fn get_type(&self) -> PyType<'p> {
let py = self.ptype.python(); let py = self.ptype.python();
match self.ptype.clone().cast_into::<PyType>() { match self.ptype.clone().cast_into::<PyType>() {
@ -161,7 +164,7 @@ impl <'p> PyErr<'p> {
} }
/// Retrieves the exception instance for this error. /// Retrieves the exception instance for this error.
/// This method takes &mut self because the error might need /// This method takes `&mut self` because the error might need
/// to be normalized in order to create the exception instance. /// to be normalized in order to create the exception instance.
pub fn instance(&mut self) -> PyObject<'p> { pub fn instance(&mut self) -> PyObject<'p> {
self.normalize(); self.normalize();
@ -171,7 +174,8 @@ impl <'p> PyErr<'p> {
} }
} }
/// Restores the error by writing it to the python interpreter's global state. /// Writes the error back to the python interpreter's global state.
/// This is the opposite of `PyErr::fetch()`.
#[inline] #[inline]
pub fn restore(self) { pub fn restore(self) {
let PyErr { ptype, pvalue, ptraceback } = self; let PyErr { ptype, pvalue, ptraceback } = self;
@ -182,13 +186,15 @@ impl <'p> PyErr<'p> {
/// Issue a warning message. /// Issue a warning message.
/// May return a PyErr if warnings-as-errors is enabled. /// May return a PyErr if warnings-as-errors is enabled.
pub fn warn(py: Python<'p>, category: &PyObject, message: &CStr, stacklevel: i32) -> PyResult<'p, ()> { pub fn warn(py: Python<'p>, category: &PyObject, message: &str, stacklevel: i32) -> PyResult<'p, ()> {
let message = CString::new(message).unwrap();
unsafe { unsafe {
error_on_minusone(py, ffi::PyErr_WarnEx(category.as_ptr(), message.as_ptr(), stacklevel as ffi::Py_ssize_t)) error_on_minusone(py, ffi::PyErr_WarnEx(category.as_ptr(), message.as_ptr(), stacklevel as ffi::Py_ssize_t))
} }
} }
} }
/// Converts `PythonObjectDowncastError` to python `TypeError`.
impl <'p> std::convert::From<PythonObjectDowncastError<'p>> for PyErr<'p> { impl <'p> std::convert::From<PythonObjectDowncastError<'p>> for PyErr<'p> {
fn from(err: PythonObjectDowncastError<'p>) -> PyErr<'p> { fn from(err: PythonObjectDowncastError<'p>) -> PyErr<'p> {
PyErr::new_lazy_init(err.0.get_type::<exc::TypeError>(), None) PyErr::new_lazy_init(err.0.get_type::<exc::TypeError>(), None)
@ -196,7 +202,7 @@ impl <'p> std::convert::From<PythonObjectDowncastError<'p>> for PyErr<'p> {
} }
/// Construct PyObject from the result of a python FFI call that returns a new reference (owned pointer). /// Construct PyObject from the result of a python FFI call that returns a new reference (owned pointer).
/// Returns Err(PyErr) if the pointer is null. /// Returns `Err(PyErr)` if the pointer is `null`.
/// Unsafe because the pointer might be invalid. /// Unsafe because the pointer might be invalid.
#[inline] #[inline]
pub unsafe fn result_from_owned_ptr(py : Python, p : *mut ffi::PyObject) -> PyResult<PyObject> { pub unsafe fn result_from_owned_ptr(py : Python, p : *mut ffi::PyObject) -> PyResult<PyObject> {
@ -207,18 +213,22 @@ pub unsafe fn result_from_owned_ptr(py : Python, p : *mut ffi::PyObject) -> PyRe
} }
} }
fn panic_after_error(py: Python) -> ! {
unsafe { ffi::PyErr_Print(); }
panic!("Python API called failed");
}
#[inline] #[inline]
pub unsafe fn from_owned_ptr_or_panic(py : Python, p : *mut ffi::PyObject) -> PyObject { pub unsafe fn from_owned_ptr_or_panic(py : Python, p : *mut ffi::PyObject) -> PyObject {
if p.is_null() { if p.is_null() {
ffi::PyErr_Print(); panic_after_error(py);
panic!("Python API called failed");
} else { } else {
PyObject::from_owned_ptr(py, p) PyObject::from_owned_ptr(py, p)
} }
} }
/// Construct PyObject from the result of a python FFI call that returns a borrowed reference. /// Construct PyObject from the result of a python FFI call that returns a borrowed reference.
/// Returns Err(PyErr) if the pointer is null. /// Returns `Err(PyErr)` if the pointer is `null`.
/// Unsafe because the pointer might be invalid. /// Unsafe because the pointer might be invalid.
#[inline] #[inline]
pub unsafe fn result_from_borrowed_ptr(py : Python, p : *mut ffi::PyObject) -> PyResult<PyObject> { pub unsafe fn result_from_borrowed_ptr(py : Python, p : *mut ffi::PyObject) -> PyResult<PyObject> {
@ -239,6 +249,16 @@ pub unsafe fn result_cast_from_owned_ptr<'p, T>(py : Python<'p>, p : *mut ffi::P
} }
} }
pub unsafe fn cast_from_owned_ptr_or_panic<'p, T>(py : Python<'p>, p : *mut ffi::PyObject) -> T
where T: ::python::PythonObjectWithCheckedDowncast<'p>
{
if p.is_null() {
panic_after_error(py);
} else {
PyObject::from_owned_ptr(py, p).cast_into().unwrap()
}
}
/// Returns Ok if the error code is not -1. /// Returns Ok if the error code is not -1.
#[inline] #[inline]
pub fn error_on_minusone(py : Python, result : libc::c_int) -> PyResult<()> { pub fn error_on_minusone(py : Python, result : libc::c_int) -> PyResult<()> {

View File

@ -8,17 +8,47 @@
//! Rust bindings to the python interpreter. //! 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.
//!
//! Because all python objects potentially have multiple owners, the concept
//! 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 the lifetime parameter `PyObject<'p>` to ensure that python objects cannot
//! be accessed without holding the GIL.
//! Throughout this library, the lifetime `'p` always refers to the lifetime of the python interpreter.
//!
//! When accessing existing objects, the lifetime on `PyObject<'p>` is sufficient to ensure that the GIL
//! is held by the current code. But we also need to ensure that the GIL is held when creating new objects.
//! For this purpose, this library uses the marker type `Python<'p>`,
//! which acts like a reference to the whole python interpreter.
//!
//! You can obtain a `Python<'p>` instance by acquiring the GIL, or by calling `python()`
//! on any existing python object.
//!
//! # Error Handling
//! The vast majority of operations in this library will return `PyResult<'p, ...>`.
//! This is an alias for the type `Result<..., PyErr<'p>>`.
//!
//! A `PyErr` represents a python exception. Errors within the rust-cpython library are
//! also exposed as python exceptions.
//!
//! # Example //! # Example
//! ``` //! ```
//! #[macro_use] extern crate cpython; //! extern crate cpython;
//! //!
//! use cpython::{PythonObject, ObjectProtocol, PyModule, Python}; //! use cpython::{PythonObject, Python};
//! //!
//! fn main() { //! fn main() {
//! let gil = Python::acquire_gil(); //! let gil_guard = Python::acquire_gil();
//! let py = gil.python(); //! let py = gil_guard.python();
//! let sys = py.import("sys").unwrap(); //! let sys = py.import("sys").unwrap();
//! let version: String = sys.get("version").unwrap().extract().unwrap(); //! let version = sys.get("version").unwrap().extract::<String>().unwrap();
//! println!("Hello Python {}", version); //! println!("Hello Python {}", version);
//! } //! }
//! ``` //! ```
@ -51,6 +81,7 @@ mod objectprotocol;
mod pythonrun; mod pythonrun;
/// Private re-exports for macros. Do not use. /// Private re-exports for macros. Do not use.
#[doc(hidden)]
pub mod _detail { pub mod _detail {
pub use ffi; pub use ffi;
pub use libc; pub use libc;
@ -117,6 +148,18 @@ macro_rules! py_module_initializer {
} }
} }
/// Creates a python callable object that invokes a Rust function.
///
/// Arguments:
///
/// 1. The `Python<'p>` marker, to ensure this macro is only used while holding the GIL.
/// 2. A Rust function with the signature `<'p>(Python<'p>, &PyTuple<'p>) -> PyResult<'p, T>`
/// for some `T` that implements `ToPyObject`.
///
/// See `py_module_initializer!` for example usage.
///
/// # Panic
/// May panic when python runs out of memory.
#[macro_export] #[macro_export]
macro_rules! py_func { macro_rules! py_func {
($py: expr, $f: expr) => ({ ($py: expr, $f: expr) => ({
@ -132,7 +175,10 @@ macro_rules! py_func {
}; };
match result { match result {
Ok(val) => $crate::ToPythonPointer::steal_ptr(val), Ok(val) => $crate::ToPythonPointer::steal_ptr(val),
Err(e) => { e.restore(); ::std::ptr::null_mut() } Err(e) => {
e.restore();
::std::ptr::null_mut()
}
} }
} }
static mut method_def: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef { static mut method_def: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {

View File

@ -9,6 +9,7 @@ use objects::{PyObject, PyTuple, PyDict};
use conversion::ToPyObject; use conversion::ToPyObject;
use err::{PyErr, PyResult, result_from_owned_ptr, error_on_minusone}; use err::{PyErr, PyResult, result_from_owned_ptr, error_on_minusone};
/// Trait that contains methods
pub trait ObjectProtocol<'p> : PythonObject<'p> { pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Determines whether this object has the given attribute. /// Determines whether this object has the given attribute.
/// This is equivalent to the Python expression 'hasattr(self, attr_name)'. /// This is equivalent to the Python expression 'hasattr(self, attr_name)'.

View File

@ -7,11 +7,12 @@ use conversion::{FromPyObject, ToPyObject};
pyobject_newtype!(PyBool, PyBool_Check, PyBool_Type); pyobject_newtype!(PyBool, PyBool_Check, PyBool_Type);
impl <'p> PyBool<'p> { impl <'p> PyBool<'p> {
/// Depending on `val`, returns `py.True()` or `py.False()`.
#[inline] #[inline]
pub fn get(py: Python<'p>, val: bool) -> PyBool<'p> { pub fn get(py: Python<'p>, val: bool) -> PyBool<'p> {
if val { py.True() } else { py.False() } if val { py.True() } else { py.False() }
} }
#[inline] #[inline]
pub fn is_true(&self) -> bool { pub fn is_true(&self) -> bool {
self.as_ptr() == unsafe { ::ffi::Py_True() } self.as_ptr() == unsafe { ::ffi::Py_True() }

View File

@ -7,8 +7,14 @@ use err::{self, PyResult};
pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type); pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);
impl <'p> PyDict<'p> { impl <'p> PyDict<'p> {
/// Creates a new empty dictionary.
///
/// # Panic
/// May panic when running out of memory.
pub fn new(py: Python<'p>) -> PyDict<'p> { pub fn new(py: Python<'p>) -> PyDict<'p> {
unimplemented!() unsafe {
err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New())
}
} }
} }

View File

@ -1,3 +1,5 @@
//! This module contains the python exception types.
use libc::c_char; use libc::c_char;
use std::ops::Range; use std::ops::Range;
use std::str::Utf8Error; use std::str::Utf8Error;

View File

@ -7,8 +7,7 @@ pyobject_newtype!(PyIterator, PyIter_Check);
impl <'p> PyIterator<'p> { impl <'p> PyIterator<'p> {
/// Retrieves the next item from an iterator. /// Retrieves the next item from an iterator.
/// Returns None when the iterator is exhausted. /// Returns `None` when the iterator is exhausted.
#[inline]
pub fn iter_next(&self) -> PyResult<'p, Option<PyObject<'p>>> { pub fn iter_next(&self) -> PyResult<'p, Option<PyObject<'p>>> {
let py = self.python(); let py = self.python();
match unsafe { PyObject::from_owned_ptr_opt(py, ffi::PyIter_Next(self.as_ptr())) } { match unsafe { PyObject::from_owned_ptr_opt(py, ffi::PyIter_Next(self.as_ptr())) } {
@ -24,4 +23,3 @@ impl <'p> PyIterator<'p> {
} }
} }

View File

@ -9,6 +9,7 @@ use conversion::{ToPyObject, FromPyObject};
pyobject_newtype!(PyList, PyList_Check, PyList_Type); pyobject_newtype!(PyList, PyList_Check, PyList_Type);
impl <'p> PyList<'p> { impl <'p> PyList<'p> {
/// Construct a new list with the given elements.
pub fn new(py: Python<'p>, elements: &[PyObject<'p>]) -> PyList<'p> { pub fn new(py: Python<'p>, elements: &[PyObject<'p>]) -> PyList<'p> {
unsafe { unsafe {
let ptr = ffi::PyList_New(elements.len() as Py_ssize_t); let ptr = ffi::PyList_New(elements.len() as Py_ssize_t);
@ -20,6 +21,7 @@ impl <'p> PyList<'p> {
} }
} }
/// Gets the length of the list.
#[inline] #[inline]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
// non-negative Py_ssize_t should always fit into Rust uint // non-negative Py_ssize_t should always fit into Rust uint
@ -28,6 +30,9 @@ impl <'p> PyList<'p> {
} }
} }
/// Gets the item at the specified index.
///
/// Panics if the index is out of range.
pub fn get_item(&self, index: usize) -> PyObject<'p> { pub fn get_item(&self, index: usize) -> PyObject<'p> {
assert!(index < self.len()); assert!(index < self.len());
unsafe { unsafe {
@ -35,21 +40,21 @@ impl <'p> PyList<'p> {
} }
} }
/// Sets the item at the specified index.
///
/// Panics if the index is out of range.
pub fn set_item(&self, index: usize, item: PyObject<'p>) { pub fn set_item(&self, index: usize, item: PyObject<'p>) {
let r = unsafe { ffi::PyList_SetItem(self.as_ptr(), index as Py_ssize_t, item.steal_ptr()) }; let r = unsafe { ffi::PyList_SetItem(self.as_ptr(), index as Py_ssize_t, item.steal_ptr()) };
assert!(r == 0); assert!(r == 0);
} }
/// Inserts an item at the specified index.
///
/// Panics if the index is out of range.
pub fn insert_item(&self, index: usize, item: PyObject<'p>) { pub fn insert_item(&self, index: usize, item: PyObject<'p>) {
let r = unsafe { ffi::PyList_Insert(self.as_ptr(), index as Py_ssize_t, item.as_ptr()) }; let r = unsafe { ffi::PyList_Insert(self.as_ptr(), index as Py_ssize_t, item.as_ptr()) };
assert!(r == 0); assert!(r == 0);
} }
/*
#[inline]
pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, PyObject<'p>> {
self.as_slice().iter()
}*/
} }
impl <'p, T> ToPyObject<'p> for [T] where T: ToPyObject<'p> { impl <'p, T> ToPyObject<'p> for [T] where T: ToPyObject<'p> {

View File

@ -27,6 +27,7 @@ impl <'p> PyModule<'p> {
} }
// Helper method for module_initializer!() macro, do not use directly! // Helper method for module_initializer!() macro, do not use directly!
#[doc(hidden)]
pub fn _init<F, R>(py: Python<'p>, name: &CStr, init: F) -> PyResult<'p, R> pub fn _init<F, R>(py: Python<'p>, name: &CStr, init: F) -> PyResult<'p, R>
where F: FnOnce(Python<'p>, PyModule<'p>) -> PyResult<'p, R> { where F: FnOnce(Python<'p>, PyModule<'p>) -> PyResult<'p, R> {
let module = try!(unsafe { let module = try!(unsafe {
@ -60,21 +61,28 @@ impl <'p> PyModule<'p> {
} }
} }
/// Gets the module name.
///
/// May fail if the module does not have a __name__ attribute.
pub fn name<'a>(&'a self) -> PyResult<'p, &'a str> { pub fn name<'a>(&'a self) -> PyResult<'p, &'a str> {
unsafe { self.str_from_ptr(ffi::PyModule_GetName(self.as_ptr())) } unsafe { self.str_from_ptr(ffi::PyModule_GetName(self.as_ptr())) }
} }
/// Gets the module filename.
///
/// May fail if the module does not have a __file__ attribute.
pub fn filename<'a>(&'a self) -> PyResult<'p, &'a str> { pub fn filename<'a>(&'a self) -> PyResult<'p, &'a str> {
unsafe { self.str_from_ptr(ffi::PyModule_GetFilename(self.as_ptr())) } unsafe { self.str_from_ptr(ffi::PyModule_GetFilename(self.as_ptr())) }
} }
/// Convenience function for retrieving a member from the module. /// Gets a member from the module.
pub fn get(&self, name: &str) -> PyResult<'p, PyObject<'p>> { pub fn get(&self, name: &str) -> PyResult<'p, PyObject<'p>> {
use objectprotocol::ObjectProtocol; use objectprotocol::ObjectProtocol;
self.as_object().getattr(name) self.as_object().getattr(name)
} }
/// Adds a member to the module. /// Adds a member to the module.
///
/// This is a convenience function which can be used from the module's initialization function. /// This is a convenience function which can be used from the module's initialization function.
pub fn add<V>(&self, name: &str, value: V) -> PyResult<'p, ()> where V: ToPyObject<'p> { pub fn add<V>(&self, name: &str, value: V) -> PyResult<'p, ()> where V: ToPyObject<'p> {
let py = self.python(); let py = self.python();

View File

@ -12,12 +12,14 @@ pyobject_newtype!(PyLong, PyLong_Check, PyLong_Type);
pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type); pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type);
impl <'p> PyInt<'p> { impl <'p> PyInt<'p> {
/// Creates a new python `int` object.
pub fn new(py: Python<'p>, val: c_long) -> PyInt<'p> { pub fn new(py: Python<'p>, val: c_long) -> PyInt<'p> {
unsafe { unsafe {
err::result_from_owned_ptr(py, ffi::PyInt_FromLong(val)).unwrap().unchecked_cast_into::<PyInt>() err::cast_from_owned_ptr_or_panic(py, ffi::PyInt_FromLong(val))
} }
} }
/// Gets the value of this integer.
pub fn value(&self) -> c_long { pub fn value(&self) -> c_long {
unsafe { ffi::PyInt_AS_LONG(self.as_ptr()) } unsafe { ffi::PyInt_AS_LONG(self.as_ptr()) }
} }
@ -25,12 +27,14 @@ impl <'p> PyInt<'p> {
impl <'p> PyFloat<'p> { impl <'p> PyFloat<'p> {
/// Creates a new python `float` object.
pub fn new(py: Python<'p>, val: c_double) -> PyFloat<'p> { pub fn new(py: Python<'p>, val: c_double) -> PyFloat<'p> {
unsafe { unsafe {
err::result_from_owned_ptr(py, ffi::PyFloat_FromDouble(val)).unwrap().unchecked_cast_into::<PyFloat>() err::cast_from_owned_ptr_or_panic(py, ffi::PyFloat_FromDouble(val))
} }
} }
/// Gets the value of this float.
pub fn value(&self) -> c_double { pub fn value(&self) -> c_double {
unsafe { ffi::PyFloat_AS_DOUBLE(self.as_ptr()) } unsafe { ffi::PyFloat_AS_DOUBLE(self.as_ptr()) }
} }

View File

@ -14,6 +14,7 @@ pub struct PyObject<'p> {
py : Python<'p> py : Python<'p>
} }
/// Dropping a `PyObject` decrements the reference count on the object by 1.
impl <'p> Drop for PyObject<'p> { impl <'p> Drop for PyObject<'p> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
@ -24,6 +25,7 @@ impl <'p> Drop for PyObject<'p> {
} }
} }
/// Cloning a `PyObject` increments the reference count on the object by 1.
impl <'p> Clone for PyObject<'p> { impl <'p> Clone for PyObject<'p> {
#[inline] #[inline]
fn clone(&self) -> PyObject<'p> { fn clone(&self) -> PyObject<'p> {
@ -135,7 +137,7 @@ impl <'p> PyObject<'p> {
} }
} }
/// Transmutes an owned FFI pointer to &PyObject. /// Transmutes an owned FFI pointer to `&PyObject`.
/// Undefined behavior if the pointer is NULL or invalid. /// Undefined behavior if the pointer is NULL or invalid.
#[inline] #[inline]
pub unsafe fn borrow_from_owned_ptr<'a>(py : Python<'p>, ptr : &'a *mut ffi::PyObject) -> &'a PyObject<'p> { pub unsafe fn borrow_from_owned_ptr<'a>(py : Python<'p>, ptr : &'a *mut ffi::PyObject) -> &'a PyObject<'p> {
@ -143,19 +145,20 @@ impl <'p> PyObject<'p> {
transmute(ptr) transmute(ptr)
} }
/// Transmutes a slice of owned FFI pointers to &[PyObject]. /// Transmutes a slice of owned FFI pointers to `&[PyObject]`.
/// Undefined behavior if the pointer is NULL or invalid. /// Undefined behavior if the pointer is NULL or invalid.
#[inline] #[inline]
pub unsafe fn borrow_from_owned_ptr_slice<'a>(py : Python<'p>, ptr : &'a [*mut ffi::PyObject]) -> &'a [PyObject<'p>] { pub unsafe fn borrow_from_owned_ptr_slice<'a>(py : Python<'p>, ptr : &'a [*mut ffi::PyObject]) -> &'a [PyObject<'p>] {
transmute(ptr) transmute(ptr)
} }
/// Retrieves the reference count of this python object. /// Gets the reference count of this python object.
#[inline] #[inline]
pub fn get_refcnt(&self) -> usize { pub fn get_refcnt(&self) -> usize {
unsafe { ffi::Py_REFCNT(self.as_ptr()) as usize } unsafe { ffi::Py_REFCNT(self.as_ptr()) as usize }
} }
/// Gets the python type object for this object's type.
#[inline] #[inline]
pub fn get_type(&self) -> &PyType<'p> { pub fn get_type(&self) -> &PyType<'p> {
unsafe { unsafe {
@ -166,15 +169,15 @@ impl <'p> PyObject<'p> {
/// Casts the PyObject to a concrete python object type. /// Casts the PyObject to a concrete python object type.
/// Causes undefined behavior if the object is not of the expected type. /// Causes undefined behavior if the object is not of the expected type.
/// This is a wrapper function around PythonObject::unchecked_downcast_from(). /// This is a wrapper function around `PythonObject::unchecked_downcast_from()`.
#[inline] #[inline]
pub unsafe fn unchecked_cast_into<T>(self) -> T where T: PythonObject<'p> { pub unsafe fn unchecked_cast_into<T>(self) -> T where T: PythonObject<'p> {
PythonObject::unchecked_downcast_from(self) PythonObject::unchecked_downcast_from(self)
} }
/// Casts the PyObject to a concrete python object type. /// Casts the PyObject to a concrete python object type.
/// Returns a python TypeError if the object is not of the expected type. /// Returns a python `TypeError` if the object is not of the expected type.
/// This is a wrapper function around PythonObjectWithCheckedDowncast::downcast_from(). /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_from()`.
#[inline] #[inline]
pub fn cast_into<T>(self) -> Result<T, PythonObjectDowncastError<'p>> where T: PythonObjectWithCheckedDowncast<'p> { pub fn cast_into<T>(self) -> Result<T, PythonObjectDowncastError<'p>> where T: PythonObjectWithCheckedDowncast<'p> {
PythonObjectWithCheckedDowncast::downcast_from(self) PythonObjectWithCheckedDowncast::downcast_from(self)
@ -182,34 +185,39 @@ impl <'p> PyObject<'p> {
/// Casts the PyObject to a concrete python object type. /// Casts the PyObject to a concrete python object type.
/// Causes undefined behavior if the object is not of the expected type. /// Causes undefined behavior if the object is not of the expected type.
/// This is a wrapper function around PythonObject::unchecked_downcast_borrow_from(). /// This is a wrapper function around `PythonObject::unchecked_downcast_borrow_from()`.
#[inline] #[inline]
pub unsafe fn unchecked_cast_as<'s, T>(&'s self) -> &'s T where T: PythonObject<'p> { pub unsafe fn unchecked_cast_as<'s, T>(&'s self) -> &'s T where T: PythonObject<'p> {
PythonObject::unchecked_downcast_borrow_from(self) PythonObject::unchecked_downcast_borrow_from(self)
} }
/// Casts the PyObject to a concrete python object type. /// Casts the PyObject to a concrete python object type.
/// Returns a python TypeError if the object is not of the expected type. /// Returns a python `TypeError` if the object is not of the expected type.
/// This is a wrapper function around PythonObjectWithCheckedDowncast::downcast_borrow_from(). /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_borrow_from()`.
#[inline] #[inline]
pub fn cast_as<'s, T>(&'s self) -> Result<&'s T, PythonObjectDowncastError<'p>> where T: PythonObjectWithCheckedDowncast<'p> { pub fn cast_as<'s, T>(&'s self) -> Result<&'s T, PythonObjectDowncastError<'p>> where T: PythonObjectWithCheckedDowncast<'p> {
PythonObjectWithCheckedDowncast::downcast_borrow_from(self) PythonObjectWithCheckedDowncast::downcast_borrow_from(self)
} }
/// Extracts some type from the python object. /// Extracts some type from the python object.
/// This is a wrapper function around FromPyObject::from_py_object(). /// This is a wrapper function around `FromPyObject::from_py_object()`.
#[inline] #[inline]
pub fn extract<'s, T>(&'s self) -> Result<T, PyErr<'p>> where T: ::conversion::FromPyObject<'p, 's> { pub fn extract<'s, T>(&'s self) -> Result<T, PyErr<'p>> where T: ::conversion::FromPyObject<'p, 's> {
::conversion::FromPyObject::from_py_object(self) ::conversion::FromPyObject::from_py_object(self)
} }
} }
/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in python.
impl <'p> PartialEq for PyObject<'p> { impl <'p> PartialEq for PyObject<'p> {
#[inline] #[inline]
fn eq(&self, o : &PyObject<'p>) -> bool { fn eq(&self, o : &PyObject<'p>) -> bool {
self.ptr == o.ptr self.ptr == o.ptr
} }
} }
/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in python.
impl <'p> Eq for PyObject<'p> { } impl <'p> Eq for PyObject<'p> { }

View File

@ -1,3 +1,5 @@
//! This module contains support for old-style classes. Only available in Python 2.x.
use ffi; use ffi;
use python::{Python, PythonObject, ToPythonPointer}; use python::{Python, PythonObject, ToPythonPointer};
use err::{self, PyResult}; use err::{self, PyResult};

View File

@ -16,6 +16,21 @@ pyobject_newtype!(PyString, PyString_Check, PyString_Type);
pyobject_newtype!(PyUnicode, PyUnicode_Check, PyUnicode_Type); pyobject_newtype!(PyUnicode, PyUnicode_Check, PyUnicode_Type);
impl <'p> PyString<'p> { impl <'p> PyString<'p> {
/// Creates a new python string object from the Rust string.
///
/// Note: on Python 2, this function always creates a `str` object,
/// never a `unicode` object.
/// Use `str::to_py_object()` instead to create `unicode` objects for non-ascii strings.
pub fn new(py: Python<'p>, s: &str) -> PyString<'p> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
err::cast_from_owned_ptr_or_panic(py,
ffi::PyString_FromStringAndSize(ptr, len))
}
}
/// Gets the python string data as byte slice.
pub fn as_slice(&self) -> &[u8] { pub fn as_slice(&self) -> &[u8] {
unsafe { unsafe {
let buffer = ffi::PyString_AS_STRING(self.as_ptr()) as *const u8; let buffer = ffi::PyString_AS_STRING(self.as_ptr()) as *const u8;
@ -24,12 +39,23 @@ impl <'p> PyString<'p> {
} }
} }
/// Gets the python string data as `&str`.
pub fn as_str(&self) -> Result<&str, str::Utf8Error> { pub fn as_str(&self) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.as_slice()) str::from_utf8(self.as_slice())
} }
} }
impl <'p> PyUnicode<'p> { impl <'p> PyUnicode<'p> {
/// Creates a new unicode string object from the Rust string.
pub fn new(py: Python<'p>, s: &str) -> PyUnicode<'p> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
err::cast_from_owned_ptr_or_panic(py,
ffi::PyUnicode_FromStringAndSize(ptr, len))
}
}
pub fn as_slice(&self) -> &[ffi::Py_UNICODE] { pub fn as_slice(&self) -> &[ffi::Py_UNICODE] {
unsafe { unsafe {
let buffer = ffi::PyUnicode_AS_UNICODE(self.as_ptr()) as *const _; let buffer = ffi::PyUnicode_AS_UNICODE(self.as_ptr()) as *const _;
@ -41,6 +67,10 @@ impl <'p> PyUnicode<'p> {
// When converting strings to/from python, we need to copy the string data. // When converting strings to/from python, we need to copy the string data.
// This means we can implement ToPyObject for str, but FromPyObject only for (Cow)String. // This means we can implement ToPyObject for str, but FromPyObject only for (Cow)String.
/// Converts rust `str` to python object:
/// ASCII-only strings are converted to python `str` objects;
/// other strings are converted to python `unicode` objects.
impl <'p> ToPyObject<'p> for str { impl <'p> ToPyObject<'p> for str {
type ObjectType = PyObject<'p>; type ObjectType = PyObject<'p>;
@ -58,6 +88,9 @@ impl <'p> ToPyObject<'p> for str {
} }
} }
/// Converts rust `&str` to python object:
/// ASCII-only strings are converted to python `str` objects;
/// other strings are converted to python `unicode` objects.
impl <'p, 'a> ToPyObject<'p> for &'a str { impl <'p, 'a> ToPyObject<'p> for &'a str {
type ObjectType = PyObject<'p>; type ObjectType = PyObject<'p>;

View File

@ -9,22 +9,25 @@ use pythonrun::GILGuard;
struct PythonInterpreterState; struct PythonInterpreterState;
impl !Sync for PythonInterpreterState {} impl !Sync for PythonInterpreterState {}
/// Marker type that indicates that the GIL is currently held.
///
/// The 'Python' struct is a zero-size marker struct that is required for most python operations. /// 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, /// 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 GIL is acquired. /// and thus can only be called if the python interpreter is initialized and the
/// The lifetime 'p represents the lifetime of the python interpreter. /// python global interpreter lock (GIL) is acquired.
/// For example, python constants like None have the type "&'p PyObject<'p>". /// The lifetime `'p` represents the lifetime of the python interpreter.
/// You can imagine the GIL to be a giant "Mutex<AllPythonState>". This makes 'p the lifetime of the ///
/// python state protected by that mutex. /// 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)] #[derive(Copy, Clone)]
pub struct Python<'p>(PhantomData<&'p PythonInterpreterState>); pub struct Python<'p>(PhantomData<&'p PythonInterpreterState>);
// Trait for converting from Self to *mut ffi::PyObject /// This trait allows retrieving the underlying FFI pointer from python objects.
pub trait ToPythonPointer { pub trait ToPythonPointer {
/// Retrieves the underlying FFI pointer (as a borrowed pointer). /// Retrieves the underlying FFI pointer (as a borrowed pointer).
fn as_ptr(&self) -> *mut ffi::PyObject; fn as_ptr(&self) -> *mut ffi::PyObject;
/// Destructures the input object, moving out the ownership of the underlying FFI pointer. /// Retrieves the underlying FFI pointer as a "stolen pointer".
fn steal_ptr(self) -> *mut ffi::PyObject; fn steal_ptr(self) -> *mut ffi::PyObject;
} }
@ -51,7 +54,7 @@ pub trait PythonObject<'p> : 'p + Clone + ToPythonPointer {
} }
} }
// Marker type that indicates an error while downcasting // Marker type that indicates an error while downcasting
pub struct PythonObjectDowncastError<'p>(pub Python<'p>); pub struct PythonObjectDowncastError<'p>(pub Python<'p>);
/// Trait implemented by python object types that allow a checked downcast. /// Trait implemented by python object types that allow a checked downcast.
@ -162,3 +165,9 @@ impl<'p> Python<'p> {
} }
} }
impl <'p> std::fmt::Debug for PythonObjectDowncastError<'p> {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.write_str("PythonObjectDowncastError")
}
}