Merge branch 'master' of https://github.com/dgrunwald/rust-cpython
This commit is contained in:
commit
387ca4b2d9
|
@ -4,7 +4,7 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst
|
|||
.PHONY: all clean
|
||||
|
||||
ifndef PY
|
||||
PY=3
|
||||
PY=2
|
||||
endif
|
||||
|
||||
all:
|
||||
|
@ -33,5 +33,6 @@ stamps/test-hello: hello.out
|
|||
@grep "Rust says: Hello Python!" hello.out >/dev/null
|
||||
@grep "Rust got 42" hello.out >/dev/null
|
||||
|
||||
all: inheritance.so
|
||||
|
||||
|
||||
|
|
24
extensions/inheritance.rs
Normal file
24
extensions/inheritance.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
#![crate_type = "dylib"]
|
||||
#![feature(plugin)]
|
||||
#![plugin(interpolate_idents)]
|
||||
|
||||
#[macro_use] extern crate cpython;
|
||||
|
||||
//use cpython::{PyObject, PyResult, PyModule, Python, PyTuple, PythonObject};
|
||||
|
||||
py_module_initializer!(inheritance, |_py, m| {
|
||||
try!(m.add("__doc__", "Module documentation string"));
|
||||
let B = try!(
|
||||
m.add_type::<()>("BaseClass")
|
||||
.doc("Type doc string")
|
||||
.finish());
|
||||
for i in 1..10 {
|
||||
try!(
|
||||
m.add_type::<()>(&format!("C{}", i))
|
||||
.base(&B)
|
||||
.doc(&format!("Derived class #{}", i))
|
||||
.finish());
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
|
@ -413,7 +413,6 @@ impl Clone for PyTypeObject {
|
|||
#[inline] fn clone(&self) -> PyTypeObject { *self }
|
||||
}
|
||||
|
||||
/* PyHeapTypeObject omitted, it doesn't seem to be part of the documented API
|
||||
#[repr(C)]
|
||||
#[derive(Copy)]
|
||||
pub struct PyHeapTypeObject {
|
||||
|
@ -426,11 +425,16 @@ pub struct PyHeapTypeObject {
|
|||
pub ht_slots: *mut PyObject,
|
||||
}
|
||||
|
||||
// access macro to the members which are floating "behind" the object
|
||||
#define PyHeapType_GET_MEMBERS(etype) \
|
||||
((PyMemberDef *)(((char *)etype) + Py_TYPE(etype)->tp_basicsize))
|
||||
*/
|
||||
impl Clone for PyHeapTypeObject {
|
||||
#[inline] fn clone(&self) -> PyHeapTypeObject { *self }
|
||||
}
|
||||
|
||||
// access macro to the members which are floating "behind" the object
|
||||
#[inline]
|
||||
pub unsafe fn PyHeapType_GET_MEMBERS(etype: *mut PyHeapTypeObject) -> *mut ::structmember::PyMemberDef {
|
||||
let basicsize = (*Py_TYPE(etype as *mut PyObject)).tp_basicsize;
|
||||
(etype as *mut u8).offset(basicsize as isize) as *mut ::structmember::PyMemberDef
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn PyType_IsSubtype(a: *mut PyTypeObject, b: *mut PyTypeObject) -> c_int;
|
||||
|
|
|
@ -171,9 +171,9 @@ fn run_python_script(script: &str) -> Result<String, String> {
|
|||
fn get_rustc_link_lib(version: &PythonVersion, enable_shared: bool) -> Result<String, String> {
|
||||
let dotted_version = format!("{}.{}", version.major, version.minor.unwrap());
|
||||
if enable_shared {
|
||||
Ok(format!("cargo:rustc-link-lib=python{}", dotted_version));
|
||||
Ok(format!("cargo:rustc-link-lib=python{}", dotted_version))
|
||||
} else {
|
||||
Ok(format!("cargo:rustc-link-lib=static=python{}", dotted_version));
|
||||
Ok(format!("cargo:rustc-link-lib=static=python{}", dotted_version))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,8 @@ pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonOb
|
|||
pub use pythonrun::{GILGuard, prepare_freethreaded_python};
|
||||
pub use conversion::{FromPyObject, ToPyObject};
|
||||
pub use objectprotocol::{ObjectProtocol};
|
||||
#[cfg(feature="python27-sys")]
|
||||
pub use rustobject::{PyRustTypeBuilder, PyRustType, PyRustObject};
|
||||
|
||||
/// Constructs a `&'static CStr` literal.
|
||||
macro_rules! cstr(
|
||||
|
@ -105,6 +107,8 @@ mod conversion;
|
|||
mod objects;
|
||||
mod objectprotocol;
|
||||
mod pythonrun;
|
||||
#[cfg(feature="python27-sys")]
|
||||
mod rustobject;
|
||||
|
||||
/// Private re-exports for macros. Do not use.
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -175,12 +175,12 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
|
|||
/// Returns the length of the sequence or mapping.
|
||||
/// This is equivalent to the python expression: 'len(self)'
|
||||
#[inline]
|
||||
fn len(&self) -> PyResult<'p, ffi::Py_ssize_t> {
|
||||
fn len(&self) -> PyResult<'p, usize> {
|
||||
let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
|
||||
if v == -1 {
|
||||
Err(PyErr::fetch(self.python()))
|
||||
} else {
|
||||
Ok(v)
|
||||
Ok(v as usize)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
|
||||
use std;
|
||||
use ffi;
|
||||
use python::{Python, PythonObject};
|
||||
use python::{Python, ToPythonPointer, PythonObject};
|
||||
use conversion::ToPyObject;
|
||||
use objects::PyObject;
|
||||
use err::{self, PyResult};
|
||||
use err::{self, PyResult, PyErr};
|
||||
|
||||
pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);
|
||||
|
||||
|
@ -34,5 +35,68 @@ impl <'p> PyDict<'p> {
|
|||
err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a new dictionary that contains the same key-value pairs as self.
|
||||
pub fn copy(&self) -> PyResult<'p, PyDict<'p>> {
|
||||
let py = self.python();
|
||||
unsafe {
|
||||
err::result_cast_from_owned_ptr(py, ffi::PyDict_Copy(self.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Empty an existing dictionary of all key-value pairs.
|
||||
#[inline]
|
||||
pub fn clear(&self) {
|
||||
unsafe { ffi::PyDict_Clear(self.as_ptr()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { ffi::PyDict_Size(self.as_ptr()) as usize }
|
||||
}
|
||||
|
||||
/// Determine if the dictionary contains the specified key.
|
||||
/// This is equivalent to the Python expression `key in self`.
|
||||
pub fn contains(&self, key: &PyObject<'p>) -> PyResult<'p, bool> {
|
||||
let py = self.python();
|
||||
unsafe {
|
||||
match ffi::PyDict_Contains(self.as_ptr(), key.as_ptr()) {
|
||||
1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
_ => Err(PyErr::fetch(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an item from the dictionary.
|
||||
/// Returns None if the item is not present, or if an error occurs.
|
||||
pub fn get_item<K>(&self, key: K) -> Option<PyObject<'p>> where K: ToPyObject<'p> {
|
||||
let py = self.python();
|
||||
key.with_borrowed_ptr(py, |key| unsafe {
|
||||
PyObject::from_borrowed_ptr_opt(py,
|
||||
ffi::PyDict_GetItem(self.as_ptr(), key))
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets an item value.
|
||||
/// This is equivalent to the Python expression `self[key] = value`.
|
||||
pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
|
||||
let py = self.python();
|
||||
key.with_borrowed_ptr(py, move |key|
|
||||
value.with_borrowed_ptr(py, |value| unsafe {
|
||||
err::error_on_minusone(py,
|
||||
ffi::PyDict_SetItem(self.as_ptr(), key, value))
|
||||
}))
|
||||
}
|
||||
|
||||
/// Deletes an item.
|
||||
/// This is equivalent to the Python expression 'del self[key]'.
|
||||
pub fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
|
||||
let py = self.python();
|
||||
key.with_borrowed_ptr(py, |key| unsafe {
|
||||
err::error_on_minusone(py,
|
||||
ffi::PyDict_DelItem(self.as_ptr(), key))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ impl <'p> PyModule<'p> {
|
|||
pub fn name<'a>(&'a self) -> PyResult<'p, &'a str> {
|
||||
unsafe { self.str_from_ptr(ffi::PyModule_GetName(self.as_ptr())) }
|
||||
}
|
||||
|
||||
|
||||
/// Gets the module filename.
|
||||
///
|
||||
/// May fail if the module does not have a __file__ attribute.
|
||||
|
@ -114,11 +114,17 @@ impl <'p> PyModule<'p> {
|
|||
///
|
||||
/// This is a convenience function which can be used from the module's initialization function.
|
||||
pub fn add<V>(&self, name: &str, value: V) -> PyResult<'p, ()> where V: ToPyObject<'p> {
|
||||
let py = self.python();
|
||||
let name = CString::new(name).unwrap();
|
||||
let value = value.into_py_object(py);
|
||||
let r = unsafe { ffi::PyModule_AddObject(self.as_ptr(), name.as_ptr(), value.steal_ptr()) };
|
||||
err::error_on_minusone(py, r)
|
||||
self.dict().set_item(name, value)
|
||||
}
|
||||
|
||||
/// Adds a new extension type to the module.
|
||||
///
|
||||
/// This is a convenience function that creates a new `PyRustTypeBuilder` and
|
||||
/// sets `new_type.__module__` to this module's name.
|
||||
/// The new type will be added to this module when `finish()` is called on the builder.
|
||||
#[cfg(feature="python27-sys")]
|
||||
pub fn add_type<T>(&self, name: &str) -> ::rustobject::PyRustTypeBuilder<'p, T> {
|
||||
::rustobject::new_typebuilder_for_module(self, name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ macro_rules! int_fits_c_long(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
impl <'p> FromPyObject<'p> for $rust_type {
|
||||
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
|
||||
|
@ -147,6 +147,84 @@ macro_rules! int_fits_larger_int(
|
|||
);
|
||||
|
||||
|
||||
fn err_if_invalid_value<'p, T: PartialEq, F: Fn() -> T>
|
||||
(obj: &PyObject<'p>, invalid_value: T, func: F) -> PyResult<'p, T> {
|
||||
let py = obj.python();
|
||||
let v = func();
|
||||
if v == invalid_value && PyErr::occurred(py) {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int_convert_u64_or_i64 (
|
||||
($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ull_or_ull:expr) => (
|
||||
impl <'p> ToPyObject<'p> for $rust_type {
|
||||
#[cfg(feature="python27-sys")]
|
||||
type ObjectType = PyObject<'p>;
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
type ObjectType = PyLong<'p>;
|
||||
|
||||
#[cfg(feature="python27-sys")]
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
|
||||
unsafe {
|
||||
let ptr = match num::traits::cast::<$rust_type, c_long>(*self) {
|
||||
Some(v) => ffi::PyInt_FromLong(v),
|
||||
None => $pylong_from_ll_or_ull(*self)
|
||||
};
|
||||
err::from_owned_ptr_or_panic(py, ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
|
||||
unsafe {
|
||||
err::cast_from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> FromPyObject<'p> for $rust_type {
|
||||
#[cfg(feature="python27-sys")]
|
||||
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
|
||||
let py = s.python();
|
||||
let ptr = s.as_ptr();
|
||||
|
||||
unsafe {
|
||||
if ffi::PyLong_Check(ptr) != 0 {
|
||||
err_if_invalid_value(s, !0, || $pylong_as_ull_or_ull(s.as_ptr()) )
|
||||
} else if ffi::PyInt_Check(ptr) != 0 {
|
||||
match num::traits::cast::<c_long, $rust_type>(ffi::PyInt_AS_LONG(ptr)) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(overflow_error(py))
|
||||
}
|
||||
} else {
|
||||
let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
|
||||
err_if_invalid_value(&num, !0, || $pylong_as_ull_or_ull(num.as_ptr()) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
|
||||
let py = s.python();
|
||||
let ptr = s.as_ptr();
|
||||
unsafe {
|
||||
if ffi::PyLong_Check(ptr) != 0 {
|
||||
err_if_invalid_value(s, !0, || $pylong_as_ull_or_ull(s.as_ptr()) )
|
||||
} else {
|
||||
let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
|
||||
err_if_invalid_value(&num, !0, || $pylong_as_ull_or_ull(num.as_ptr()) )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
int_fits_c_long!(i8);
|
||||
int_fits_c_long!(u8);
|
||||
int_fits_c_long!(i16);
|
||||
|
@ -161,9 +239,10 @@ int_fits_larger_int!(u32, u64);
|
|||
|
||||
#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
|
||||
int_fits_c_long!(i64);
|
||||
// TODO: manual implementation for i64 on systems with 32-bit long
|
||||
|
||||
// u64 has a manual implementation as it never fits into signed long
|
||||
// manual implementation for i64 on systems with 32-bit long
|
||||
#[cfg(any(target_pointer_width="32", target_os="windows"))]
|
||||
int_convert_u64_or_i64!(i64, ffi::PyLong_FromLongLong, ffi::PyLong_AsLongLong);
|
||||
|
||||
#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
|
||||
int_fits_c_long!(isize);
|
||||
|
@ -172,76 +251,8 @@ int_fits_larger_int!(isize, i64);
|
|||
|
||||
int_fits_larger_int!(usize, u64);
|
||||
|
||||
impl <'p> ToPyObject<'p> for u64 {
|
||||
#[cfg(feature="python27-sys")]
|
||||
type ObjectType = PyObject<'p>;
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
type ObjectType = PyLong<'p>;
|
||||
|
||||
#[cfg(feature="python27-sys")]
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
|
||||
unsafe {
|
||||
let ptr = match num::traits::cast::<u64, c_long>(*self) {
|
||||
Some(v) => ffi::PyInt_FromLong(v),
|
||||
None => ffi::PyLong_FromUnsignedLongLong(*self)
|
||||
};
|
||||
err::from_owned_ptr_or_panic(py, ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
|
||||
unsafe {
|
||||
err::cast_from_owned_ptr_or_panic(py, ffi::PyLong_FromUnsignedLongLong(*self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pylong_as_u64<'p>(obj: &PyObject<'p>) -> PyResult<'p, u64> {
|
||||
let py = obj.python();
|
||||
let v = unsafe { ffi::PyLong_AsUnsignedLongLong(obj.as_ptr()) };
|
||||
if v == !0 && PyErr::occurred(py) {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> FromPyObject<'p> for u64 {
|
||||
#[cfg(feature="python27-sys")]
|
||||
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, u64> {
|
||||
let py = s.python();
|
||||
let ptr = s.as_ptr();
|
||||
unsafe {
|
||||
if ffi::PyLong_Check(ptr) != 0 {
|
||||
pylong_as_u64(s)
|
||||
} else if ffi::PyInt_Check(ptr) != 0 {
|
||||
match num::traits::cast::<c_long, u64>(ffi::PyInt_AS_LONG(ptr)) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(overflow_error(py))
|
||||
}
|
||||
} else {
|
||||
let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
|
||||
pylong_as_u64(&num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="python3-sys")]
|
||||
fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, u64> {
|
||||
let py = s.python();
|
||||
let ptr = s.as_ptr();
|
||||
unsafe {
|
||||
if ffi::PyLong_Check(ptr) != 0 {
|
||||
pylong_as_u64(s)
|
||||
} else {
|
||||
let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
|
||||
pylong_as_u64(&num)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// u64 has a manual implementation as it never fits into signed long
|
||||
int_convert_u64_or_i64!(u64, ffi::PyLong_FromUnsignedLongLong, ffi::PyLong_AsUnsignedLongLong);
|
||||
|
||||
impl <'p> ToPyObject<'p> for f64 {
|
||||
type ObjectType = PyFloat<'p>;
|
||||
|
@ -328,7 +339,29 @@ mod test {
|
|||
assert_eq!(v as u64, obj.extract::<u64>().unwrap());
|
||||
assert!(obj.extract::<i32>().is_err());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_i64_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i64::MAX;
|
||||
let obj = v.to_py_object(py).into_object();
|
||||
assert_eq!(v, obj.extract::<i64>().unwrap());
|
||||
assert_eq!(v as u64, obj.extract::<u64>().unwrap());
|
||||
assert!(obj.extract::<u32>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_min() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i64::MIN;
|
||||
let obj = v.to_py_object(py).into_object();
|
||||
assert_eq!(v, obj.extract::<i64>().unwrap());
|
||||
assert!(obj.extract::<i32>().is_err());
|
||||
assert!(obj.extract::<u64>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u64_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -340,4 +373,3 @@ mod test {
|
|||
assert!(obj.extract::<i64>().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
326
src/rustobject.rs
Normal file
326
src/rustobject.rs
Normal file
|
@ -0,0 +1,326 @@
|
|||
// Copyright (c) 2015 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use libc;
|
||||
use ffi;
|
||||
use python::{Python, ToPythonPointer, PythonObject};
|
||||
use objects::{PyObject, PyType, PyString, PyModule, PyDict};
|
||||
use std::{mem, ops, ptr, marker};
|
||||
use err::{self, PyResult};
|
||||
|
||||
/// A PythonObject that is usable as a base type with PyTypeBuilder::base().
|
||||
pub trait PythonBaseObject<'p> : PythonObject<'p> {
|
||||
/// Gets the size of the object, in bytes.
|
||||
fn size() -> usize;
|
||||
}
|
||||
|
||||
impl <'p> PythonBaseObject<'p> for PyObject<'p> {
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
mem::size_of::<ffi::PyObject>()
|
||||
}
|
||||
}
|
||||
|
||||
/// A python object that contains a rust value of type T,
|
||||
/// and is derived from base class B.
|
||||
/// Note that this type effectively acts like `Rc<T>`,
|
||||
/// except that the reference counting is done by the python runtime.
|
||||
#[repr(C)]
|
||||
pub struct PyRustObject<'p, T, B = PyObject<'p>> where T: 'p, B: PythonBaseObject<'p> {
|
||||
obj: PyObject<'p>,
|
||||
/// The PyRustObject acts like a shared reference to the contained T.
|
||||
t: marker::PhantomData<&'p T>,
|
||||
b: marker::PhantomData<B>
|
||||
}
|
||||
|
||||
impl <'p, T, B> PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
#[inline] // this function can usually be reduced to a compile-time constant
|
||||
fn offset() -> usize {
|
||||
let align = mem::min_align_of::<T>();
|
||||
// round B::size() up to next multiple of align
|
||||
(B::size() + align - 1) / align * align
|
||||
}
|
||||
|
||||
/// Gets a reference to this object,
|
||||
#[inline]
|
||||
pub fn base(&self) -> &B {
|
||||
unsafe { B::unchecked_downcast_borrow_from(&self.obj) }
|
||||
}
|
||||
|
||||
/// Gets a reference to the rust value stored in this python object.
|
||||
#[inline]
|
||||
pub fn get(&self) -> &T {
|
||||
let offset = PyRustObject::<T, B>::offset() as isize;
|
||||
unsafe {
|
||||
let ptr = (self.obj.as_ptr() as *mut u8).offset(offset) as *mut T;
|
||||
&*ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> PythonBaseObject<'p> for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
PyRustObject::<T, B>::offset() + mem::size_of::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> Clone for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
PyRustObject {
|
||||
obj: self.obj.clone(),
|
||||
t: marker::PhantomData,
|
||||
b: marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> ToPythonPointer for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.obj.as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steal_ptr(self) -> *mut ffi::PyObject {
|
||||
self.obj.steal_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> PythonObject<'p> for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn as_object(&self) -> &PyObject<'p> {
|
||||
&self.obj
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_object(self) -> PyObject<'p> {
|
||||
self.obj
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_from(obj: PyObject<'p>) -> Self {
|
||||
PyRustObject {
|
||||
obj: obj,
|
||||
t: marker::PhantomData,
|
||||
b: marker::PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a PyObject<'p>) -> &'a Self {
|
||||
mem::transmute(obj)
|
||||
}
|
||||
}
|
||||
|
||||
/// A python class that contains rust values of type T.
|
||||
/// Serves as a python type object, and can be used to construct
|
||||
/// `PyRustObject<T>` instances.
|
||||
#[repr(C)]
|
||||
pub struct PyRustType<'p, T, B = PyObject<'p>> where T: 'p, B: PythonBaseObject<'p> {
|
||||
type_obj: PyType<'p>,
|
||||
phantom: marker::PhantomData<&'p (B, T)>
|
||||
}
|
||||
|
||||
impl <'p, T> PyRustType<'p, T, PyObject<'p>> {
|
||||
/// Creates a PyRustObject instance from a value.
|
||||
pub fn create_instance(&self, val: T) -> PyRustObject<'p, T> {
|
||||
let py = self.type_obj.python();
|
||||
unsafe {
|
||||
let obj = ffi::PyType_GenericAlloc(self.type_obj.as_type_ptr(), 0);
|
||||
if obj.is_null() {
|
||||
panic!("Out of memory")
|
||||
}
|
||||
let offset = PyRustObject::<T>::offset() as isize;
|
||||
ptr::write((obj as *mut u8).offset(offset) as *mut T, val);
|
||||
PyRustObject {
|
||||
obj: PyObject::from_owned_ptr(py, obj),
|
||||
t: marker::PhantomData,
|
||||
b: marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> ops::Deref for PyRustType<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
type Target = PyType<'p>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &PyType<'p> {
|
||||
&self.type_obj
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T: 'p> ToPythonPointer for PyRustType<'p, T> {
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.type_obj.as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn steal_ptr(self) -> *mut ffi::PyObject {
|
||||
self.type_obj.steal_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T: 'p> Clone for PyRustType<'p, T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
PyRustType {
|
||||
type_obj: self.type_obj.clone(),
|
||||
phantom: marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T: 'p> PythonObject<'p> for PyRustType<'p, T> {
|
||||
#[inline]
|
||||
fn as_object(&self) -> &PyObject<'p> {
|
||||
self.type_obj.as_object()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_object(self) -> PyObject<'p> {
|
||||
self.type_obj.into_object()
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_from(obj: PyObject<'p>) -> Self {
|
||||
PyRustType {
|
||||
type_obj: PyType::unchecked_downcast_from(obj),
|
||||
phantom: marker::PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a PyObject<'p>) -> &'a Self {
|
||||
mem::transmute(obj)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[must_use]
|
||||
pub struct PyRustTypeBuilder<'p, T, B = PyObject<'p>> where T: 'p, B: PythonBaseObject<'p> {
|
||||
type_obj: PyType<'p>,
|
||||
target_module: Option<PyModule<'p>>,
|
||||
ht: *mut ffi::PyHeapTypeObject,
|
||||
phantom: marker::PhantomData<&'p (B, T)>
|
||||
}
|
||||
|
||||
pub fn new_typebuilder_for_module<'p, T>(m: &PyModule<'p>, name: &str) -> PyRustTypeBuilder<'p, T> {
|
||||
let b = PyRustTypeBuilder::new(m.python(), name);
|
||||
if let Ok(mod_name) = m.name() {
|
||||
b.dict().set_item("__module__", mod_name).ok();
|
||||
}
|
||||
PyRustTypeBuilder { target_module: Some(m.clone()), .. b }
|
||||
}
|
||||
|
||||
impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'p {
|
||||
/// Create a new type builder.
|
||||
pub fn new(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> {
|
||||
unsafe {
|
||||
let obj = ffi::PyType_GenericAlloc(&mut ffi::PyType_Type, 0);
|
||||
if obj.is_null() {
|
||||
panic!("Out of memory")
|
||||
}
|
||||
let ht = obj as *mut ffi::PyHeapTypeObject;
|
||||
(*ht).ht_name = PyString::new(py, name.as_bytes()).steal_ptr();
|
||||
(*ht).ht_type.tp_name = ffi::PyString_AS_STRING((*ht).ht_name);
|
||||
(*ht).ht_type.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HEAPTYPE;
|
||||
PyRustTypeBuilder {
|
||||
type_obj: PyType::unchecked_downcast_from(PyObject::from_owned_ptr(py, obj)),
|
||||
target_module: None,
|
||||
ht: ht,
|
||||
phantom: marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base<T2, B2>(self, base_type: &PyRustType<'p, T2, B2>)
|
||||
-> PyRustTypeBuilder<'p, T, PyRustObject<'p, T2, B2>>
|
||||
where B2: PythonBaseObject<'p>
|
||||
{
|
||||
unsafe {
|
||||
ffi::Py_XDECREF((*self.ht).ht_type.tp_base as *mut ffi::PyObject);
|
||||
(*self.ht).ht_type.tp_base = base_type.as_type_ptr();
|
||||
ffi::Py_INCREF(base_type.as_ptr());
|
||||
}
|
||||
PyRustTypeBuilder {
|
||||
type_obj: self.type_obj,
|
||||
target_module: self.target_module,
|
||||
ht: self.ht,
|
||||
phantom: marker::PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dict(&self) -> PyDict<'p> {
|
||||
let py = self.type_obj.python();
|
||||
unsafe {
|
||||
if (*self.ht).ht_type.tp_dict.is_null() {
|
||||
(*self.ht).ht_type.tp_dict = PyDict::new(py).steal_ptr();
|
||||
}
|
||||
PyDict::unchecked_downcast_from(PyObject::from_borrowed_ptr(py, (*self.ht).ht_type.tp_dict))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> PyRustTypeBuilder<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
pub fn finish(self) -> PyResult<'p, PyRustType<'p, T, B>> {
|
||||
let py = self.type_obj.python();
|
||||
unsafe {
|
||||
(*self.ht).ht_type.tp_basicsize = PyRustObject::<T, B>::size() as ffi::Py_ssize_t;
|
||||
try!(err::error_on_minusone(py, ffi::PyType_Ready(self.type_obj.as_type_ptr())))
|
||||
}
|
||||
if let Some(m) = self.target_module {
|
||||
// Register the new type in the target module
|
||||
let name = unsafe { PyObject::from_borrowed_ptr(py, (*self.ht).ht_name) };
|
||||
try!(m.dict().set_item(name, self.type_obj.as_object()));
|
||||
}
|
||||
Ok(PyRustType {
|
||||
type_obj: self.type_obj,
|
||||
phantom: marker::PhantomData
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the doc string on the type being built.
|
||||
pub fn doc(self, doc_str: &str) -> Self {
|
||||
unsafe {
|
||||
if !(*self.ht).ht_type.tp_doc.is_null() {
|
||||
ffi::PyObject_Free((*self.ht).ht_type.tp_doc as *mut libc::c_void);
|
||||
}
|
||||
// ht_type.tp_doc must be allocated with PyObject_Malloc
|
||||
let p = ffi::PyObject_Malloc((doc_str.len() + 1) as libc::size_t);
|
||||
(*self.ht).ht_type.tp_doc = p as *const libc::c_char;
|
||||
if p.is_null() {
|
||||
panic!("Out of memory")
|
||||
}
|
||||
ptr::copy_nonoverlapping(doc_str.as_ptr(), p as *mut u8, doc_str.len() + 1);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue