Improve documentation.
This commit is contained in:
parent
6b496b4772
commit
3b02ef5b99
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
src/err.rs
37
src/err.rs
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
38
src/lib.rs
38
src/lib.rs
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 module‘s 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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 object’s 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 object’s 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 object’s __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 object’s __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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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>());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>`
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue