Improve documentation.

This commit is contained in:
Daniel Grunwald 2015-06-27 22:45:35 +02:00
parent 6b496b4772
commit 3b02ef5b99
23 changed files with 455 additions and 224 deletions

View File

@ -22,14 +22,14 @@ use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, ToPythonPoin
use objects::{exc, PyObject, PyBool, PyTuple};
use err::{self, PyErr, PyResult};
/// Conversion trait that allows various objects to be converted into python objects.
/// Conversion trait that allows various objects to be converted into Python objects.
pub trait ToPyObject<'p> {
type ObjectType : PythonObject<'p> = PyObject<'p>;
/// Converts self into a python object.
/// Converts self into a Python object.
fn to_py_object(&self, py: Python<'p>) -> Self::ObjectType;
/// Converts self into a python object.
/// 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.
@ -39,11 +39,11 @@ pub trait ToPyObject<'p> {
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.
/// 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.
/// to touch any reference counts when the input object already is a Python object.
#[inline]
fn with_borrowed_ptr<F, R>(&self, py: Python<'p>, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R {
@ -58,7 +58,7 @@ pub trait ToPyObject<'p> {
// 2) input is PyObject
// -> with_borrowed_ptr() just forwards to the closure
// 3) input is &str, int, ...
// -> to_py_object() allocates new python object; FFI call happens; PyObject::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:
// let input = try!(input.into_py_object()); ffi::Call(input.steal_ptr())
@ -67,18 +67,18 @@ pub trait ToPyObject<'p> {
// 2) input is PyObject
// -> into_py_object() is no-op
// 3) input is &str, int, ...
// -> into_py_object() allocates new python object
// -> into_py_object() allocates new Python object
}
/// FromPyObject is implemented by various types that can be extracted from a python object.
/// FromPyObject is implemented by various types that can be extracted from a Python object.
pub trait FromPyObject<'p> {
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, Self>;
}
// PyObject, PyModule etc.
// We support FromPyObject and ToPyObject for owned python references.
// This allows using existing python objects in code that generically expects a value
// convertible to a python object.
// This allows using existing Python objects in code that generically expects a value
// convertible to a Python object.
/// Identity conversion: allows using existing `PyObject` instances where
/// `ToPyObject` is expected.
@ -114,25 +114,25 @@ impl <'p, T> FromPyObject<'p> for T where T: PythonObjectWithCheckedDowncast<'p>
// &PyObject, &PyModule etc.
// We support FromPyObject and ToPyObject for borrowed python references.
// This allows using existing python objects in code that generically expects a value
// convertible to a python object.
impl <'p, 's, T> ToPyObject<'p> for &'s T where T : ToPyObject<'p> {
// This allows using existing Python objects in code that generically expects a value
// convertible to a Python object.
impl <'p, 's, T: ?Sized> ToPyObject<'p> for &'s T where T: ToPyObject<'p> {
type ObjectType = T::ObjectType;
#[inline]
fn to_py_object(&self, py: Python<'p>) -> T::ObjectType {
(**self).to_py_object(py)
<T as ToPyObject>::to_py_object(*self, py)
}
#[inline]
fn into_py_object(self, py: Python<'p>) -> T::ObjectType {
(*self).to_py_object(py)
<T as ToPyObject>::to_py_object(self, py)
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, py: Python<'p>, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R {
(**self).with_borrowed_ptr(py, f)
<T as ToPyObject>::with_borrowed_ptr(*self, py, f)
}
}

View File

