Initial commit
This commit is contained in:
commit
7ccf5c88f1
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
/testmodule.so
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
|
||||
name = "rust-cpython"
|
||||
version = "0.0.1"
|
||||
authors = ["Daniel Grunwald <daniel@danielgrunwald.de>"]
|
||||
|
||||
[dependencies.libpython27-sys]
|
||||
path="../libpython27-sys"
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
.PHONY: build all test testextension debug
|
||||
|
||||
all: test
|
||||
|
||||
test:
|
||||
cargo test
|
||||
|
||||
testextension: testmodule.so
|
||||
python -c "import testmodule; print(repr(testmodule.__author__))"
|
||||
|
||||
debug: testmodule.so
|
||||
gdb --args python -c "import testmodule; print(repr(testmodule.__author__))"
|
||||
|
||||
target/librust-cpython-21cf8ea55e61f78d.rlib: src/*.rs Cargo.toml
|
||||
cargo build
|
||||
|
||||
testmodule.so: testmodule.rs target/librust-cpython-21cf8ea55e61f78d.rlib Makefile
|
||||
rustc testmodule.rs -g --extern rust-cpython=target/librust-cpython-21cf8ea55e61f78d.rlib --extern abort_on_panic=target/deps/libabort_on_panic-95c987fec9e5b445.rlib -o testmodule.so
|
||||
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
use libc::c_char;
|
||||
use std;
|
||||
use ffi;
|
||||
use err;
|
||||
use {Python, PyObject, PyResult, PythonObject, PyErr};
|
||||
use pyptr::{PyPtr, PythonPointer};
|
||||
|
||||
/// FromPyObject is implemented by various types that can be extracted from a python object.
|
||||
pub trait FromPyObject<'p, 'a> {
|
||||
fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, Self>;
|
||||
}
|
||||
|
||||
/// ToPyObject is implemented for types that can be converted into a python object.
|
||||
pub trait ToPyObject<'p> for Sized? {
|
||||
//type PointerType : 'p + PythonPointer + Deref<PyObject<'p>> = PyPtr<'p, PyObject<'p>>;
|
||||
|
||||
//fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, Self::PointerType>;
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>>;
|
||||
}
|
||||
|
||||
/// BorrowAsPyObject is implemented for types that can be accessed as a borrowed python object
|
||||
/// (without having to allocate a temporary python object)
|
||||
trait BorrowAsPyObject<'p> for Sized? {
|
||||
fn as_py_object(&self, py: Python<'p>) -> &PyObject<'p>;
|
||||
}
|
||||
// Note: I think BorrowAsPyObject is too restricted to be useful, we might as well use &PyObject.
|
||||
// On the other hand, we might want to optimize ToPyObject so that it doesn't always return a new
|
||||
// reference: it could return PyResult<A> with associated type A : 'p + PythonPointer + Deref<PyObject<'p>>.
|
||||
// Then types that can borrow existing python objects can return A=&'p PyObject<'p>,
|
||||
// while other types can return A=PyPtr<'p, PyObject<'p>>.
|
||||
|
||||
// impl ToPyObject for BorrowAsPyObject
|
||||
impl <'p, T : BorrowAsPyObject<'p>> ToPyObject<'p> for T {
|
||||
#[inline]
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
Ok(PyPtr::new(self.as_py_object(py)))
|
||||
}
|
||||
}
|
||||
|
||||
// PyObject, PyModule etc.
|
||||
// We support all three traits (FromPyObject, ToPyObject, BorrowAsPyObject) for
|
||||
// borrowed python references.
|
||||
// This allows using existing python objects in code that generically expects a value
|
||||
// convertible to a python object.
|
||||
impl <'p, T : PythonObject<'p>> BorrowAsPyObject<'p> for T {
|
||||
#[inline]
|
||||
fn as_py_object(&self, _: Python<'p>) -> &PyObject<'p> {
|
||||
self.as_object()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, 'a, T : PythonObject<'p>> FromPyObject<'p, 'a> for &'a T {
|
||||
#[inline]
|
||||
fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, &'a T> {
|
||||
s.downcast()
|
||||
}
|
||||
}
|
||||
|
||||
// PyPtr<T>
|
||||
// We support all three traits (FromPyObject, ToPyObject, BorrowAsPyObject) for
|
||||
// owned python references.
|
||||
// This allows using existing python objects in code that generically expects a value
|
||||
// convertible to a python object.
|
||||
|
||||
impl <'p, T : PythonObject<'p>> BorrowAsPyObject<'p> for PyPtr<'p, T> {
|
||||
#[inline]
|
||||
fn as_py_object(&self, _: Python<'p>) -> &PyObject<'p> {
|
||||
self.as_object()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, 'a, T : PythonObject<'p>> FromPyObject<'p, 'a> for PyPtr<'p, T> {
|
||||
#[inline]
|
||||
fn from_py_object(s : &'a PyObject<'p>) -> PyResult<'p, PyPtr<'p, T>> {
|
||||
PyPtr::new(s).downcast_into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// bool
|
||||
// As the bool instances have lifetime 'p, we can implement BorrowAsPyObject, not just ToPyObject
|
||||
impl <'p> BorrowAsPyObject<'p> for bool {
|
||||
#[inline]
|
||||
fn as_py_object(&self, py: Python<'p>) -> &PyObject<'p> {
|
||||
if *self { py.True() } else { py.False() }
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, 'a> FromPyObject<'p, 'a> for bool {
|
||||
fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, bool> {
|
||||
let py = s.python();
|
||||
if s == py.True() {
|
||||
Ok(true)
|
||||
} else if s == py.False() {
|
||||
Ok(false)
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strings.
|
||||
// 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 String.
|
||||
impl <'p> ToPyObject<'p> for str {
|
||||
fn to_py_object(&self, py : Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
let ptr : *const c_char = self.as_ptr() as *const _;
|
||||
let len : ffi::Py_ssize_t = std::num::from_uint(self.len()).unwrap();
|
||||
unsafe {
|
||||
use std::ascii::AsciiExt;
|
||||
let obj = if self.is_ascii() {
|
||||
ffi::PyString_FromStringAndSize(ptr, len)
|
||||
} else {
|
||||
ffi::PyUnicode_FromStringAndSize(ptr, len)
|
||||
};
|
||||
err::result_from_owned_ptr(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, 'a> FromPyObject<'p, 'a> for String {
|
||||
fn from_py_object(s : &'a PyObject<'p>) -> PyResult<'p, String> {
|
||||
string_as_slice(s).map(|buf| String::from_utf8_lossy(buf).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_as_slice<'a, 'p>(s : &'a PyObject<'p>) -> PyResult<'p, &'a [u8]> {
|
||||
unsafe {
|
||||
let mut buffer : *mut c_char = std::mem::uninitialized();
|
||||
let mut length : ffi::Py_ssize_t = std::mem::uninitialized();
|
||||
if ffi::PyString_AsStringAndSize(s.as_ptr(), &mut buffer, &mut length) == 1 {
|
||||
Err(PyErr::fetch(s.python()))
|
||||
} else {
|
||||
let buffer = buffer as *const u8;
|
||||
Ok(std::slice::from_raw_buf(std::mem::copy_lifetime(s, &buffer), length as uint))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
use std;
|
||||
use {PyObject, PythonObject, PyTypeObject, Python, PyPtr};
|
||||
use pyptr::PythonPointer;
|
||||
use ffi;
|
||||
use libc;
|
||||
|
||||
/// Represents a python exception that was raised.
|
||||
#[derive(Clone, Show)]
|
||||
pub struct PyErr<'p> {
|
||||
ptype : Option<PyPtr<'p, PyObject<'p>>>,
|
||||
pvalue : Option<PyPtr<'p, PyObject<'p>>>,
|
||||
ptraceback : Option<PyPtr<'p, PyObject<'p>>>
|
||||
}
|
||||
|
||||
|
||||
/// Represents the result of a python call.
|
||||
pub type PyResult<'p, T> = Result<T, PyErr<'p>>;
|
||||
pub type PyPtrResult<'p, T> = PyResult<'p, PyPtr<'p, T>>;
|
||||
|
||||
impl <'p> PyErr<'p> {
|
||||
/// Gets whether an error is present in the python interpreter's global state.
|
||||
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.
|
||||
pub fn fetch(py : Python<'p>) -> PyErr<'p> {
|
||||
unsafe {
|
||||
let mut ptype : *mut ffi::PyObject = std::mem::uninitialized();
|
||||
let mut pvalue : *mut ffi::PyObject = std::mem::uninitialized();
|
||||
let mut ptraceback : *mut ffi::PyObject = std::mem::uninitialized();
|
||||
ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback);
|
||||
PyErr {
|
||||
ptype: PyPtr::from_owned_ptr_opt(py, ptype),
|
||||
pvalue: PyPtr::from_owned_ptr_opt(py, pvalue),
|
||||
ptraceback: PyPtr::from_owned_ptr_opt(py, pvalue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores the error by writing it to the python interpreter's global state.
|
||||
pub fn restore(self) {
|
||||
let PyErr { ptype, pvalue, ptraceback } = self;
|
||||
unsafe {
|
||||
ffi::PyErr_Restore(ptype.steal_ptr(), pvalue.steal_ptr(), ptraceback.steal_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn type_error(obj : &PyObject<'p>, expected_type : &PyTypeObject<'p>) -> PyErr<'p> {
|
||||
let py = obj.python();
|
||||
PyErr {
|
||||
ptype: Some(unsafe { PyPtr::from_borrowed_ptr(py, ffi::PyExc_TypeError) }),
|
||||
pvalue: None,
|
||||
ptraceback: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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]
|
||||
pub unsafe fn result_from_owned_ptr(py : Python, p : *mut ffi::PyObject) -> PyResult<PyPtr<PyObject>> {
|
||||
if p.is_null() {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(PyPtr::from_owned_ptr(py, p))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Ok if the error code is 0.
|
||||
#[inline]
|
||||
pub fn result_from_error_code(py : Python, result : libc::c_int) -> PyResult<()> {
|
||||
if result == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PyErr::fetch(py))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#![feature(unsafe_destructor)]
|
||||
#![allow(unused_imports, dead_code, unused_variables)]
|
||||
#![feature(associated_types)]
|
||||
|
||||
extern crate libc;
|
||||
extern crate "libpython27-sys" as ffi;
|
||||
pub use ffi::Py_ssize_t;
|
||||
pub use err::{PyErr, PyResult};
|
||||
pub use python::Python;
|
||||
pub use pythonrun::PythonInterpreter;
|
||||
pub use object::{PythonObject, PyObject, ObjectProtocol};
|
||||
pub use typeobject::PyTypeObject;
|
||||
pub use pyptr::PyPtr;
|
||||
pub use module::PyModule;
|
||||
pub use conversion::{FromPyObject, ToPyObject};
|
||||
|
||||
// Fundamentals:
|
||||
mod python;
|
||||
mod object;
|
||||
mod pyptr;
|
||||
mod err;
|
||||
|
||||
// Object Types:
|
||||
mod typeobject;
|
||||
mod module;
|
||||
|
||||
mod pythonrun;
|
||||
mod conversion;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let interpreter = unsafe { PythonInterpreter::new() };
|
||||
let py = interpreter.python();
|
||||
let sys = PyModule::import(py, "sys").unwrap();
|
||||
let path = sys.as_object().getattr("path").unwrap();
|
||||
println!("{0}", path);
|
||||
panic!()
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
use std;
|
||||
use ffi;
|
||||
//use ffi::CStringAsPtr;
|
||||
//use {PyResult, PyErr, FromPyObject, PyTypeObject, ToPyObject, PyObject, Python, PyPtr, PythonObject};
|
||||
use {Python, PyPtr, PyResult, PyObject, PythonObject, PyTypeObject};
|
||||
use err;
|
||||
|
||||
pub struct PyModule<'p>(PyObject<'p>);
|
||||
|
||||
impl <'p> PythonObject<'p> for PyModule<'p> {
|
||||
fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyModule<'p>> {
|
||||
unsafe {
|
||||
if ffi::PyModule_Check(obj.as_ptr()) {
|
||||
Some(std::mem::transmute(obj))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn type_object(_ : Option<&Self>) -> &'p PyTypeObject<'p> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> PyModule<'p> {
|
||||
pub fn import<N : std::c_str::ToCStr>(py : Python<'p>, name : N) -> PyResult<PyPtr<PyModule<'p>>> {
|
||||
let result = name.with_c_str(|name| unsafe {
|
||||
err::result_from_owned_ptr(py, ffi::PyImport_ImportModule(name))
|
||||
});
|
||||
try!(result).downcast_into()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn as_module<'p>(py : &'p Python, obj : PyPtr<PyObject>) -> PyResult<'p, PyPtr<'p, PyModule>> {
|
||||
if py.module_type().is_instance(obj.deref()) {
|
||||
Ok(unsafe { PyPtr::from_owned_ptr(py, obj.steal_ptr()) })
|
||||
} else {
|
||||
Err(PyErr::type_error(py, obj.deref(), py.module_type()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyModule {
|
||||
|
||||
pub fn add_object<Sized? S : ToCStr, Sized? T : ToPyObject>
|
||||
(&self, name : &S, value : &T) -> PyResult<()>
|
||||
{
|
||||
let value = try!(value.to_py_object(self.python())).steal_ptr();
|
||||
let rc = name.with_c_str(|name| unsafe {
|
||||
ffi::PyModule_AddObject(self.as_ptr(), name, value)
|
||||
});
|
||||
err::result_from_error_code(self.python(), rc)
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
//use {PythonObject, PyPtr, PyResult, PyTypeObject, Python};
|
||||
use std;
|
||||
use std::cmp::Ordering;
|
||||
use libc;
|
||||
use ffi;
|
||||
use {Python, Py_ssize_t, PyResult, PyErr, PyPtr, ToPyObject};
|
||||
use typeobject::PyTypeObject;
|
||||
use err;
|
||||
|
||||
/// Trait implemented by all python object types.
|
||||
pub trait PythonObject<'p> {
|
||||
/// 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.
|
||||
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(unused_self : Option<&Self>) -> &'p PyTypeObject<'p>;
|
||||
|
||||
/// Retrieve python instance from an existing python object.
|
||||
fn python(&self) -> Python<'p> {
|
||||
self.as_object().python()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PyObject<'p> {
|
||||
cell : std::cell::UnsafeCell<ffi::PyObject>,
|
||||
py : Python<'p>
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/// Retrieves the underlying FFI pointer associated with this python object.
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.cell.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn python(&self) -> Python<'p> {
|
||||
self.py
|
||||
}
|
||||
|
||||
fn type_object(_ : Option<&Self>) -> &'p PyTypeObject<'p> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> PyObject<'p> {
|
||||
|
||||
|
||||
/// Retrieves the PyObject instance for the given FFI pointer.
|
||||
/// Undefined behavior if the pointer is NULL or invalid.
|
||||
/// Also, the output lifetime 'a is unconstrained, make sure to use a lifetime
|
||||
/// appropriate for the underlying FFI pointer.
|
||||
#[inline]
|
||||
pub unsafe fn from_ptr<'a>(_ : Python<'p>, p : *mut ffi::PyObject) -> &'a PyObject<'p> {
|
||||
debug_assert!(!p.is_null());
|
||||
&*(p as *mut PyObject)
|
||||
}
|
||||
|
||||
/// Retrieves the reference count of this python object.
|
||||
#[inline]
|
||||
pub fn get_refcnt(&self) -> Py_ssize_t {
|
||||
unsafe { ffi::Py_REFCNT(self.as_ptr()) }
|
||||
}
|
||||
|
||||
/*pub fn get_type(&self) -> &PyTypeObject {
|
||||
unsafe { PyTypeObject::from_type_ptr(self.python(), ffi::Py_TYPE(self.as_ptr())) }
|
||||
}*/
|
||||
|
||||
/// Casts the PyObject to a concrete python object type.
|
||||
/// Returns a python TypeError if the object is not of the expected type.
|
||||
pub fn downcast<T : PythonObject<'p>>(&self) -> PyResult<'p, &T> {
|
||||
let obj_opt : Option<&T> = PythonObject::from_object(self);
|
||||
match obj_opt {
|
||||
Some(obj) => Ok(obj),
|
||||
None => Err(PyErr::type_error(self, PythonObject::type_object(obj_opt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> std::fmt::Show for PyObject<'p> {
|
||||
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
let rep = try!(self.repr().map_err(|_| std::fmt::Error));
|
||||
let slice = try!(::conversion::string_as_slice(&*rep).map_err(|_| std::fmt::Error));
|
||||
f.write_str(try!(std::str::from_utf8(slice).map_err(|_| std::fmt::Error)))
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> PartialEq for PyObject<'p> {
|
||||
#[inline]
|
||||
fn eq(&self, o : &PyObject<'p>) -> bool {
|
||||
self.as_ptr() == o.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ObjectProtocol<'p> : PythonObject<'p> {
|
||||
/// Determines whether this object has the given attribute.
|
||||
/// This is equivalent to the Python expression 'hasattr(self, attr_name)'.
|
||||
#[inline]
|
||||
fn hasattr<Sized? N: ToPyObject<'p>>(&self, attr_name: &N) -> PyResult<'p, bool> {
|
||||
let py = self.python();
|
||||
let attr_name = try!(attr_name.to_py_object(py));
|
||||
unsafe {
|
||||
Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name.as_ptr()) != 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves an attribute value.
|
||||
/// This is equivalent to the Python expression 'self.attr_name'.
|
||||
#[inline]
|
||||
fn getattr<Sized? N: ToPyObject<'p>>(&self, attr_name: &N) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
let py = self.python();
|
||||
let attr_name = try!(attr_name.to_py_object(py));
|
||||
unsafe {
|
||||
err::result_from_owned_ptr(py, ffi::PyObject_GetAttr(self.as_ptr(), attr_name.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an attribute value.
|
||||
/// This is equivalent to the Python expression 'self.attr_name = value'.
|
||||
#[inline]
|
||||
fn setattr<Sized? N: ToPyObject<'p>, Sized? V: ToPyObject<'p>>
|
||||
(&self, attr_name: &N, value: &V) -> PyResult<'p, ()> {
|
||||
let py = self.python();
|
||||
let attr_name = try!(attr_name.to_py_object(py));
|
||||
let value = try!(value.to_py_object(py));
|
||||
unsafe {
|
||||
err::result_from_error_code(py,
|
||||
ffi::PyObject_SetAttr(self.as_ptr(), attr_name.as_ptr(), value.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes an attribute.
|
||||
/// This is equivalent to the Python expression 'del self.attr_name'.
|
||||
#[inline]
|
||||
fn delattr<Sized? N: ToPyObject<'p>>(&self, attr_name: &N) -> PyResult<'p, ()> {
|
||||
let py = self.python();
|
||||
let attr_name = try!(attr_name.to_py_object(py));
|
||||
unsafe {
|
||||
err::result_from_error_code(py,
|
||||
ffi::PyObject_DelAttr(self.as_ptr(), attr_name.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares two python objects.
|
||||
/// This is equivalent to the python expression 'cmp(self, other)'.
|
||||
fn compare(&self, other : &PyObject<'p>) -> PyResult<'p, Ordering> {
|
||||
unsafe {
|
||||
let mut result : libc::c_int = std::mem::uninitialized();
|
||||
try!(err::result_from_error_code(self.python(),
|
||||
ffi::PyObject_Cmp(self.as_ptr(), other.as_ptr(), &mut result)));
|
||||
Ok(if result < 0 {
|
||||
Ordering::Less
|
||||
} else if result > 0 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the string representation of self.
|
||||
/// This is equivalent to the python expression 'repr(self)'.
|
||||
fn repr(&self) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
unsafe {
|
||||
err::result_from_owned_ptr(self.python(), ffi::PyObject_Repr(self.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the string representation of self.
|
||||
/// This is equivalent to the python expression 'str(self)'.
|
||||
fn str(&self) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
unsafe {
|
||||
err::result_from_owned_ptr(self.python(), ffi::PyObject_Str(self.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the unicode string representation of self.
|
||||
/// This is equivalent to the python expression 'unistr(self)'.
|
||||
fn unistr(&self) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
unsafe {
|
||||
err::result_from_owned_ptr(self.python(), ffi::PyObject_Unicode(self.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether this object is callable.
|
||||
fn is_callable(&self) -> bool {
|
||||
unsafe {
|
||||
ffi::PyCallable_Check(self.as_ptr()) != 0
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, args: &PyObject<'p>, kw: Option<&PyObject<'p>>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn call_method<Sized? N: ToPyObject<'p>>(&self, name: &N, args: &PyObject<'p>, kw: Option<&PyObject<'p>>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
||||
try!(self.getattr(name)).call(args, kw)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> ObjectProtocol<'p> for PyObject<'p> {}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
use std;
|
||||
use std::ops::Deref;
|
||||
use ffi;
|
||||
use object::{PythonObject, PyObject};
|
||||
use err::{PyResult, PyErr};
|
||||
use python::Python;
|
||||
//use conversion::{FromPyObject, ToPyObject};
|
||||
//use PyResult;
|
||||
|
||||
|
||||
/// Owned pointer to python object.
|
||||
/// The PyPtr<T> owns one reference to a python object.
|
||||
/// Python objects are reference-counted, so it is possible to have
|
||||
/// multiple PyPtr<T> objects pointing to the same object, like Rc<T>.
|
||||
pub struct PyPtr<'p, T : 'p + PythonObject<'p>>(&'p T);
|
||||
|
||||
// impl Deref for PyPtr
|
||||
impl <'p, T : 'p + PythonObject<'p>> Deref for PyPtr<'p, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
debug_assert!(self.0.as_object().get_refcnt() > 0);
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// impl Drop for PyPtr
|
||||
#[unsafe_destructor]
|
||||
impl<'p, T : 'p + PythonObject<'p>> Drop for PyPtr<'p, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.0.as_object().get_refcnt() > 0);
|
||||
unsafe { ffi::Py_DECREF(self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
// impl Clone for PyPtr
|
||||
impl<'p, T : 'p + PythonObject<'p>> Clone for PyPtr<'p, T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> PyPtr<'p, T> {
|
||||
unsafe { ffi::Py_INCREF(self.as_ptr()) };
|
||||
PyPtr(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// impl Show for PyPtr
|
||||
impl<'p, T : 'p + PythonObject<'p> + std::fmt::Show> std::fmt::Show for PyPtr<'p, T> {
|
||||
#[inline]
|
||||
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
self.deref().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// impl PyPtr
|
||||
impl<'p, T : 'p + PythonObject<'p>> PyPtr<'p, T> {
|
||||
/// Creates a new PyPtr instance from a borrowed reference.
|
||||
/// This increments the reference count.
|
||||
#[inline]
|
||||
pub fn new(obj : &T) -> PyPtr<'p, T> {
|
||||
debug_assert!(obj.as_object().get_refcnt() > 0);
|
||||
let obj_extended_life : &T = unsafe {
|
||||
ffi::Py_INCREF(obj.as_ptr());
|
||||
// transmuting from &T to &'p T is safe because we just incremented the reference count,
|
||||
// and the &'p T is used only within the PyPtr -- the reference returned by Deref has
|
||||
// the lifetime restricted to the PyPtr's lifetime.
|
||||
std::mem::transmute(obj)
|
||||
};
|
||||
PyPtr(obj_extended_life)
|
||||
}
|
||||
}
|
||||
|
||||
/// The PythonPointer trait allows extracting an FFI pointer.
|
||||
pub trait PythonPointer {
|
||||
/// Gets the FFI pointer (borrowed reference).
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject;
|
||||
/// Gets the FFI pointer (owned reference).
|
||||
/// If the implementation of this trait is an owned pointer, this steals the reference.
|
||||
fn steal_ptr(self) -> *mut ffi::PyObject;
|
||||
}
|
||||
|
||||
impl <'p, T : 'p + PythonObject<'p>> PythonPointer for PyPtr<'p, T> {
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.deref().as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steal_ptr(self) -> *mut ffi::PyObject {
|
||||
// Destruct the PyPtr without decrementing the reference count
|
||||
let p = self.deref().as_ptr();
|
||||
unsafe { std::mem::forget(self) };
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
// &PyObject (etc.) is also a PythonPointer
|
||||
// (but steal_ptr increases the reference count)
|
||||
impl <'p, 'a, T : 'p + PythonObject<'p>> PythonPointer for &'a T {
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.deref().as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steal_ptr(self) -> *mut ffi::PyObject {
|
||||
PyPtr::new(self).steal_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
// Option<PythonPointer> can be used to extract a nullable FFI pointer.
|
||||
impl <T : PythonPointer> PythonPointer for Option<T> {
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
match *self {
|
||||
Some(ref p) => p.as_ptr(),
|
||||
None => std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steal_ptr(self) -> *mut ffi::PyObject {
|
||||
match self {
|
||||
Some(p) => p.steal_ptr(),
|
||||
None => std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl PyPtr<PyObject>
|
||||
impl<'p> PyPtr<'p, PyObject<'p>> {
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr(py : Python<'p>, p : *mut ffi::PyObject) -> PyPtr<'p, PyObject<'p>> {
|
||||
debug_assert!(!p.is_null() && ffi::Py_REFCNT(p) > 0);
|
||||
PyPtr(PyObject::from_ptr(py, p))
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr(py : Python<'p>, p : *mut ffi::PyObject) -> PyPtr<'p, PyObject<'p>> {
|
||||
debug_assert!(!p.is_null() && ffi::Py_REFCNT(p) > 0);
|
||||
ffi::Py_INCREF(p);
|
||||
PyPtr(PyObject::from_ptr(py, p))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr_opt(py : Python<'p>, p : *mut ffi::PyObject) -> Option<PyPtr<'p, PyObject<'p>>> {
|
||||
if p.is_null() { None } else { Some(PyPtr::from_owned_ptr(py, p)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr_opt(py : Python<'p>, p : *mut ffi::PyObject) -> Option<PyPtr<'p, PyObject<'p>>> {
|
||||
if p.is_null() { None } else { Some(PyPtr::from_borrowed_ptr(py, 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>> {
|
||||
// TODO: avoid unnecessary IncRef/DefRef
|
||||
self.deref().downcast().map(PyPtr::new)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
use std;
|
||||
use std::kinds::marker::{NoSend, NoCopy, InvariantLifetime};
|
||||
use std::ptr;
|
||||
use ffi;
|
||||
use std::c_str::CString;
|
||||
use object::PyObject;
|
||||
|
||||
/// 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 GIL is acquired.
|
||||
/// The lifetime 'p represents the lifetime of the python interpreter.
|
||||
/// For example, python constants like None have the type "&'p PyObject<'p>".
|
||||
/// You can imagine the GIL to be a giant "Mutex<AllPythonState>". This makes 'p the lifetime of the
|
||||
/// python state protected by that mutex.
|
||||
#[derive(Copy)]
|
||||
pub struct Python<'p>(NoSend, InvariantLifetime<'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.
|
||||
pub unsafe fn assume_gil_acquired() -> Python<'p> {
|
||||
Python(NoSend, InvariantLifetime)
|
||||
}
|
||||
|
||||
/// Retrieves a reference to the special 'None' value.
|
||||
#[allow(non_snake_case)] // the python keyword starts with uppercase
|
||||
#[inline]
|
||||
pub fn None(self) -> &'p PyObject<'p> {
|
||||
unsafe { PyObject::from_ptr(self, ffi::Py_None()) }
|
||||
}
|
||||
|
||||
/// Retrieves a reference to the 'True' constant value.
|
||||
#[allow(non_snake_case)] // the python keyword starts with uppercase
|
||||
#[inline]
|
||||
pub fn True(self) -> &'p PyObject<'p> {
|
||||
unsafe { PyObject::from_ptr(self, ffi::Py_True()) }
|
||||
}
|
||||
|
||||
/// Retrieves a reference to the 'False' constant value.
|
||||
#[allow(non_snake_case)] // the python keyword starts with uppercase
|
||||
#[inline]
|
||||
pub fn False(self) -> &'p PyObject<'p> {
|
||||
unsafe { PyObject::from_ptr(self, ffi::Py_False()) }
|
||||
}
|
||||
|
||||
/*
|
||||
/// Retrieve python instance from an existing PyObject.
|
||||
/// This can be used to avoid having to explicitly pass the &Python parameter to each function call.
|
||||
/// Note that the reference may point to a different memory location than the original &Python used to
|
||||
/// construct the object -- the Python type is just used as a token to prove that the GIL is currently held.
|
||||
pub fn from_object<T : PythonObject>(_ : &T) -> &Python {
|
||||
&STATIC_PYTHON_INSTANCE
|
||||
}
|
||||
|
||||
/// Acquires the global interpreter lock, which allows access to the Python runtime.
|
||||
/// This function is unsafe because
|
||||
/// This function is unsafe because it is possible to recursively acquire the GIL,
|
||||
/// and thus getting access to multiple '&mut Python' references to the python interpreter.
|
||||
pub unsafe fn acquire_gil(&self) -> GILGuard {
|
||||
let gstate = ffi::PyGILState_Ensure(); // acquire GIL
|
||||
GILGuard { py: NEW_PYTHON_INSTANCE, gstate: gstate }
|
||||
}
|
||||
|
||||
/// Releases the GIL and allows the use of python on other threads.
|
||||
pub fn allow_threads<T>(&mut self, f: fn() -> T) -> T {
|
||||
let save = unsafe { ffi::PyEval_SaveThread() };
|
||||
let result = f();
|
||||
unsafe { ffi::PyEval_RestoreThread(save); }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn module_type(&self) -> &PyTypeObject {
|
||||
unsafe { PyTypeObject::from_type_ptr(self, &mut ffi::PyModule_Type) }
|
||||
}
|
||||
|
||||
// Importing Modules
|
||||
|
||||
/// Imports the python with the given name.
|
||||
pub fn import_module<'s, N : ToCStr>(&'s self, name : N) -> PyResult<'s, PyPtr<'s, PyModule>> {
|
||||
use module;
|
||||
name.with_c_str(|name| unsafe {
|
||||
let m = ffi::PyImport_ImportModule(name);
|
||||
let m : PyPtr<PyObject> = try!(err::result_from_owned_ptr(self, m));
|
||||
module::as_module(self, m)
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new module object based on a name.
|
||||
pub fn init_module<Sized? N : ToCStr>
|
||||
(&self, name : &N, doc : Option<&CString>) -> PyResult<&PyModule>
|
||||
{
|
||||
let name = name.to_c_str();
|
||||
unsafe {
|
||||
ffi::PyEval_InitThreads();
|
||||
let m = ffi::Py_InitModule3(name.as_ptr(), ptr::null_mut(), doc.as_ptr());
|
||||
if m.is_null() {
|
||||
Err(PyErr::fetch(self))
|
||||
} else {
|
||||
Ok(PythonObject::from_ptr(self, m))
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
/// RAII type that represents an acquired GIL.
|
||||
#[must_use]
|
||||
pub struct GILGuard {
|
||||
gstate : ffi::PyGILState_STATE,
|
||||
py : Python
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl Drop for GILGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::PyGILState_Release(self.gstate) }
|
||||
}
|
||||
}
|
||||
|
||||
impl GILGuard {
|
||||
pub fn python(&mut self) -> &mut Python {
|
||||
&mut self.py
|
||||
}
|
||||
}*/
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
use ffi;
|
||||
use std::kinds::marker::{NoSend, NoCopy};
|
||||
use python::Python;
|
||||
|
||||
/// Represents the python interpreter instance.
|
||||
/// The python runtime is initialized using `PythonInterpreter::new()`,
|
||||
/// and destroyed when the PythonInterpreter is dropped.
|
||||
pub struct PythonInterpreter(NoSend, NoCopy);
|
||||
|
||||
#[must_use]
|
||||
impl PythonInterpreter {
|
||||
/// Initializes the python interpreter.
|
||||
/// Unsafe because we currently do not prevent multiple initialization, which is not supported.
|
||||
pub unsafe fn new() -> PythonInterpreter {
|
||||
ffi::Py_Initialize();
|
||||
ffi::PyEval_InitThreads();
|
||||
PythonInterpreter(NoSend, NoCopy)
|
||||
}
|
||||
|
||||
pub fn python<'p>(&'p self) -> Python<'p> {
|
||||
unsafe { Python::assume_gil_acquired() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl Drop for PythonInterpreter {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::Py_Finalize() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
use object::{PythonObject, PyObject};
|
||||
use python::Python;
|
||||
use ffi;
|
||||
use libc::c_char;
|
||||
use std;
|
||||
|
||||
pub struct PyTypeObject<'p> {
|
||||
cell : std::cell::UnsafeCell<ffi::PyTypeObject>,
|
||||
py : Python<'p>
|
||||
}
|
||||
|
||||
impl <'p> PythonObject<'p> for PyTypeObject<'p> {
|
||||
#[inline]
|
||||
fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyTypeObject<'p>> {
|
||||
unsafe {
|
||||
if ffi::PyType_Check(obj.as_ptr()) {
|
||||
Some(std::mem::transmute(obj))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn python(&self) -> Python<'p> {
|
||||
self.py
|
||||
}
|
||||
|
||||
fn type_object(_ : Option<&Self>) -> &'p PyTypeObject<'p> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl PyTypeObject {
|
||||
pub fn as_type_ptr(&self) -> *mut ffi::PyTypeObjectRaw {
|
||||
// safe because the PyObject is only accessed while holding the GIL
|
||||
(unsafe { self.cell.get() })
|
||||
}
|
||||
|
||||
pub unsafe fn from_type_ptr(_ : &Python, p : *mut ffi::PyTypeObjectRaw) -> &PyTypeObject {
|
||||
debug_assert!(p.is_not_null())
|
||||
&*(p as *mut PyTypeObject)
|
||||
}
|
||||
|
||||
/// Return true if self is a subtype of b.
|
||||
pub fn is_subtype_of(&self, b : &PyTypeObject) -> bool {
|
||||
unsafe { ffi::PyType_IsSubtype(self.as_type_ptr(), b.as_type_ptr()) != 0 }
|
||||
}
|
||||
|
||||
/// Return true if obj is an instance of self.
|
||||
pub fn is_instance(&self, obj : &PyObject) -> bool {
|
||||
obj.get_type().is_subtype_of(self)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@c:\cygwin\bin\touch src/lib.rs
|
||||
cargo build
|
||||
@if errorlevel 1 exit /b 1
|
||||
rustc testmodule.rs --extern rust-cpython=target\librust-cpython-211b85e007ec6e28.rlib -L "c:\Program Files\Python27\libs" -o testmodule.pyd
|
||||
@if errorlevel 1 exit /b 1
|
||||
python -c "import testmodule"
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
set -xeu
|
||||
#@c:\cygwin\bin\touch src/lib.rs
|
||||
cargo build
|
||||
rustc testmodule.rs --extern rust-cpython=target/librust-cpython-21cf8ea55e61f78d.rlib -o testmodule.so
|
||||
python -c "import testmodule; print(repr(testmodule.__author__))"
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#![crate_type = "dylib"]
|
||||
#![feature(phase)]
|
||||
|
||||
#[phase(plugin, link)] extern crate abort_on_panic;
|
||||
extern crate "rust-cpython" as cpython;
|
||||
extern crate rustrt;
|
||||
extern crate libc;
|
||||
|
||||
use cpython::{PyModule, PyResult, Python};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn inittestmodule() {
|
||||
//abort_on_panic!({
|
||||
let py = unsafe { Python::assume_gil_acquired() };
|
||||
if let Err(e) = init(py) {
|
||||
println!("Restore error")
|
||||
e.restore()
|
||||
}
|
||||
//})
|
||||
}
|
||||
|
||||
fn init(py : Python) -> PyResult<()> {
|
||||
let m : &PyModule = try!(py.init_module("testmodule", None));
|
||||
//println!("init_module done")
|
||||
try!(m.add_object("__author__", "Daniel Grunwald"));
|
||||
try!(m.add_object("__version__", "0.0.1"));
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue