pyo3/src/class/typeob.rs

249 lines
8.1 KiB
Rust
Raw Normal View History

2017-05-16 18:58:18 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
use std::mem;
use std::ffi::CString;
use std::collections::HashMap;
2017-05-17 06:43:39 +00:00
use ::{ffi, class, PyErr, Python, PyResult, PythonObject};
2017-05-19 04:35:08 +00:00
use objects::PyType;
2017-05-17 06:43:39 +00:00
use callback::AbortOnDrop;
use class::{BaseObject, PyMethodDefType};
2017-05-16 18:58:18 +00:00
2017-05-19 04:35:08 +00:00
/// Trait implemented by Python object types that have a corresponding type object.
pub trait PyTypeObject {
2017-05-16 18:58:18 +00:00
2017-05-19 04:35:08 +00:00
/// Retrieves the type object for this Python object type.
fn type_object(py: Python) -> PyType;
2017-05-16 18:58:18 +00:00
2017-05-19 04:35:08 +00:00
}
/// Trait implemented by object that generated by py::class macro
pub trait PyTypeObjectInfo {
fn type_name() -> &'static str;
fn type_object() -> &'static mut ffi::PyTypeObject;
}
impl<T> PyTypeObject for T where T: BaseObject + PythonObject + PyTypeObjectInfo {
2017-05-16 18:58:18 +00:00
2017-05-19 04:35:08 +00:00
#[inline]
fn type_object(py: Python) -> PyType {
let mut ty = <T as PyTypeObjectInfo>::type_object();
2017-05-16 18:58:18 +00:00
2017-05-19 04:35:08 +00:00
if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 {
unsafe { PyType::from_type_ptr(py, ty) }
} else {
// automatically initialize the class on-demand
initialize_type::<T>(py, None, <T as PyTypeObjectInfo>::type_name(), ty).expect(
format!("An error occurred while initializing class {}",
<T as PyTypeObjectInfo>::type_name()).as_ref());
unsafe { PyType::from_type_ptr(py, ty) }
}
}
2017-05-16 18:58:18 +00:00
}
2017-05-18 23:57:39 +00:00
pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str,
2017-05-17 06:43:39 +00:00
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>
where T: BaseObject + PythonObject
{
// 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>);
// GC support
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
// type size
type_object.tp_basicsize = <T as BaseObject>::size() as ffi::Py_ssize_t;
// descriptor protocol
type_object.tp_descr_get = class::descr::get_descrfunc::<T>();
type_object.tp_descr_set = class::descr::set_descrfunc::<T>();
2017-05-18 22:57:38 +00:00
// iterator methods
<T as class::iter::PyIterProtocolImpl>::tp_as_iter(type_object);
2017-05-17 06:43:39 +00:00
// number methods
if let Some(meth) = ffi::PyNumberMethods::new::<T>() {
static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT;
*(unsafe { &mut NB_METHODS }) = meth;
type_object.tp_as_number = unsafe { &mut NB_METHODS };
2017-05-18 22:57:38 +00:00
mem::forget(meth);
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
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() {
2017-05-17 06:43:39 +00:00
static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT;
*(unsafe { &mut MP_METHODS }) = meth;
type_object.tp_as_mapping = unsafe { &mut MP_METHODS };
2017-05-18 22:57:38 +00:00
mem::forget(meth);
2017-05-17 06:43:39 +00:00
} else {
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
}
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// sequence methods
if let Some(meth) = ffi::PySequenceMethods::new::<T>() {
static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
*(unsafe { &mut SQ_METHODS }) = meth;
type_object.tp_as_sequence = unsafe { &mut SQ_METHODS };
2017-05-18 22:57:38 +00:00
mem::forget(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() {
2017-05-17 06:43:39 +00:00
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
*(unsafe { &mut ASYNC_METHODS }) = meth;
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };
2017-05-18 22:57:38 +00:00
mem::forget(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) = ffi::PyBufferProcs::new::<T>() {
static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT;
*(unsafe { &mut BUFFER_PROCS }) = meth;
type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS };
2017-05-18 22:57:38 +00:00
mem::forget(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
2017-05-20 06:14:59 +00:00
let (new, call, mut methods) = py_class_method_defs::<T>();
2017-05-17 06:43:39 +00:00
if !methods.is_empty() {
methods.push(ffi::PyMethodDef_INIT);
type_object.tp_methods = methods.as_ptr() as *mut _;
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
*(unsafe { &mut METHODS }) = methods.as_ptr();
2017-05-18 22:57:38 +00:00
mem::forget(methods);
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);
let props = props.into_boxed_slice();
type_object.tp_getset = props.as_ptr() as *mut _;
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
static mut PROPS: *const ffi::PyGetSetDef = 0 as *const _;
*(unsafe { &mut PROPS }) = props.as_ptr();
2017-05-16 18:58:18 +00:00
2017-05-17 06:43:39 +00:00
// strange
mem::forget(props);
}
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-17 06:43:39 +00:00
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject) where T: BaseObject
2017-05-16 18:58:18 +00:00
{
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
let py = Python::assume_gil_acquired();
let r = T::dealloc(py, obj);
mem::forget(guard);
r
}
2017-05-20 06:14:59 +00:00
fn py_class_method_defs<T>() -> (Option<ffi::newfunc>,
Option<ffi::PyCFunctionWithKeywords>,
Vec<ffi::PyMethodDef>) {
2017-05-16 18:58:18 +00:00
let mut defs = Vec::new();
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::number::PyNumberProtocolImpl>::py_methods() {
match def {
&PyMethodDefType::Method(ref def) => defs.push(def.as_method_def()),
_ => (),
}
}
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.push(def.as_method_def())
}
2017-05-16 18:58:18 +00:00
_ => (),
}
}
2017-05-19 18:48:05 +00:00
for def in <T as class::async::PyAsyncProtocolImpl>::methods() {
defs.push(def.as_method_def())
}
2017-05-20 06:14:59 +00:00
for def in <T as class::context::PyContextProtocolImpl>::methods() {
defs.push(def.as_method_def())
}
2017-05-16 18:58:18 +00:00
2017-05-20 06:14:59 +00:00
(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()
}