Split PythonObjectDowncast from PythonObject

This commit is contained in:
Daniel Grunwald 2015-01-05 00:07:31 +01:00
parent df14c894b2
commit eca5b4039b
10 changed files with 153 additions and 125 deletions

View file

@ -1,11 +1,11 @@
[package]
name = "rust-cpython"
name = "cpython"
version = "0.0.1"
description = "Bindings to Python 2.7"
authors = ["Daniel Grunwald <daniel@danielgrunwald.de>"]
[dependencies.libpython27-sys]
path="../libpython27-sys"
[dependencies]
python27-sys="0.0.1"

View file

@ -2,7 +2,7 @@ use libc::c_char;
use std;
use ffi;
use err;
use {Python, PyObject, PyResult, PythonObject, PyErr};
use {Python, PyObject, PyResult, PythonObject, PythonObjectDowncast, PyErr};
use pyptr::{PyPtr, PythonPointer};
/// ToPyObject is implemented for types that can be converted into a python object.
@ -71,15 +71,16 @@ pub trait FromPyObject<'p, 's> {
// This allows using existing python objects in code that generically expects a value
// convertible to a python object.
impl <'p, 's, T : PythonObject<'p>> ToPyObject<'p, 's> for T {
impl <'p, 's, T> ToPyObject<'p, 's> for T where T : PythonObject<'p> {
type PointerType = &'s PyObject<'p>;
#[inline]
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s PyObject<'p>> {
Ok(self.as_object())
}
}
impl <'p, 's, T : PythonObject<'p>> FromPyObject<'p, 's> for &'s T {
impl <'p, 's, T> FromPyObject<'p, 's> for &'s T where T: PythonObjectDowncast<'p> {
#[inline]
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, &'s T> {
s.downcast()
@ -87,20 +88,20 @@ impl <'p, 's, T : PythonObject<'p>> FromPyObject<'p, 's> for &'s T {
}
// PyPtr<T>
// We support all three traits (FromPyObject, ToPyObject, BorrowAsPyObject) for
// owned python references.
// 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.
// convertible to a python object, without having to re-borrow the &PyObject.
impl <'p, 's, T : PythonObject<'p>> ToPyObject<'p, 's> for PyPtr<'p, T> {
impl <'p, 's, T> ToPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObject<'p> {
type PointerType = &'s PyObject<'p>;
#[inline]
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s PyObject<'p>> {
Ok(self.as_object())
}
}
impl <'p, 's, T : PythonObject<'p>> FromPyObject<'p, 's> for PyPtr<'p, T> {
impl <'p, 's, T> FromPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObjectDowncast<'p> {
#[inline]
fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, PyPtr<'p, T>> {
PyPtr::new(s).downcast_into()

View file

@ -5,11 +5,11 @@
#![feature(slicing_syntax)]
extern crate libc;
extern crate "libpython27-sys" as ffi;
extern crate "python27-sys" as ffi;
pub use ffi::Py_ssize_t;
pub use err::{PyErr, PyResult};
pub use python::Python;
pub use object::{PythonObject, PyObject};
pub use python::{Python, PythonObject, PythonObjectDowncast};
pub use object::PyObject;
pub use typeobject::PyType;
pub use pyptr::PyPtr;
pub use module::PyModule;
@ -18,17 +18,18 @@ pub use objectprotocol::{ObjectProtocol};
// Fundamentals:
mod python;
mod object;
mod pyptr;
mod err;
mod conversion;
// Object Types:
mod object;
mod typeobject;
mod module;
// Python APIs:
mod objectprotocol;
mod pythonrun;
mod conversion;
#[test]
fn it_works() {

View file

@ -1,11 +1,22 @@
use std;
use ffi;
use {Python, PyPtr, PyResult, PyObject, PythonObject, PyType};
use err;
use python::{Python, PythonObject, PythonObjectDowncast};
use object::PyObject;
use typeobject::PyType;
use pyptr::PyPtr;
use err::{self, PyResult};
pub struct PyModule<'p>(PyObject<'p>);
impl <'p> PythonObject<'p> for PyModule<'p> {
#[inline]
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
&self.0
}
}
impl <'p> PythonObjectDowncast<'p> for PyModule<'p> {
#[inline]
fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyModule<'p>> {
unsafe {
if ffi::PyModule_Check(obj.as_ptr()) {
@ -16,10 +27,6 @@ impl <'p> PythonObject<'p> for PyModule<'p> {
}
}
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
&self.0
}
#[inline]
fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> {
unsafe { PyType::from_type_ptr(py, &mut ffi::PyModule_Type) }

View file

@ -1,39 +1,10 @@
use std;
use libc;
use ffi;
use python::{Python, PythonObject, PythonObjectDowncast};
use typeobject::PyType;
use python::Python;
use err::{PyErr, PyResult};
/// Trait implemented by all python object types.
pub trait PythonObject<'p> : 'p {
// TODO: split this trait; not every PythonObject impl has a statically known type,
// or the ability to perform a typecheck
/// Upcast from PyObject to a concrete python object type.
/// Returns None if the python object is not of the specified type.
fn from_object<'a>(&'a PyObject<'p>) -> Option<&'a Self>;
/// Casts the python object to PyObject.
fn as_object<'a>(&'a self) -> &'a PyObject<'p>;
/// Retrieves the underlying FFI pointer associated with this python object.
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.as_object().as_ptr()
}
/// Retrieves the type object for this python object type.
/// unused_self is necessary until UFCS is implemented.
fn type_object(py: Python<'p>, unused_self : Option<&Self>) -> &'p PyType<'p>;
/// Retrieve python instance from an existing python object.
#[inline]
fn python(&self) -> Python<'p> {
self.as_object().python()
}
}
pub struct PyObject<'p> {
cell : std::cell::UnsafeCell<ffi::PyObject>,
py : Python<'p>
@ -46,11 +17,6 @@ fn test_sizeof() {
}
impl <'p> PythonObject<'p> for PyObject<'p> {
#[inline]
fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyObject<'p>> {
Some(obj)
}
#[inline]
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
self
@ -66,6 +32,13 @@ impl <'p> PythonObject<'p> for PyObject<'p> {
fn python(&self) -> Python<'p> {
self.py
}
}
impl <'p> PythonObjectDowncast<'p> for PyObject<'p> {
#[inline]
fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyObject<'p>> {
Some(obj)
}
#[inline]
fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> {
@ -100,11 +73,11 @@ impl <'p> PyObject<'p> {
/// Casts the PyObject to a concrete python object type.
/// Returns a python TypeError if the object is not of the expected type.
#[inline]
pub fn downcast<T : PythonObject<'p>>(&self) -> PyResult<'p, &T> {
let obj_opt : Option<&T> = PythonObject::from_object(self);
pub fn downcast<T : PythonObjectDowncast<'p>>(&self) -> PyResult<'p, &T> {
let obj_opt : Option<&T> = PythonObjectDowncast::from_object(self);
match obj_opt {
Some(obj) => Ok(obj),
None => Err(PyErr::type_error(self, PythonObject::type_object(self.python(), obj_opt)))
None => Err(PyErr::type_error(self, PythonObjectDowncast::type_object(self.python(), obj_opt)))
}
}
}

View file

@ -2,8 +2,8 @@ use std;
use std::cmp::Ordering;
use ffi;
use libc;
use python::{Python};
use object::{PythonObject, PyObject};
use python::{Python, PythonObject};
use object::PyObject;
use pyptr::{PyPtr, PythonPointer, as_ptr};
use conversion::ToPyObject;
use err::{PyErr, PyResult, result_from_owned_ptr, error_on_minusone};

View file

@ -1,9 +1,9 @@
use std;
use std::ops::Deref;
use ffi;
use object::{PythonObject, PyObject};
use python::{Python, PythonObject, PythonObjectDowncast};
use object::PyObject;
use err::{PyResult, PyErr};
use python::Python;
//use conversion::{FromPyObject, ToPyObject};
//use PyResult;
@ -181,7 +181,7 @@ impl<'p> PyPtr<'p, PyObject<'p>> {
/// Casts the PyPtr<PyObject> to a PyPtr of a concrete python object type.
/// Returns a python TypeError if the object is not of the expected type.
#[inline]
pub fn downcast_into<T : PythonObject<'p>>(self) -> PyResult<'p, PyPtr<'p, T>> {
pub fn downcast_into<T>(self) -> PyResult<'p, PyPtr<'p, T>> where T: PythonObjectDowncast<'p> {
// TODO: avoid unnecessary IncRef/DefRef
self.deref().downcast().map(PyPtr::new)
}

View file

@ -1,10 +1,10 @@
use std;
use std::kinds::marker::{NoSend, NoCopy, InvariantLifetime};
use std::kinds::marker::{NoSend, InvariantLifetime};
use std::ptr;
use ffi;
use std::c_str::CString;
use object::{PythonObject, PyObject};
use object::PyObject;
use typeobject::PyType;
use pythonrun::GILGuard;
/// 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,
@ -16,13 +16,66 @@ use typeobject::PyType;
#[derive(Copy)]
pub struct Python<'p>(NoSend, InvariantLifetime<'p>);
/// Trait implemented by all python object types.
pub trait PythonObject<'p> : 'p {
/// Casts the python object to PyObject.
fn as_object(&self) -> &PyObject<'p>;
/// Retrieves the underlying FFI pointer associated with this python object.
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.as_object().as_ptr()
}
/// Retrieve python instance from an existing python object.
#[inline]
fn python(&self) -> Python<'p> {
self.as_object().python()
}
}
/// Trait implemented by python object types that allow a checked downcast.
pub trait PythonObjectDowncast<'p> : PythonObject<'p> {
// TODO: maybe add 'unsafe fn from_object_unchecked' to PythonObject/PythonObjectDowncast,
// and then implement from_object using type_object().is_instance() ?
/// Upcast from PyObject to a concrete python object type.
/// Returns None if the python object is not of the specified type.
fn from_object<'a>(&'a PyObject<'p>) -> Option<&'a Self>;
/// Retrieves the type object for this python object type.
/// Option<&Self> is necessary until UFCS is implemented.
fn type_object(Python<'p>, Option<&Self>) -> &'p PyType<'p>;
}
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.
/// and stays acquired for the lifetime 'p
#[inline]
pub unsafe fn assume_gil_acquired() -> Python<'p> {
Python(NoSend, InvariantLifetime)
}
/// 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.
#[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
/// are not accessed within the closure.
pub unsafe fn allow_threads<T, F>(self, f: F) -> T where F : FnOnce() -> T {
// TODO: we should use a type with destructor to be panic-safe, and avoid the unnecessary closure
let save = ffi::PyEval_SaveThread();
let result = f();
ffi::PyEval_RestoreThread(save);
result
}
/// Retrieves a reference to the special 'None' value.
#[allow(non_snake_case)] // the python keyword starts with uppercase
#[inline]
@ -46,48 +99,9 @@ impl<'p> Python<'p> {
/// Retrieves a reference to the type object for type T.
#[inline]
pub fn get_type<T>(self) -> &'p PyType<'p> where T: PythonObject<'p> {
pub fn get_type<T>(self) -> &'p PyType<'p> where T: PythonObjectDowncast<'p> {
let none : Option<&T> = None;
PythonObject::type_object(self, none)
}
/// 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.
pub fn acquire_gil() -> GILGuard {
::pythonrun::prepare_freethreaded_python();
let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL
GILGuard { gstate: gstate, marker: NoSend }
}
/// 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 {
let save = ffi::PyEval_SaveThread();
let result = f();
ffi::PyEval_RestoreThread(save);
result
}
}
/// RAII type that represents an acquired GIL.
#[must_use]
pub struct GILGuard {
gstate: ffi::PyGILState_STATE,
marker: NoSend
}
impl Drop for GILGuard {
fn drop(&mut self) {
unsafe { ffi::PyGILState_Release(self.gstate) }
}
}
impl GILGuard {
pub fn python<'p>(&'p self) -> Python<'p> {
unsafe { Python::assume_gil_acquired() }
PythonObjectDowncast::type_object(self, none)
}
}

