Initial commit

This commit is contained in:
Daniel Grunwald 2015-01-05 17:05:53 +01:00
commit 7ccf5c88f1
15 changed files with 1004 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/target
/Cargo.lock
/testmodule.so

11
Cargo.toml Normal file
View File

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

20
Makefile Normal file
View File

@ -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

139
src/conversion.rs Normal file
View File

@ -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))
}
}
}

82
src/err.rs Normal file
View File

@ -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))
}
}

39
src/lib.rs Normal file
View File

@ -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!()
}

62
src/module.rs Normal file
View File

@ -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)
}
}
*/

222
src/object.rs Normal file
View File

@ -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> {}

164
src/pyptr.rs Normal file
View File

@ -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)
}
}

126
src/python.rs Normal file
View File

@ -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
}
}*/

32
src/pythonrun.rs Normal file
View File

@ -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() }
}
}

61
src/typeobject.rs Normal file
View File

@ -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)
}
}
*/

6
test.bat Normal file
View File

@ -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"

7
test.sh Executable file
View File

@ -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__))"

29
testmodule.rs Normal file
View File

@ -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(())
}