@ -26,7 +26,7 @@ use libc;
use conversion::ToPyObject;
use std::ffi::{CString, CStr};
/// Represents a python exception that was raised.
/// Represents a Python exception that was raised.
#[derive(Clone, Debug)]
pub struct PyErr<'p> {
/// The type of the exception. This should be either a `PyClass` or a `PyType`.
@ -43,18 +43,18 @@ pub struct PyErr<'p> {
}
/// Represents the result of a python call.
/// Represents the result of a Python call.
pub type PyResult<'p, T> = Result<T, PyErr<'p>>;
impl <'p> PyErr<'p> {
/// Gets whether an error is present in the python interpreter's global state.
/// Gets whether an error is present in the Python interpreter's global state.
#[inline]
pub fn occurred(_ : Python<'p>) -> bool {
unsafe { !ffi::PyErr_Occurred().is_null() }
}
/// Retrieves the current error from the python interpreter's global state.
/// The error is cleared from the python interpreter.
/// Retrieves the current error from the Python interpreter's global state.
/// The error is cleared from the Python interpreter.
/// If no error is set, returns a `SystemError`.
pub fn fetch(py : Python<'p>) -> PyErr<'p> {
unsafe {
@ -79,16 +79,16 @@ impl <'p> PyErr<'p> {
ptraceback: PyObject::from_owned_ptr_opt(py, ptraceback)
}
}
/// 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
/// Otherwise, a `TypeError` is returned instead.
/// If `obj` is a Python exception instance, the PyErr will use that instance.
/// If `obj` is a Python exception type object, the PyErr will (lazily) create a new instance of that type.
/// Otherwise, a `TypeError` is created instead.
pub fn new<O>(obj: O) -> PyErr<'p> where O: PythonObject<'p> {
PyErr::new_from_object(obj.into_object())
}
fn new_from_object(obj: PyObject<'p>) -> PyErr<'p> {
let py = obj.python();
if unsafe { ffi::PyExceptionInstance_Check(obj.as_ptr()) } != 0 {
@ -112,7 +112,7 @@ impl <'p> PyErr<'p> {
}
}
/// Construct a new error, with the usual lazy initialization of python exceptions.
/// Construct a new error, with the usual lazy initialization of Python exceptions.
/// `exc` is the exception type; usually one of the standard exceptions like `PyExc::runtime_error()`.
/// `value` is the exception instance, or a tuple of arguments to pass to the exception constructor.
#[inline]
@ -143,7 +143,7 @@ impl <'p> PyErr<'p> {
pub fn matches(&self, exc: &PyObject) -> bool {
unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc.as_ptr()) != 0 }
}
/// Normalizes the error. This ensures that the exception value is an instance of the exception type.
pub fn normalize(&mut self) {
// The normalization helper function involves temporarily moving out of the &mut self,
@ -167,8 +167,9 @@ impl <'p> PyErr<'p> {
PyErr::new_from_ffi_tuple(py, ptype, pvalue, ptraceback)
}
}
/// Retrieves the exception type.
///
/// If the exception type is an old-style class, returns `oldstyle::PyClass`.
#[cfg(feature="python27-sys")]
pub fn get_type(&self) -> PyType<'p> {
@ -182,7 +183,7 @@ impl <'p> PyErr<'p> {
}
}
}
/// Retrieves the exception type.
#[cfg(not(feature="python27-sys"))]
pub fn get_type(&self) -> PyType<'p> {
@ -204,7 +205,7 @@ impl <'p> PyErr<'p> {
}
}
/// Writes the error back 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]
pub fn restore(self) {
@ -213,7 +214,7 @@ impl <'p> PyErr<'p> {
ffi::PyErr_Restore(ptype.steal_ptr(), pvalue.steal_ptr(), ptraceback.steal_ptr())
}
}
/// Issue a warning message.
/// May return a PyErr if warnings-as-errors is enabled.
pub fn warn(py: Python<'p>, category: &PyObject, message: &str, stacklevel: i32) -> PyResult<'p, ()> {
@ -224,14 +225,14 @@ impl <'p> PyErr<'p> {
}
}
/// Converts `PythonObjectDowncastError` to python `TypeError`.
/// Converts `PythonObjectDowncastError` to Python `TypeError`.
impl <'p> std::convert::From<PythonObjectDowncastError<'p>> for PyErr<'p> {
fn from(err: PythonObjectDowncastError<'p>) -> PyErr<'p> {
PyErr::new_lazy_init(err.0.get_type::<exc::TypeError>(), None)
}
}
/// 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`.
/// Unsafe because the pointer might be invalid.
#[inline]

View File

@ -24,13 +24,13 @@ use conversion::ToPyObject;
use ffi;
use err;
/// Creates a python callable object that invokes a Rust function.
/// Creates a Python callable object that invokes a Rust function.
///
/// As arguments, takes the name of a rust function with the signature
/// `for<'p> fn(Python<'p>, &PyTuple<'p>) -> PyResult<'p, T>`
/// for some `T` that implements `ToPyObject`.
///
/// Returns a type that implements `ToPyObject` by producing a python callable.
/// Returns a type that implements `ToPyObject` by producing a Python callable.
///
/// See `py_module_initializer!` for example usage.
#[macro_export]

View File

@ -20,42 +20,42 @@
#![feature(filling_drop)] // necessary to avoid segfault with unsafe_no_drop_flag
#![feature(optin_builtin_traits)] // for opting out of Sync/Send
#![feature(slice_patterns)] // for tuple_conversion macros
#![feature(utf8_error)] // for translating Utf8Error to python exception
#![feature(utf8_error)] // for translating Utf8Error to Python exception
#![feature(plugin)]
#![plugin(interpolate_idents)]
#![allow(unused_imports, unused_variables)]
//! 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.
//! 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
//! 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)
//! 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
//! 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.
//! 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.
//! 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.
//! 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.
//! A `PyErr` represents a Python exception. Errors within the rust-cpython library are
//! also exposed as Python exceptions.
//!
//! # Example
//! ```
@ -137,14 +137,14 @@ pub mod _detail {
}
}
/// Expands to an `extern "C"` function that allows python to load
/// the rust code as a python extension module.
/// Expands to an `extern "C"` function that allows Python to load
/// the rust code as a Python extension module.
///
/// Macro syntax: `py_module_initializer!($name, |$py, $m| $body)`
///
/// 1. The module name as a string literal.
/// 2. The name of the init function as an identifier.
/// The function must be named `init$module_name` so that python 2.7 can load the module.
/// The function must be named `init$module_name` so that Python 2.7 can load the module.
/// Note: this parameter will be removed in a future version
/// (once Rust supports `concat_ident!` as function name).
/// 3. A function or lambda of type `Fn(Python<'p>, &PyModule<'p>) -> PyResult<'p, ()>`.
@ -176,7 +176,7 @@ pub mod _detail {
/// ```bash
/// rustc example.rs -o example.so
/// ```
/// It can then be imported into python:
/// It can then be imported into Python:
///
/// ```python
/// >>> import example

View File

@ -74,8 +74,8 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
})
}
/// Compares two python objects.
/// This is equivalent to the python expression 'cmp(self, other)'.
/// Compares two Python objects.
/// This is equivalent to the Python expression 'cmp(self, other)'.
#[cfg(feature="python27-sys")]
fn compare<O>(&self, other: O) -> PyResult<'p, Ordering> where O: ToPyObject<'p> {
let py = self.python();
@ -94,7 +94,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Compute the string representation of self.
/// This is equivalent to the python expression 'repr(self)'.
/// This is equivalent to the Python expression 'repr(self)'.
#[inline]
fn repr(&self) -> PyResult<'p, PyObject<'p>> {
unsafe {
@ -103,7 +103,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Compute the string representation of self.
/// This is equivalent to the python expression 'str(self)'.
/// This is equivalent to the Python expression 'str(self)'.
#[inline]
fn str(&self) -> PyResult<'p, PyObject<'p>> {
unsafe {
@ -112,7 +112,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Compute the unicode string representation of self.
/// This is equivalent to the python expression 'unistr(self)'.
/// This is equivalent to the Python expression 'unistr(self)'.
#[inline]
#[cfg(feature="python27-sys")]
fn unistr(&self) -> PyResult<'p, PyObject<'p>> {
@ -130,7 +130,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Calls the object.
/// This is equivalent to the python expression: 'self(*args, **kwargs)'
/// This is equivalent to the Python expression: 'self(*args, **kwargs)'
#[inline]
fn call<A>(&self, args: A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
@ -141,7 +141,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Calls a method on the object.
/// This is equivalent to the python expression: 'self.name(*args, **kwargs)'
/// This is equivalent to the Python expression: 'self.name(*args, **kwargs)'
#[inline]
fn call_method<A>(&self, name: &str, args: A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
@ -149,7 +149,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Retrieves the hash code of the object.
/// This is equivalent to the python expression: 'hash(self)'
/// This is equivalent to the Python expression: 'hash(self)'
#[inline]
fn hash(&self) -> PyResult<'p, libc::c_long> {
let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
@ -161,7 +161,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Returns whether the object is considered to be true.
/// This is equivalent to the python expression: 'not not self'
/// This is equivalent to the Python expression: 'not not self'
#[inline]
fn is_true(&self) -> PyResult<'p, bool> {
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
@ -173,7 +173,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
/// Returns the length of the sequence or mapping.
/// This is equivalent to the python expression: 'len(self)'
/// This is equivalent to the Python expression: 'len(self)'
#[inline]
fn len(&self) -> PyResult<'p, usize> {
let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
@ -184,7 +184,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
}
}
/// This is equivalent to the python expression: 'self[key]'
/// This is equivalent to the Python expression: 'self[key]'
#[inline]
fn get_item<K>(&self, key: K) -> PyResult<'p, PyObject<'p>> where K: ToPyObject<'p> {
let py = self.python();

View File

@ -4,6 +4,9 @@ use err::PyResult;
use super::PyObject;
use conversion::{FromPyObject, ToPyObject};
/// Represents a Python `bool`.
pub struct PyBool<'p>(PyObject<'p>);
pyobject_newtype!(PyBool, PyBool_Check, PyBool_Type);
impl <'p> PyBool<'p> {
@ -13,12 +16,14 @@ impl <'p> PyBool<'p> {
if val { py.True() } else { py.False() }
}
/// Gets whether this boolean is `true`.
#[inline]
pub fn is_true(&self) -> bool {
self.as_ptr() == unsafe { ::ffi::Py_True() }
}
}
/// Converts a rust `bool` to a Python `bool`.
impl <'p> ToPyObject<'p> for bool {
type ObjectType = PyBool<'p>;
@ -36,6 +41,9 @@ impl <'p> ToPyObject<'p> for bool {
}
}
/// Converts a Python `bool` to a rust `bool`.
///
/// Fails with `TypeError` if the input is not a Python `bool`.
impl <'p> FromPyObject<'p> for bool {
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, bool> {
Ok(try!(s.clone().cast_into::<PyBool>()).is_true())

View File

@ -23,12 +23,14 @@ use conversion::ToPyObject;
use objects::PyObject;
use err::{self, PyResult, PyErr};
/// Represents a Python `dict`.
pub struct PyDict<'p>(PyObject<'p>);
pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);
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> {
unsafe {
@ -37,6 +39,7 @@ impl <'p> PyDict<'p> {
}
/// Return a new dictionary that contains the same key-value pairs as self.
/// Corresponds to `dict(self)` in Python.
pub fn copy(&self) -> PyResult<'p, PyDict<'p>> {
let py = self.python();
unsafe {
@ -50,6 +53,8 @@ impl <'p> PyDict<'p> {
unsafe { ffi::PyDict_Clear(self.as_ptr()) }
}
/// Return the number of items in the dictionary.
/// This is equivalent to len(p) on a dictionary.
#[inline]
pub fn len(&self) -> usize {
unsafe { ffi::PyDict_Size(self.as_ptr()) as usize }
@ -57,15 +62,15 @@ impl <'p> PyDict<'p> {
/// Determine if the dictionary contains the specified key.
/// This is equivalent to the Python expression `key in self`.
pub fn contains(&self, key: &PyObject<'p>) -> PyResult<'p, bool> {
pub fn contains<K>(&self, key: K) -> PyResult<'p, bool> where K: ToPyObject<'p> {
let py = self.python();
unsafe {
match ffi::PyDict_Contains(self.as_ptr(), key.as_ptr()) {
key.with_borrowed_ptr(py, |key| unsafe {
match ffi::PyDict_Contains(self.as_ptr(), key) {
1 => Ok(true),
0 => Ok(false),
_ => Err(PyErr::fetch(py))
}
}
})
}
/// Gets an item from the dictionary.
@ -90,7 +95,7 @@ impl <'p> PyDict<'p> {
}
/// Deletes an item.
/// This is equivalent to the Python expression 'del self[key]'.
/// This is equivalent to the Python expression `del self[key]`.
pub fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
let py = self.python();
key.with_borrowed_ptr(py, |key| unsafe {

View File

@ -31,8 +31,10 @@ use super::typeobject::PyType;
macro_rules! exc_type(
($name:ident, $exc_name:ident) => (
pub struct $name<'p>(PyObject<'p>);
pyobject_newtype!($name);
impl <'p> PythonObjectWithCheckedDowncast<'p> for $name<'p> {
#[inline]
fn downcast_from(obj : PyObject<'p>) -> Result<$name<'p>, PythonObjectDowncastError<'p>> {
@ -44,7 +46,7 @@ macro_rules! exc_type(
}
}
}
#[inline]
fn downcast_borrow_from<'a>(obj : &'a ::objects::object::PyObject<'p>) -> Result<&'a $name<'p>, PythonObjectDowncastError<'p>> {
unsafe {
@ -56,7 +58,7 @@ macro_rules! exc_type(
}
}
}
impl <'p> PythonObjectWithTypeObject<'p> for $name<'p> {
#[inline]
fn type_object(py: Python<'p>) -> PyType<'p> {

View File

@ -21,6 +21,8 @@ use objects::PyObject;
use err::{PyErr, PyResult};
use ffi;
pub struct PyIterator<'p>(PyObject<'p>);
pyobject_newtype!(PyIterator, PyIter_Check);
impl <'p> PyIterator<'p> {

View File

@ -24,6 +24,9 @@ use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};
/// Represents a Python `list`.
pub struct PyList<'p>(PyObject<'p>);
pyobject_newtype!(PyList, PyList_Check, PyList_Type);
impl <'p> PyList<'p> {
@ -95,6 +98,7 @@ impl <'a, 'p> IntoIterator for &'a PyList<'p> {
}
}
/// Used by `impl IntoIterator for &PyList`.
pub struct PyListIterator<'p> {
list: PyList<'p>,
index: usize

View File

@ -41,8 +41,14 @@ pub use self::num::{PyLong, PyFloat};
macro_rules! pyobject_newtype(
($name: ident) => (
#[derive(Clone)]
pub struct $name<'p>(::objects::object::PyObject<'p>);
/// Clone returns another reference to the Python object,
/// thus incrementing the reference count by 1.
impl <'p> Clone for $name<'p> {
#[inline]
fn clone(&self) -> Self {
$name(self.0.clone())
}
}
impl <'p> ::python::PythonObject<'p> for $name<'p> {
#[inline]

View File

@ -20,15 +20,19 @@ use std;
use ffi;
use libc::c_char;
use python::{Python, PythonObject, ToPythonPointer};
use objectprotocol::ObjectProtocol;
use conversion::ToPyObject;
use objects::{PyObject, PyType, PyDict, exc};
use objects::{PyObject, PyType, PyTuple, PyDict, exc};
use err::{self, PyResult, PyErr};
use std::ffi::{CStr, CString};
/// Represents a Python module object.
pub struct PyModule<'p>(PyObject<'p>);
pyobject_newtype!(PyModule, PyModule_Check, PyModule_Type);
impl <'p> PyModule<'p> {
/// Create a new module object with the __name__ attribute set to name.
/// Create a new module object with the `__name__` attribute set to name.
pub fn new(py: Python<'p>, name: &str) -> PyResult<'p, PyModule<'p>> {
let name = CString::new(name).unwrap();
unsafe {
@ -36,7 +40,7 @@ impl <'p> PyModule<'p> {
}
}
/// Import the python module with the specified name.
/// Import the Python module with the specified name.
pub fn import(py: Python<'p>, name: &str) -> PyResult<'p, PyModule<'p>> {
let name = CString::new(name).unwrap();
unsafe {
@ -44,8 +48,8 @@ impl <'p> PyModule<'p> {
}
}
/// Return the dictionary object that implements modules namespace;
/// this object is the same as the __dict__ attribute of the module object.
/// Return the dictionary object that implements module's namespace;
/// this object is the same as the `__dict__` attribute of the module object.
pub fn dict(&self) -> PyDict<'p> {
let py = self.python();
unsafe {
@ -69,24 +73,31 @@ impl <'p> PyModule<'p> {
/// Gets the module name.
///
/// May fail if the module does not have a __name__ attribute.
/// May fail if the module does not have a `__name__` attribute.
pub fn name<'a>(&'a self) -> PyResult<'p, &'a str> {
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.
/// May fail if the module does not have a `__file__` attribute.
pub fn filename<'a>(&'a self) -> PyResult<'p, &'a str> {
unsafe { self.str_from_ptr(ffi::PyModule_GetFilename(self.as_ptr())) }
}
/// Gets a member from the module.
/// This is equivalent to the Python expression: `getattr(module, name)`
pub fn get(&self, name: &str) -> PyResult<'p, PyObject<'p>> {
use objectprotocol::ObjectProtocol;
self.as_object().getattr(name)
}
/// Calls a function in the module.
/// This is equivalent to the Python expression: `getattr(module, name)(*args, **kwargs)`
pub fn call<A>(&self, name: &str, args: A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
try!(self.as_object().getattr(name)).call(args, kwargs)
}
/// Adds a member to the module.
///
/// This is a convenience function which can be used from the module's initialization function.

View File

@ -27,15 +27,45 @@ use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};
/// Represents a Python `int` object.
///
/// Note that in Python 2.x, `int` and `long` are different types.
/// When rust-cpython is compiled for Python 3.x,
/// `PyInt` and `PyLong` are aliases for the same type, which
/// corresponds to a Python `int`.
///
/// You can usually avoid directly working with this type
/// by using [ToPyObject](trait.ToPyObject.html)
/// and [extract](struct.PyObject.html#method.extract)
/// with the primitive Rust integer types.
#[cfg(feature="python27-sys")]
pub struct PyInt<'p>(PyObject<'p>);
#[cfg(feature="python27-sys")]
pyobject_newtype!(PyInt, PyInt_Check, PyInt_Type);
/// In Python 2.x, represents a Python `long` object.
/// In Python 3.x, represents a Python `int` object.
/// Both `PyInt` and `PyLong` refer to the same type on Python 3.x.
///
/// You can usually avoid directly working with this type
/// by using [ToPyObject](trait.ToPyObject.html)
/// and [extract](struct.PyObject.html#method.extract)
/// with the primitive Rust integer types.
pub struct PyLong<'p>(PyObject<'p>);
pyobject_newtype!(PyLong, PyLong_Check, PyLong_Type);
/// Represents a Python `float` object.
///
/// You can usually avoid directly working with this type
/// by using [ToPyObject](trait.ToPyObject.html)
/// and [extract](struct.PyObject.html#method.extract)
/// with `f32`/`f64`.
pub struct PyFloat<'p>(PyObject<'p>);
pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type);
#[cfg(feature="python27-sys")]
impl <'p> PyInt<'p> {
/// Creates a new python `int` object.
/// Creates a new Python `int` object.
pub fn new(py: Python<'p>, val: c_long) -> PyInt<'p> {
unsafe {
err::cast_from_owned_ptr_or_panic(py, ffi::PyInt_FromLong(val))
@ -50,7 +80,7 @@ impl <'p> PyInt<'p> {
impl <'p> PyFloat<'p> {
/// Creates a new python `float` object.
/// Creates a new Python `float` object.
pub fn new(py: Python<'p>, val: c_double) -> PyFloat<'p> {
unsafe {
err::cast_from_owned_ptr_or_panic(py, ffi::PyFloat_FromDouble(val))
@ -286,7 +316,7 @@ impl <'p> ToPyObject<'p> for f32 {
}
}
impl <'p, 's> FromPyObject<'p> for f32 {
impl <'p> FromPyObject<'p> for f32 {
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, f32> {
Ok(try!(s.extract::<f64>()) as f32)
}

View File

@ -23,6 +23,23 @@ use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObject
use objects::PyType;
use err::{PyErr, PyResult};
/// Represents a reference to a Python object.
///
/// Python objects are reference counted.
/// Calling `clone()` on a `PyObject` will return a new reference to the same object
/// (thus incrementing the reference count).
/// The `Drop` implementation will decrement the reference count.
///
/// `PyObject` can be used with all Python objects, since all python types
/// derive from `object`. This crate also contains other, more specific types
/// that serve as references to Python objects (e.g. `PyTuple` for Python tuples, etc.).
///
/// You can convert from any Python object to `PyObject` by calling `as_object()` or `into_object`
/// from the [PythonObject trait](trait.PythonObject.html).
/// In the other direction, you can call `cast_as` or `cast_into`
/// on `PyObject` to convert to more specific object types.
///
/// Most of the interesting methods are provided by the [ObjectProtocol trait](trait.ObjectProtocol.html).
#[unsafe_no_drop_flag]
#[repr(C)]
pub struct PyObject<'p> {
@ -43,7 +60,8 @@ impl <'p> Drop for PyObject<'p> {
}
}
/// Cloning a `PyObject` increments the reference count on the object by 1.
/// Clone returns another reference to the Python object,
/// thus incrementing the reference count by 1.
impl <'p> Clone for PyObject<'p> {
#[inline]
fn clone(&self) -> PyObject<'p> {
@ -172,13 +190,13 @@ impl <'p> PyObject<'p> {
mem::transmute(ptr)
}
/// Gets the reference count of this python object.
/// Gets the reference count of this Python object.
#[inline]
pub fn get_refcnt(&self) -> usize {
unsafe { ffi::Py_REFCNT(self.as_ptr()) as usize }
}
/// Gets the python type object for this object's type.
/// Gets the Python type object for this object's type.
#[inline]
pub fn get_type(&self) -> &PyType<'p> {
unsafe {
@ -187,7 +205,7 @@ 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.
/// This is a wrapper function around `PythonObject::unchecked_downcast_from()`.
#[inline]
@ -195,31 +213,31 @@ impl <'p> PyObject<'p> {
PythonObject::unchecked_downcast_from(self)
}
/// Casts the PyObject to a concrete python object type.
/// Returns a python `TypeError` if the object is not of the expected type.
/// Casts the PyObject to a concrete Python object type.
/// Fails with `PythonObjectDowncastError` if the object is not of the expected type.
/// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_from()`.
#[inline]
pub fn cast_into<T>(self) -> Result<T, PythonObjectDowncastError<'p>> where T: PythonObjectWithCheckedDowncast<'p> {
PythonObjectWithCheckedDowncast::downcast_from(self)
}
/// 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.
/// This is a wrapper function around `PythonObject::unchecked_downcast_borrow_from()`.
#[inline]
pub unsafe fn unchecked_cast_as<'s, T>(&'s self) -> &'s T where T: PythonObject<'p> {
PythonObject::unchecked_downcast_borrow_from(self)
}
/// Casts the PyObject to a concrete python object type.
/// Returns a python `TypeError` if the object is not of the expected type.
/// Casts the PyObject to a concrete Python object type.
/// Fails with `PythonObjectDowncastError` if the object is not of the expected type.
/// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_borrow_from()`.
#[inline]
pub fn cast_as<'s, T>(&'s self) -> Result<&'s T, PythonObjectDowncastError<'p>> where T: PythonObjectWithCheckedDowncast<'p> {
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()`.
#[inline]
pub fn extract<T>(&self) -> Result<T, PyErr<'p>> where T: ::conversion::FromPyObject<'p> {
@ -228,7 +246,7 @@ impl <'p> PyObject<'p> {
}
/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in python.
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in Python.
impl <'p> PartialEq for PyObject<'p> {
#[inline]
fn eq(&self, o : &PyObject<'p>) -> bool {
@ -237,7 +255,7 @@ impl <'p> PartialEq for PyObject<'p> {
}
/// PyObject implements the `==` operator using reference equality:
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in python.
/// `obj1 == obj2` in rust is equivalent to `obj1 is obj2` in Python.
impl <'p> Eq for PyObject<'p> { }
impl <'p> ToPythonPointer for PyObject<'p> {

View File

@ -20,12 +20,22 @@
use ffi;
use python::{Python, PythonObject, ToPythonPointer};
use conversion::ToPyObject;
use err::{self, PyResult};
use super::object::PyObject;
use super::tuple::PyTuple;
use super::dict::PyDict;
/// Represents an old-style Python class.
///
/// Only available with Python 2.x.
pub struct PyClass<'p>(PyObject<'p>);
pyobject_newtype!(PyClass, PyClass_Check, PyClass_Type);
/// Represents an old-style Python instance.
///
/// Only available with Python 2.x.
pub struct PyInstance<'p>(PyObject<'p>);
pyobject_newtype!(PyInstance, PyInstance_Check, PyInstance_Type);
impl <'p> PyClass<'p> {
@ -35,20 +45,21 @@ impl <'p> PyClass<'p> {
}
/// Create a new instance of the class.
/// The parameters arg and kw are used as the positional and keyword parameters to the objects constructor.
pub fn create_instance(&self, arg: &PyTuple<'p>, kw: &PyDict<'p>) -> PyResult<'p, PyObject<'p>> {
unsafe {
err::result_from_owned_ptr(self.python(),
ffi::PyInstance_New(self.as_ptr(), arg.as_ptr(), kw.as_ptr()))
}
/// The parameters args and kw are used as the positional and keyword parameters to the objects constructor.
pub fn create_instance<T>(&self, args: T, kw: Option<&PyDict<'p>>) -> PyResult<'p, PyInstance<'p>>
where T: ToPyObject<'p, ObjectType=PyTuple<'p>> {
args.with_borrowed_ptr(self.python(), |args| unsafe {
err::result_cast_from_owned_ptr(self.python(),
ffi::PyInstance_New(self.as_ptr(), args, kw.as_ptr()))
})
}
/// Create a new instance of a specific class without calling its constructor.
/// The dict parameter will be used as the objects __dict__; if None, a new dictionary will be created for the instance.
pub fn create_instance_raw(&self, arg: &PyTuple<'p>, kw: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>> {
/// The dict parameter will be used as the objects __dict__.
pub fn create_instance_raw(&self, dict: PyDict<'p>) -> PyResult<'p, PyInstance<'p>> {
unsafe {
err::result_from_owned_ptr(self.python(),
ffi::PyInstance_New(self.as_ptr(), arg.as_ptr(), kw.as_ptr()))
err::result_cast_from_owned_ptr(self.python(),
ffi::PyInstance_NewRaw(self.as_ptr(), dict.as_object().as_ptr()))
}
}
}

View File

@ -27,6 +27,14 @@ use super::{exc, PyObject};
use err::{self, PyResult, PyErr};
use conversion::{FromPyObject, ToPyObject};
/// Represents a Python byte string.
/// Corresponds to `str` in Python 2, and `bytes` in Python 3.
pub struct PyBytes<'p>(PyObject<'p>);
/// Represents a Python unicode string.
/// Corresponds to `unicode` in Python 2, and `str` in Python 3.
pub struct PyUnicode<'p>(PyObject<'p>);
pyobject_newtype!(PyBytes, PyBytes_Check, PyBytes_Type);
pyobject_newtype!(PyUnicode, PyUnicode_Check, PyUnicode_Type);
@ -36,7 +44,10 @@ pub use PyBytes as PyString;
pub use PyUnicode as PyString;
impl <'p> PyBytes<'p> {
/// Creates a new python byte string object from the &[u8].
/// Creates a new Python byte string object.
/// The byte string is initialized by copying the data from the `&[u8]`.
///
/// Panics if out of memory.
pub fn new(py: Python<'p>, s: &[u8]) -> PyBytes<'p> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
@ -46,7 +57,7 @@ impl <'p> PyBytes<'p> {
}
}
/// Gets the python string data as byte slice.
/// Gets the Python string data as byte slice.
pub fn as_slice(&self) -> &[u8] {
unsafe {
let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
@ -54,18 +65,6 @@ impl <'p> PyBytes<'p> {
std::slice::from_raw_parts(buffer, length)
}
}
// In python 2.7, PyBytes serves as PyString, so it should offer the
// to_str and to_string_lossy functions:
#[cfg(feature="python27-sys")]
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.as_slice())
}
#[cfg(feature="python27-sys")]
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self.as_slice())
}
}
impl <'p> PyUnicode<'p> {
@ -90,6 +89,9 @@ impl <'p> PyUnicode<'p> {
}
}*/
/// Convert the `PyUnicode` into a rust string.
///
/// Returns a `UnicodeDecodeError` if the input contains invalid code points.
pub fn to_string(&self) -> PyResult<'p, Cow<str>> {
// TODO: use PyUnicode_AsUTF8AndSize if available
let py = self.python();
@ -102,6 +104,9 @@ impl <'p> PyUnicode<'p> {
}
}
/// Convert the `PyUnicode` into a rust string.
///
/// Any invalid code points are replaced with U+FFFD REPLACEMENT CHARACTER.
pub fn to_string_lossy(&self) -> Cow<str> {
// TODO: use PyUnicode_AsUTF8AndSize if available
// TODO: test how this function handles lone surrogates or otherwise invalid code points
@ -117,14 +122,20 @@ impl <'p> PyUnicode<'p> {
// On PyString (i.e. PyBytes in 2.7, PyUnicode otherwise), put static methods
// for extraction as Cow<str>:
impl <'p> PyString<'p> {
/// Extract a rust string from the Python object.
///
/// In Python 2.7, accepts both byte strings and unicode strings.
/// Byte strings will be decoded using UTF-8.
///
/// In Python 3.x, accepts unicode strings only.
///
/// Returns `TypeError` if the input is not one of the accepted types.
/// Returns `UnicodeDecodeError` if the input is not valid unicode.
#[cfg(feature="python27-sys")]
pub fn extract<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
if let Ok(s) = o.cast_as::<PyBytes>() {
match s.to_str() {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(e) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(py, s.as_slice(), e))))
}
s.to_string()
} else if let Ok(u) = o.cast_as::<PyUnicode>() {
u.to_string()
} else {
@ -132,6 +143,15 @@ impl <'p> PyString<'p> {
}
}
/// Extract a rust string from the Python object.
///
/// In Python 2.7, accepts both byte strings and unicode strings.
/// Byte strings will be decoded using UTF-8.
///
/// In Python 3.x, accepts unicode strings only.
///
/// Returns `TypeError` if the input is not one of the accepted types.
/// Any invalid code points are replaced with U+FFFD REPLACEMENT CHARACTER.
#[cfg(feature="python27-sys")]
pub fn extract_lossy<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
@ -144,6 +164,15 @@ impl <'p> PyString<'p> {
}
}
/// Extract a rust string from the Python object.
///
/// In Python 2.7, accepts both byte strings and unicode strings.
/// Byte strings will be decoded using UTF-8.
///
/// In Python 3.x, accepts unicode strings only.
///
/// Returns `TypeError` if the input is not one of the accepted types.
/// Returns `UnicodeDecodeError` if the input is not valid unicode.
#[cfg(feature="python3-sys")]
pub fn extract<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
@ -154,6 +183,15 @@ impl <'p> PyString<'p> {
}
}
/// Extract a rust string from the Python object.
///
/// In Python 2.7, accepts both byte strings and unicode strings.
/// Byte strings will be decoded using UTF-8.
///
/// In Python 3.x, accepts unicode strings only.
///
/// Returns `TypeError` if the input is not one of the accepted types.
/// Any invalid code points are replaced with U+FFFD REPLACEMENT CHARACTER.
#[cfg(feature="python3-sys")]
pub fn extract_lossy<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
@ -163,14 +201,47 @@ impl <'p> PyString<'p> {
Err(PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None))
}
}
// In Python 2.7, PyBytes serves as PyString, so it should offer the
// same to_string and to_string_lossy functions as PyUnicode:
/// Convert the `PyString` into a rust string.
///
/// In Python 2.7, `PyString` is a byte string and will be decoded using UTF-8.
/// In Python 3.x, `PyString` is a unicode string.
///
/// Returns a `UnicodeDecodeError` if the input is not valid unicode.
#[cfg(feature="python27-sys")]
pub fn to_string(&self) -> PyResult<'p, Cow<str>> {
let py = self.python();
match str::from_utf8(self.as_slice()) {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(e) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(py, self.as_slice(), e))))
}
}
/// Convert the `PyString` into a rust string.
///
/// In Python 2.7, `PyString` is a byte string and will be decoded using UTF-8.
/// In Python 3.x, `PyString` is a unicode string.
///
/// Any invalid UTF-8 sequences are replaced with U+FFFD REPLACEMENT CHARACTER.
#[cfg(feature="python27-sys")]
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self.as_slice())
}
}
// 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.
/// Converts rust `str` to python object:
/// ASCII-only strings are converted to python `str` objects;
/// other strings are converted to python `unicode` objects.
/// Converts rust `str` to Python object:
/// ASCII-only strings are converted to Python `str` objects;
/// other strings are converted to Python `unicode` objects.
///
/// Note that `str::ObjectType` differs based on Python version:
/// In Python 2.7, it is `PyObject` (`object` is the common base class of `str` and `unicode`).
/// In Python 3.x, it is `PyUnicode`.
impl <'p> ToPyObject<'p> for str {
#[cfg(feature="python27-sys")]
type ObjectType = PyObject<'p>;
@ -194,20 +265,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 {
type ObjectType = <str as ToPyObject<'p>>::ObjectType;
fn to_py_object(&self, py : Python<'p>) -> Self::ObjectType {
(**self).to_py_object(py)
}
}
/// Allows extracting strings from python objects.
/// Accepts python `str` and `unicode` objects.
/// In python 2.7, `str` is expected to be UTF-8 encoded.
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
/// In Python 2.7, `str` is expected to be UTF-8 encoded.
impl <'p> FromPyObject<'p> for String {
fn from_py_object(o: &PyObject<'p>) -> PyResult<'p, String> {
PyString::extract(o).map(|s| s.into_owned())

View File

@ -24,14 +24,18 @@ use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};
/// Represents a Python tuple object.
pub struct PyTuple<'p>(PyObject<'p>);
pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type);
impl <'p> PyTuple<'p> {
/// Construct a new tuple with the given elements.
pub fn new(py: Python<'p>, elements: &[PyObject<'p>]) -> PyTuple<'p> {
unsafe {
let len = elements.len();
let ptr = ffi::PyTuple_New(len as Py_ssize_t);
let t = err::result_from_owned_ptr(py, ptr).unwrap().unchecked_cast_into::<PyTuple>();
let t = err::result_cast_from_owned_ptr::<PyTuple>(py, ptr).unwrap();
for (i, e) in elements.iter().enumerate() {
ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, e.clone().steal_ptr());
}
@ -42,10 +46,11 @@ impl <'p> PyTuple<'p> {
/// Retrieves the empty tuple.
pub fn empty(py: Python<'p>) -> PyTuple<'p> {
unsafe {
err::result_from_owned_ptr(py, ffi::PyTuple_New(0)).unwrap().unchecked_cast_into::<PyTuple>()
err::result_cast_from_owned_ptr::<PyTuple>(py, ffi::PyTuple_New(0)).unwrap()
}
}
/// Gets the length of the tuple.
#[inline]
pub fn len(&self) -> usize {
// non-negative Py_ssize_t should always fit into Rust uint
@ -65,7 +70,7 @@ impl <'p> PyTuple<'p> {
}
/* Disabled for now; we might want to change the PyObject memory layout for
compatiblity with Rust 1.0.
compatiblity with stable Rust.
#[inline]
pub fn as_slice<'a>(&'a self) -> &'a [PyObject<'p>] {
// This is safe because PyObject has the same memory layout as *mut ffi::PyObject,
@ -101,6 +106,7 @@ impl <'a, 'p> IntoIterator for &'a PyTuple<'p> {
}
}
/// Used by `impl IntoIterator for &PyTuple`.
pub struct PyTupleIterator<'p> {
tuple: PyTuple<'p>,
index: usize,
@ -191,8 +197,19 @@ tuple_conversion!(9, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
// Empty tuple:
/// An empty struct that represents the empty argument list.
/// Corresponds to the empty tuple `()` in Python.
///
/// # Example
/// ```
/// let gil_guard = cpython::Python::acquire_gil();
/// let py = gil_guard.python();
/// let os = py.import("os").unwrap();
/// let pid = os.call("get_pid", cpython::NoArgs, None);
/// ```
pub struct NoArgs;
/// Converts `NoArgs` to an empty Python tuple.
impl <'p> ToPyObject<'p> for NoArgs {
type ObjectType = PyTuple<'p>;
@ -201,6 +218,8 @@ impl <'p> ToPyObject<'p> for NoArgs {
}
}
/// Returns `Ok(NoArgs)` if the input is an empty Python tuple.
/// Otherwise, returns an error.
impl <'p> FromPyObject<'p> for NoArgs {
fn from_py_object(s : &PyObject<'p>) -> PyResult<'p, NoArgs> {
let t = try!(s.cast_as::<PyTuple>());

View File

@ -24,10 +24,13 @@ use ffi;
use libc::c_char;
use std;
/// Represents a reference to a Python type object.
pub struct PyType<'p>(PyObject<'p>);
pyobject_newtype!(PyType, PyType_Check, PyType_Type);
impl <'p> PyType<'p> {
/// Retrieves the underlying FFI pointer associated with this python object.
/// Retrieves the underlying FFI pointer associated with this Python object.
#[inline]
pub fn as_type_ptr(&self) -> *mut ffi::PyTypeObject {
self.0.as_ptr() as *mut ffi::PyTypeObject
@ -53,9 +56,9 @@ impl <'p> PyType<'p> {
}
/// Calls the type object, thus creating a new instance.
/// This is equivalent to the python expression: 'self(*args, **kwargs)'
/// This is equivalent to the Python expression: `self(*args, **kwargs)`
#[inline]
pub fn call<A: ?Sized>(&self, args: &A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
pub fn call<A>(&self, args: A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
let py = self.python();
args.with_borrowed_ptr(py, |args| unsafe {

View File

@ -23,38 +23,34 @@ use objects::{PyObject, PyType, PyBool, PyModule};
use err::PyResult;
use pythonrun::GILGuard;
// Dummy struct representing the global state in the python interpreter.
struct 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.
/// 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.
/// 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.
///
/// 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)]
pub struct Python<'p>(PhantomData<&'p PythonInterpreterState>);
pub struct Python<'p>(PhantomData<&'p GILGuard>);
/// This trait allows retrieving the underlying FFI pointer from python objects.
/// 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;
/// Retrieves the underlying FFI pointer as a "stolen pointer".
fn steal_ptr(self) -> *mut ffi::PyObject;
}
/// Trait implemented by all python object types.
/// Trait implemented by all Python object types.
pub trait PythonObject<'p> : 'p + Clone {
/// Casts the python object to PyObject.
/// Casts the Python object to PyObject.
fn as_object(&self) -> &PyObject<'p>;
/// Casts the python object to PyObject.
/// Casts the Python object to PyObject.
fn into_object(self) -> PyObject<'p>;
/// Unchecked downcast from PyObject to Self.
@ -65,7 +61,7 @@ pub trait PythonObject<'p> : 'p + Clone {
/// Undefined behavior if the input object does not have the expected type.
unsafe fn unchecked_downcast_borrow_from<'a>(&'a PyObject<'p>) -> &'a Self;
/// Retrieve python instance from an existing python object.
/// Retrieve Python instance from an existing Python object.
#[inline]
fn python(&self) -> Python<'p> {
self.as_object().python()
@ -75,22 +71,22 @@ pub trait PythonObject<'p> : 'p + Clone {
// Marker type that indicates an error while downcasting
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.
pub trait PythonObjectWithCheckedDowncast<'p> : PythonObject<'p> {
/// Cast from PyObject to a concrete python object type.
/// Cast from PyObject to a concrete Python object type.
fn downcast_from(PyObject<'p>) -> Result<Self, PythonObjectDowncastError<'p>>;
/// Cast from PyObject to a concrete python object type.
/// Cast from PyObject to a concrete Python object type.
fn downcast_borrow_from<'a>(&'a PyObject<'p>) -> Result<&'a Self, PythonObjectDowncastError<'p>>;
}
/// Trait implemented by python object types that have a corresponding type object.
/// Trait implemented by Python object types that have a corresponding type object.
pub trait PythonObjectWithTypeObject<'p> : PythonObjectWithCheckedDowncast<'p> {
/// Retrieves the type object for this python object type.
/// Retrieves the type object for this Python object type.
fn type_object(Python<'p>) -> PyType<'p>;
}
/// ToPythonPointer for borrowed python pointers.
/// ToPythonPointer for borrowed Python pointers.
impl <'a, 'p, T> ToPythonPointer for &'a T where T: PythonObject<'p> {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
@ -123,24 +119,28 @@ impl <T> ToPythonPointer for Option<T> where T: ToPythonPointer {
}
impl<'p> Python<'p> {
/// Retrieve python instance under the assumption that the GIL is already acquired at this point,
/// and stays acquired for the lifetime 'p
/// 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]
pub unsafe fn assume_gil_acquired() -> Python<'p> {
Python(PhantomData)
}
/// Acquires the global interpreter lock, which allows access to the Python runtime.
/// If the python runtime is not already initialized, this function will initialize it.
/// Note that in this case, the python runtime will not have any main thread, and will
/// not deliver signals like KeyboardInterrupt.
///
/// 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()
}
/// Releases the GIL and allows the use of python on other threads.
/// Unsafe because we do not ensure that existing references to python objects
/// 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
@ -149,34 +149,34 @@ impl<'p> Python<'p> {
ffi::PyEval_RestoreThread(save);
result
}
/// Gets the python builtin value `None`.
#[allow(non_snake_case)] // the python keyword starts with uppercase
/// Gets the Python builtin value `None`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
pub fn None(self) -> PyObject<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) }
}
/// Gets the python builtin value `True`.
#[allow(non_snake_case)] // the python keyword starts with uppercase
/// Gets the Python builtin value `True`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
pub fn True(self) -> PyBool<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_True()).unchecked_cast_into::<PyBool>() }
}
/// Gets the python builtin value `False`.
#[allow(non_snake_case)] // the python keyword starts with uppercase
/// Gets the Python builtin value `False`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
pub fn False(self) -> PyBool<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_False()).unchecked_cast_into::<PyBool>() }
}
/// Gets the python type object for type T.
/// Gets the Python type object for type T.
pub fn get_type<T>(self) -> PyType<'p> where T: PythonObjectWithTypeObject<'p> {
T::type_object(self)
}
/// Import the python module with the specified name.
/// Import the Python module with the specified name.
pub fn import(self, name : &str) -> PyResult<'p, PyModule<'p>> {
PyModule::import(self, name)
}

View File

@ -23,31 +23,45 @@ use objects::PyObject;
static START: Once = ONCE_INIT;
/// Prepares the use of python in a free-threaded context.
/// Prepares the use of Python in a free-threaded context.
///
/// If the python interpreter is not already initialized, this function
/// If the Python interpreter is not already initialized, this function
/// will initialize it with disabled signal handling
/// (python will not raise the `KeyboardInterrupt` exception).
/// (Python will not raise the `KeyboardInterrupt` exception).
/// Python signal handling depends on the notion of a 'main thread', which must be
/// the thread that initializes the python interpreter.
/// the thread that initializes the Python interpreter.
///
/// If both the Python interpreter and Python threading are already initialized,
/// this function has no effect.
///
/// # Panic
/// If the Python interpreter is initialized but Python threading is not,
/// a panic occurs.
/// It is not possible to safely access the Python runtime unless the main
/// thread (the thread which originally initialized Python) also initializes
/// threading.
///
/// When writing an extension module, the `py_module_initializer!` macro
/// will ensure that Python threading is initialized.
///
pub fn prepare_freethreaded_python() {
// Protect against race conditions when python is not yet initialized
// Protect against race conditions when Python is not yet initialized
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
// Note that we do not protect against concurrent initialization of the python runtime
// by other users of the python C API.
// Note that we do not protect against concurrent initialization of the Python runtime
// by other users of the Python C API.
START.call_once(|| unsafe {
if ffi::Py_IsInitialized() != 0 {
// If python is already initialized, we expect python threading to also be initialized,
// as we can't make the existing python main thread acquire the GIL.
// If Python is already initialized, we expect Python threading to also be initialized,
// as we can't make the existing Python main thread acquire the GIL.
assert!(ffi::PyEval_ThreadsInitialized() != 0);
} else {
// If python isn't initialized yet, we expect that python threading isn't initialized either.
// If Python isn't initialized yet, we expect that Python threading isn't initialized either.
assert!(ffi::PyEval_ThreadsInitialized() == 0);
// Initialize python.
// Initialize Python.
// We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
// Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
// Note that the 'main thread' notion in python isn't documented properly;
// and running python without one is not officially supported.
// Note that the 'main thread' notion in Python isn't documented properly;
// and running Python without one is not officially supported.
ffi::Py_InitializeEx(0);
ffi::PyEval_InitThreads();
// PyEval_InitThreads() will acquire the GIL,
@ -55,13 +69,23 @@ pub fn prepare_freethreaded_python() {
// (it's not acquired in the other code paths)
// So immediately release the GIL:
let _thread_state = ffi::PyEval_SaveThread();
// Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the python runtime,
// Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
// and will be restored by PyGILState_Ensure.
}
});
}
/// RAII type that represents an acquired GIL.
///
/// # Example
/// ```
/// use cpython::Python;
///
/// {
/// let gil_guard = Python::acquire_gil();
/// let py = gil_guard.python();
/// } // GIL is released when gil_guard is dropped
/// ```
#[must_use]
pub struct GILGuard {
gstate: ffi::PyGILState_STATE
@ -70,6 +94,9 @@ pub struct GILGuard {
/// GILGuard is not Send because the GIL must be released
/// by the same thread that acquired it.
impl !Send for GILGuard {}
/// GILGuard is not Sync because only the thread that
/// acquired the GIL may access the Python interpreter.
impl !Sync for GILGuard {}
/// The Drop implementation for GILGuard will release the GIL.
@ -81,9 +108,9 @@ impl Drop for GILGuard {
impl GILGuard {
/// Acquires the global interpreter lock, which allows access to the Python runtime.
/// If the python runtime is not already initialized, this function will initialize it.
/// Note that in this case, the python runtime will not have any main thread, and will
/// not deliver signals like KeyboardInterrupt.
///
/// If the Python runtime is not already initialized, this function will initialize it.
/// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
pub fn acquire() -> GILGuard {
::pythonrun::prepare_freethreaded_python();
let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL
@ -97,25 +124,49 @@ impl GILGuard {
}
}
/// Mutex-like wrapper object for data that is protected by the python GIL.
/// Mutex-like wrapper object for data that is protected by the Python GIL.
///
/// # Example
/// ```
/// use std::cell::Cell;
/// use cpython::{Python, GILProtected};
///
/// let data = GILProtected::new(Cell::new(0));
///
/// {
/// let gil_guard = Python::acquire_gil();
/// let cell = data.get(gil_guard.python());
/// cell.set(cell.get() + 1);
/// }
/// ```
pub struct GILProtected<T> {
data: T
}
unsafe impl<T: Send> Send for GILProtected<T> { }
/// Because `GILProtected` ensures that the contained data
/// is only accessed while the GIL is acquired,
/// it can implement `Sync` even if the contained data
/// does not.
unsafe impl<T: Send> Sync for GILProtected<T> { }
impl <T> GILProtected<T> {
/// Creates a new instance of `GILProtected`.
#[inline]
pub fn new(data: T) -> GILProtected<T> {
GILProtected { data: data }
}
/// Returns a shared reference to the data stored in the `GILProtected`.
///
/// Requires a `Python` instance as proof that the GIL is acquired.
#[inline]
pub fn get<'p>(&'p self, py: Python<'p>) -> &'p T {
pub fn get<'a>(&'a self, py: Python<'a>) -> &'a T {
&self.data
}
/// Consumes the `GILProtected`, returning the wrapped value.
#[inline]
pub fn into_inner(self) -> T {
self.data

View File

@ -25,7 +25,7 @@ use super::typebuilder::TypeMember;
use ffi;
use err;
/// Creates a python instance method descriptor that invokes a Rust function.
/// Creates a Python instance method descriptor that invokes a Rust function.
///
/// As arguments, takes the name of a rust function with the signature
/// `for<'p> fn(&PyRustObject<'p, T>, &PyTuple<'p>) -> PyResult<'p, R>`
@ -119,7 +119,7 @@ impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p>
}
/// Creates a python class method descriptor that invokes a Rust function.
/// Creates a Python class method descriptor that invokes a Rust function.
///
/// As arguments, takes the name of a rust function with the signature
/// `for<'p> fn(&PyType<'p>, &PyTuple<'p>) -> PyResult<'p, T>`

View File

@ -74,10 +74,10 @@ impl <'p> PythonBaseObject<'p> for PyObject<'p> {
}
}
/// A python object that contains a rust value of type T,
/// A Python object that contains a rust value of type T,
/// and is derived from base class B.
/// Note that this type effectively acts like `Rc<T>`,
/// except that the reference counting is done by the python runtime.
/// except that the reference counting is done by the Python runtime.
#[repr(C)]
pub struct PyRustObject<'p, T, B = PyObject<'p>> where T: 'static, B: PythonBaseObject<'p> {
obj: PyObject<'p>,
@ -99,7 +99,7 @@ impl <'p, T, B> PyRustObject<'p, T, B> where T: 'static + Send, B: PythonBaseObj
unsafe { B::unchecked_downcast_borrow_from(&self.obj) }
}
/// Gets a reference to the rust value stored in this python object.
/// Gets a reference to the rust value stored in this Python object.
#[inline]
pub fn get(&self) -> &T {
let offset = PyRustObject::<T, B>::offset() as isize;
@ -191,8 +191,8 @@ impl <'p, T, B> PythonObject<'p> for PyRustObject<'p, T, B> where T: 'static + S
}
}
/// A python class that contains rust values of type T.
/// Serves as a python type object, and can be used to construct
/// A Python class that contains rust values of type T.
/// Serves as a Python type object, and can be used to construct
/// `PyRustObject<T>` instances.
#[repr(C)]
pub struct PyRustType<'p, T, B = PyObject<'p>> where T: 'p + Send, B: PythonBaseObject<'p> {

View File

@ -163,9 +163,9 @@ impl <'p, T, B> PyRustTypeBuilder<'p, T, B> where T: 'static + Send, B: PythonBa
}
/// Represents something that can be added as a member to a python class/type.
/// Represents something that can be added as a member to a Python class/type.
///
/// T: type of rust class used for instances of the python class/type.
/// T: type of rust class used for instances of the Python class/type.
pub trait TypeMember<'p, T> where T: PythonObject<'p> {
fn into_descriptor(self, ty: &PyType<'p>, name: &str) -> PyObject<'p>;
}