pyo3/src/typeob.rs

347 lines
11 KiB
Rust
Raw Normal View History

2017-05-16 18:58:18 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
2017-05-25 03:31:51 +00:00
use std;
2017-05-16 18:58:18 +00:00
use std::mem;
2017-06-07 02:26:59 +00:00
use std::ffi::{CStr, CString};
2017-05-16 18:58:18 +00:00
use std::collections::HashMap;
2017-05-25 03:31:51 +00:00
use {ffi, class};
use err::{PyErr, PyResult};
use python::{Python, IntoPyPointer};
use objects::{PyType, PyDict};
2017-05-17 06:43:39 +00:00
use callback::AbortOnDrop;
2017-05-25 03:31:51 +00:00
use class::methods::PyMethodDefType;
2017-05-16 18:58:18 +00:00
2017-05-23 06:53:14 +00:00
/// Basic python type information
/// Implementing this trait for custom struct is enough to make it compatible with
/// python object system
pub trait PyTypeInfo {
2017-05-23 06:53:14 +00:00
/// Type of objects to store in PyObject struct
type Type;
2017-05-19 04:35:08 +00:00
2017-05-23 06:53:14 +00:00
/// Size of the PyObject structure
2017-05-22 05:22:45 +00:00
fn size() -> usize;
2017-05-23 06:53:14 +00:00
/// `Type` instance offset inside PyObject structure
fn offset() -> isize;
2017-05-22 05:22:45 +00:00
2017-05-23 06:53:14 +00:00
/// Type name
2017-05-19 04:35:08 +00:00
fn type_name() -> &'static str;
2017-05-23 06:53:14 +00:00
/// PyTypeObject instance for this type
2017-05-19 04:35:08 +00:00
fn type_object() -> &'static mut ffi::PyTypeObject;
}
impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo {
type Type = T::Type;
#[inline]
default fn size() -> usize {
<T as PyTypeInfo>::size()
}
#[inline]
default fn offset() -> isize {
<T as PyTypeInfo>::offset()
}
#[inline]
default fn type_name() -> &'static str {
<T as PyTypeInfo>::type_name()
}
#[inline]
default fn type_object() -> &'static mut ffi::PyTypeObject {
<T as PyTypeInfo>::type_object()
}
}
2017-05-25 03:31:51 +00:00
pub trait PyObjectAlloc {
type Type;
/// Allocates a new object (usually by calling ty->tp_alloc),
2017-06-09 19:30:13 +00:00
/// and initializes it using value.
2017-05-28 15:57:34 +00:00
unsafe fn alloc(py: Python, value: Self::Type) -> PyResult<*mut ffi::PyObject>;
2017-05-25 03:31:51 +00:00
/// Calls the rust destructor for the object and frees the memory
/// (usually by calling ptr->ob_type->tp_free).
/// This function is used as tp_dealloc implementation.
2017-05-28 15:57:34 +00:00
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject);
2017-05-25 03:31:51 +00:00
}
2017-06-09 19:30:13 +00:00
/// A Python object allocator that is usable as a base type for #[class]
2017-05-25 03:31:51 +00:00
impl<T> PyObjectAlloc for T where T : PyTypeInfo {
type Type = T::Type;
/// Allocates a new object (usually by calling ty->tp_alloc),
2017-06-09 19:30:13 +00:00
/// and initializes it using value.
default unsafe fn alloc(py: Python, value: Self::Type) -> PyResult<*mut ffi::PyObject> {
2017-06-07 02:26:59 +00:00
// TODO: remove this
2017-06-08 21:21:48 +00:00
<T as PyTypeObject>::init_type(py);
2017-05-27 18:43:45 +00:00
2017-05-25 03:31:51 +00:00
let obj = ffi::PyType_GenericAlloc(
<Self as PyTypeInfo>::type_object(), 0);
let offset = <Self as PyTypeInfo>::offset();
let ptr = (obj as *mut u8).offset(offset) as *mut Self::Type;
std::ptr::write(ptr, value);
Ok(obj)
}
/// Calls the rust destructor for the object and frees the memory
/// (usually by calling ptr->ob_type->tp_free).
/// This function is used as tp_dealloc implementation.
2017-06-09 19:30:13 +00:00
default unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) {
let ptr = (obj as *mut u8).offset(<Self as PyTypeInfo>::offset()) as *mut Self::Type;
2017-05-25 03:31:51 +00:00
std::ptr::drop_in_place(ptr);
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut ::c_void);
} else {
ffi::PyObject_Free(obj as *mut ::c_void);
}
// For heap types, PyType_GenericAlloc calls INCREF on the type objects,
// so we need to call DECREF here:
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
}
}
2017-05-22 05:22:45 +00:00
2017-05-23 06:53:14 +00:00
/// Trait implemented by Python object types that have a corresponding type object.
pub trait PyTypeObject {
2017-06-08 21:21:48 +00:00
/// Initialize type object
fn init_type(py: Python);
2017-05-23 06:53:14 +00:00
/// Retrieves the type object for this Python object type.
2017-06-03 01:58:16 +00:00
fn type_object(py: Python) -> PyType;
2017-05-23 06:53:14 +00:00
}
2017-05-25 03:31:51 +00:00
impl<T> PyTypeObject for T where T: PyObjectAlloc + PyTypeInfo {
2017-05-16 18:58:18 +00:00
2017-05-19 04:35:08 +00:00
#[inline]
2017-06-08 21:21:48 +00:00
default fn init_type(py: Python) {
let mut ty = <T as PyTypeInfo>::type_object();
2017-05-16 18:58:18 +00:00
2017-06-08 21:21:48 +00:00
if (ty.tp_flags & ffi::Py_TPFLAGS_READY) == 0 {
2017-05-19 04:35:08 +00:00
// automatically initialize the class on-demand
2017-06-07 02:26:59 +00:00
let to = initialize_type::<T>(
py, None, <T as PyTypeInfo>::type_name(), ty).expect(
2017-05-19 04:35:08 +00:00
format!("An error occurred while initializing class {}",
<T as PyTypeInfo>::type_name()).as_ref());
2017-06-07 02:26:59 +00:00
py.release(to);
2017-05-19 04:35:08 +00:00
}
}
2017-06-08 21:21:48 +00:00
#[inline]
default fn type_object(py: Python) -> PyType {
<T as PyTypeObject>::init_type(py);
unsafe { PyType::from_type_ptr(py, <T as PyTypeInfo>::type_object()) }
}
2017-05-16 18:58:18 +00:00
}
2017-06-08 21:21:48 +00:00
2017-06-03 01:58:16 +00:00
pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str,
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>
2017-05-25 03:31:51 +00:00
where T: PyObjectAlloc + PyTypeInfo
2017-05-17 06:43:39 +00:00
{
// type name
let name = match module_name {
2017-05-18 23:57:39 +00:00
Some(module_name) => CString::new(format!("{}.{}", module_name, type_name)),
2017-05-20 06:14:59 +00:00
None => CString::new(type_name)
2017-05-17 06:43:39 +00:00
};
let name = name.expect(
"Module name/type name must not contain NUL byte").into_raw();
type_object.tp_name = name;
// dealloc
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
// type size
type_object.tp_basicsize = <T as PyTypeInfo>::size() as ffi::Py_ssize_t;
2017-05-17 06:43:39 +00:00
2017-05-22 05:22:45 +00:00
// GC support
2017-05-26 22:33:34 +00:00
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
2017-05-22 05:22:45 +00:00
2017-05-17 06:43:39 +00:00
// descriptor protocol
2017-05-26 22:33:34 +00:00
<T as class::descr::PyDescrProtocolImpl>::tp_as_descr(type_object);
2017-05-17 06:43:39 +00:00
2017-05-18 22:57:38 +00:00
// iterator methods
2017-05-26 22:33:34 +00:00
<T as class::iter::PyIterProtocolImpl>::tp_as_iter(type_object);
2017-05-18 22:57:38 +00:00
// basic methods
2017-05-26 22:33:34 +00:00
<T as class::basic::PyObjectProtocolImpl>::tp_as_object(type_object);
2017-05-17 06:43:39 +00:00
// number methods
2017-05-26 22:33:34 +00:00
if let Some(meth) = <T as class::number::PyNumberProtocolImpl>::tp_as_number() {
type_object.tp_as_number = Box::into_raw(Box::new(meth));
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
2017-05-26 22:33:34 +00:00
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// mapping methods
2017-05-17 23:33:18 +00:00
if let Some(meth) = <T as class::mapping::PyMappingProtocolImpl>::tp_as_mapping() {
type_object.tp_as_mapping = Box::into_raw(Box::new(meth));
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
2017-05-25 03:31:51 +00:00
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// sequence methods
2017-05-26 22:33:34 +00:00
if let Some(meth) = <T as class::sequence::PySequenceProtocolImpl>::tp_as_sequence() {
type_object.tp_as_sequence = Box::into_raw(Box::new(meth));
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods;
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// async methods
2017-05-18 01:25:26 +00:00
if let Some(meth) = <T as class::async::PyAsyncProtocolImpl>::tp_as_async() {
type_object.tp_as_async = Box::into_raw(Box::new(meth));
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods;
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// buffer protocol
if let Some(meth) = <T as class::buffer::PyBufferProtocolImpl>::tp_as_buffer() {
type_object.tp_as_buffer = Box::into_raw(Box::new(meth));
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs;
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// normal methods
let (new, call, methods) = py_class_method_defs::<T>(py, type_object)?;
if methods.len(py) != 0 {
assert!(type_object.tp_dict.is_null());
type_object.tp_dict = methods.into_ptr();
2017-05-17 06:43:39 +00:00
}
2017-05-20 06:14:59 +00:00
// __new__ method
type_object.tp_new = new;
// __call__ method
type_object.tp_call = call;
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// properties
let mut props = py_class_properties::<T>();
if !props.is_empty() {
props.push(ffi::PyGetSetDef_INIT);
type_object.tp_getset = props.as_mut_ptr();
2017-05-17 06:43:39 +00:00
mem::forget(props);
2017-05-26 22:33:34 +00:00
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// register type object
unsafe {
if ffi::PyType_Ready(type_object) == 0 {
Ok(PyType::from_type_ptr(py, type_object))
} else {
Err(PyErr::fetch(py))
2017-05-16 18:58:18 +00:00
}
}
}
2017-05-25 03:31:51 +00:00
2017-05-22 05:22:45 +00:00
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
where T: PyTypeInfo
2017-05-16 18:58:18 +00:00
{
2017-06-07 02:26:59 +00:00
debug!("DEALLOC: {:?} - {:?}", obj,
CStr::from_ptr((*(*obj).ob_type).tp_name).to_string_lossy());
2017-05-16 18:58:18 +00:00
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
let py = Python::assume_gil_acquired();
2017-05-28 15:57:34 +00:00
let r = <T as PyObjectAlloc>::dealloc(py, obj);
2017-05-16 18:58:18 +00:00
mem::forget(guard);
r
}
fn py_class_method_defs<T>(py: Python, type_object: *mut ffi::PyTypeObject)
-> PyResult<(Option<ffi::newfunc>,
Option<ffi::PyCFunctionWithKeywords>,
PyDict)>
{
let defs = PyDict::new(py);
2017-05-20 06:14:59 +00:00
let mut call = None;
let mut new = None;
2017-05-16 18:58:18 +00:00
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
match def {
2017-05-20 06:14:59 +00:00
&PyMethodDefType::New(ref def) => {
if let class::methods::PyMethodType::PyNewFunc(meth) = def.ml_meth {
new = Some(meth)
}
},
&PyMethodDefType::Call(ref def) => {
if let class::methods::PyMethodType::PyCFunctionWithKeywords(meth) = def.ml_meth {
call = Some(meth)
} else {
panic!("Method type is not supoorted by tp_call slot")
}
}
&PyMethodDefType::Method(ref def) => {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-20 06:14:59 +00:00
}
2017-06-08 18:29:40 +00:00
&PyMethodDefType::Class(ref def) => {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
}
&PyMethodDefType::Static(ref def) => {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
}
2017-05-16 18:58:18 +00:00
_ => (),
}
}
2017-05-26 22:33:34 +00:00
2017-05-27 18:43:45 +00:00
for def in <T as class::basic::PyObjectProtocolImpl>::methods() {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-27 18:43:45 +00:00
}
for def in <T as class::async::PyAsyncProtocolImpl>::methods() {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-27 18:43:45 +00:00
}
for def in <T as class::context::PyContextProtocolImpl>::methods() {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-27 18:43:45 +00:00
}
for def in <T as class::mapping::PyMappingProtocolImpl>::methods() {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-27 18:43:45 +00:00
}
for def in <T as class::number::PyNumberProtocolImpl>::methods() {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-27 18:43:45 +00:00
}
for def in <T as class::descr::PyDescrProtocolImpl>::methods() {
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
2017-05-27 18:43:45 +00:00
}
2017-05-16 18:58:18 +00:00
Ok((new, call, defs))
2017-05-16 18:58:18 +00:00
}
fn py_class_properties<T>() -> Vec<ffi::PyGetSetDef> {
let mut defs = HashMap::new();
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
match def {
&PyMethodDefType::Getter(ref getter) => {
let name = getter.name.to_string();
if !defs.contains_key(&name) {
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
}
let def = defs.get_mut(&name).unwrap();
getter.copy_to(def);
},
&PyMethodDefType::Setter(ref setter) => {
let name = setter.name.to_string();
if !defs.contains_key(&name) {
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
}
let def = defs.get_mut(&name).unwrap();
setter.copy_to(def);
},
_ => (),
}
}
defs.values().map(|i| i.clone()).collect()
2017-05-25 05:43:07 +00:00
}