View file

@ -1,7 +1,8 @@
use ffi;
use std::sync::{Once, ONCE_INIT};
use std::thread::Thread;
use std::kinds::marker::NoSend;
use ffi;
use python::Python;
static START: Once = ONCE_INIT;
@ -37,4 +38,33 @@ pub fn prepare_freethreaded_python() {
});
}
/// RAII type that represents an acquired GIL.
#[must_use]
pub struct GILGuard {
gstate: ffi::PyGILState_STATE,
marker: NoSend
}
impl Drop for GILGuard {
fn drop(&mut self) {
unsafe { ffi::PyGILState_Release(self.gstate) }
}
}
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.
pub fn acquire() -> GILGuard {
::pythonrun::prepare_freethreaded_python();
let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL
GILGuard { gstate: gstate, marker: NoSend }
}
pub fn python<'p>(&'p self) -> Python<'p> {
unsafe { Python::assume_gil_acquired() }
}
}

View file

@ -1,5 +1,5 @@
use object::{PythonObject, PyObject};
use python::Python;
use python::{Python, PythonObject, PythonObjectDowncast};
use object::PyObject;
use ffi;
use libc::c_char;
use std;
@ -16,6 +16,18 @@ fn test_sizeof() {
}
impl <'p> PythonObject<'p> for PyType<'p> {
#[inline]
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
unsafe { std::mem::transmute(self) }
}
#[inline]
fn python(&self) -> Python<'p> {
self.py
}
}
impl <'p> PythonObjectDowncast<'p> for PyType<'p> {
#[inline]
fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyType<'p>> {
unsafe {
@ -26,17 +38,7 @@ impl <'p> PythonObject<'p> for PyType<'p> {
}
}
}
#[inline]
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
unsafe { std::mem::transmute(self) }
}
#[inline]
fn python(&self) -> Python<'p> {
self.py
}
#[inline]
fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> {
unsafe { PyType::from_type_ptr(py, &mut ffi::PyType_Type) }