Split PythonObjectDowncast from PythonObject
This commit is contained in:
parent
df14c894b2
commit
eca5b4039b
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -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() {
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
104
src/python.rs
104
src/python.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
|
Loading…
Reference in a new issue