PyModule::add_class(): set __module__ on the type object
This commit is contained in:
parent
b6939f158b
commit
508937bada
|
@ -109,14 +109,11 @@ impl PyModule {
|
|||
///
|
||||
/// This is a convenience function that initializes the `py_class!()`,
|
||||
/// sets `new_type.__module__` to this module's name,
|
||||
// and adds the type to this module.
|
||||
/// and adds the type to this module.
|
||||
pub fn add_class<'p, T>(&self, py: Python<'p>) -> PyResult<()>
|
||||
where T: PythonObjectFromPyClassMacro
|
||||
{
|
||||
let type_obj = try!(T::initialize(py));
|
||||
try!(self.as_object().setattr(py, type_obj.name(py), &type_obj));
|
||||
type_obj.release_ref(py);
|
||||
Ok(())
|
||||
T::add_to_module(py, self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ pub mod gc;
|
|||
use libc;
|
||||
use std::{mem, ptr, cell};
|
||||
use python::{self, Python, PythonObject};
|
||||
use objects::{PyObject, PyType};
|
||||
use objects::{PyObject, PyType, PyModule};
|
||||
use err::{self, PyResult};
|
||||
use ffi;
|
||||
|
||||
|
@ -43,8 +43,16 @@ pub enum CompareOp {
|
|||
}
|
||||
|
||||
/// Trait implemented by the types produced by the `py_class!()` macro.
|
||||
///
|
||||
/// This is an unstable implementation detail; do not implement manually!
|
||||
pub trait PythonObjectFromPyClassMacro : python::PythonObjectWithTypeObject {
|
||||
fn initialize(py: Python) -> PyResult<PyType>;
|
||||
/// Initializes the class.
|
||||
///
|
||||
/// module_name: the name of the parent module into which the class will be placed.
|
||||
fn initialize(py: Python, module_name: Option<&str>) -> PyResult<PyType>;
|
||||
|
||||
/// Initializes the class and adds it to the module.
|
||||
fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -152,7 +152,7 @@ base_case = '''
|
|||
$crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT)
|
||||
} else {
|
||||
// automatically initialize the class on-demand
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py)
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, None)
|
||||
.expect(concat!("An error occurred while initializing class ", stringify!($class)))
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ base_case = '''
|
|||
}
|
||||
|
||||
impl $crate::py_class::PythonObjectFromPyClassMacro for $class {
|
||||
fn initialize(py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
|
||||
fn initialize(py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
unsafe {
|
||||
if $crate::py_class::is_ready(py, &TYPE_OBJECT) {
|
||||
return Ok($crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT));
|
||||
|
@ -169,15 +169,20 @@ base_case = '''
|
|||
concat!("Reentrancy detected: already initializing class ",
|
||||
stringify!($class)));
|
||||
INIT_ACTIVE = true;
|
||||
let res = init(py);
|
||||
let res = init(py, module_name);
|
||||
INIT_ACTIVE = false;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_module(py: $crate::Python, module: &$crate::PyModule) -> $crate::PyResult<()> {
|
||||
let ty = <$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, module.name(py).ok())?;
|
||||
module.add(py, stringify!($class), ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn init($py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, $slots);
|
||||
fn init($py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots);
|
||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||
unsafe {
|
||||
if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 {
|
||||
|
|
|
@ -144,7 +144,7 @@ macro_rules! py_class_impl {
|
|||
$crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT)
|
||||
} else {
|
||||
// automatically initialize the class on-demand
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py)
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, None)
|
||||
.expect(concat!("An error occurred while initializing class ", stringify!($class)))
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ macro_rules! py_class_impl {
|
|||
}
|
||||
|
||||
impl $crate::py_class::PythonObjectFromPyClassMacro for $class {
|
||||
fn initialize(py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
|
||||
fn initialize(py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
unsafe {
|
||||
if $crate::py_class::is_ready(py, &TYPE_OBJECT) {
|
||||
return Ok($crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT));
|
||||
|
@ -161,15 +161,20 @@ macro_rules! py_class_impl {
|
|||
concat!("Reentrancy detected: already initializing class ",
|
||||
stringify!($class)));
|
||||
INIT_ACTIVE = true;
|
||||
let res = init(py);
|
||||
let res = init(py, module_name);
|
||||
INIT_ACTIVE = false;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_module(py: $crate::Python, module: &$crate::PyModule) -> $crate::PyResult<()> {
|
||||
let ty = <$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, module.name(py).ok())?;
|
||||
module.add(py, stringify!($class), ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn init($py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, $slots);
|
||||
fn init($py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots);
|
||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||
unsafe {
|
||||
if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 {
|
||||
|
|
|
@ -144,7 +144,7 @@ macro_rules! py_class_impl {
|
|||
$crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT)
|
||||
} else {
|
||||
// automatically initialize the class on-demand
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py)
|
||||
<$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, None)
|
||||
.expect(concat!("An error occurred while initializing class ", stringify!($class)))
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ macro_rules! py_class_impl {
|
|||
}
|
||||
|
||||
impl $crate::py_class::PythonObjectFromPyClassMacro for $class {
|
||||
fn initialize(py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
|
||||
fn initialize(py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
unsafe {
|
||||
if $crate::py_class::is_ready(py, &TYPE_OBJECT) {
|
||||
return Ok($crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT));
|
||||
|
@ -161,15 +161,20 @@ macro_rules! py_class_impl {
|
|||
concat!("Reentrancy detected: already initializing class ",
|
||||
stringify!($class)));
|
||||
INIT_ACTIVE = true;
|
||||
let res = init(py);
|
||||
let res = init(py, module_name);
|
||||
INIT_ACTIVE = false;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_module(py: $crate::Python, module: &$crate::PyModule) -> $crate::PyResult<()> {
|
||||
let ty = <$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, module.name(py).ok())?;
|
||||
module.add(py, stringify!($class), ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn init($py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, $slots);
|
||||
fn init($py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots);
|
||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||
unsafe {
|
||||
if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
use ffi;
|
||||
use std::{mem, isize, ptr};
|
||||
use std::ffi::CString;
|
||||
use libc::{c_char, c_int};
|
||||
use python::{Python, PythonObject};
|
||||
use conversion::ToPyObject;
|
||||
|
@ -80,7 +81,7 @@ pub const TPFLAGS_DEFAULT : ::libc::c_ulong = ffi::Py_TPFLAGS_DEFAULT;
|
|||
#[doc(hidden)]
|
||||
macro_rules! py_class_type_object_dynamic_init {
|
||||
// initialize those fields of PyTypeObject that we couldn't initialize statically
|
||||
($class: ident, $py:ident, $type_object:ident,
|
||||
($class: ident, $py:ident, $type_object:ident, $module_name: ident,
|
||||
/* slots: */ {
|
||||
$type_slots:tt
|
||||
$as_number:tt
|
||||
|
@ -90,7 +91,7 @@ macro_rules! py_class_type_object_dynamic_init {
|
|||
}
|
||||
) => {
|
||||
unsafe {
|
||||
$type_object.tp_name = concat!(stringify!($class), "\0").as_ptr() as *const _;
|
||||
$type_object.tp_name = $crate::py_class::slots::build_tp_name($module_name, stringify!($class));
|
||||
$type_object.tp_basicsize = <$class as $crate::py_class::BaseObject>::size()
|
||||
as $crate::_detail::ffi::Py_ssize_t;
|
||||
}
|
||||
|
@ -101,6 +102,14 @@ macro_rules! py_class_type_object_dynamic_init {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_tp_name(module_name: Option<&str>, type_name: &str) -> *mut c_char {
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(format!("{}.{}", module_name, type_name)),
|
||||
None => CString::new(type_name)
|
||||
};
|
||||
name.expect("Module name/type name must not contain NUL byte").into_raw()
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where T: super::BaseObject
|
||||
{
|
||||
|
|
|
@ -43,6 +43,22 @@ fn empty_class() {
|
|||
let typeobj = py.get_type::<EmptyClass>();
|
||||
// By default, don't allow creating instances from python.
|
||||
assert!(typeobj.call(py, NoArgs, None).is_err());
|
||||
|
||||
py_assert!(py, typeobj, "typeobj.__name__ == 'EmptyClass'");
|
||||
}
|
||||
|
||||
py_class!(class EmptyClassInModule |py| { });
|
||||
|
||||
#[test]
|
||||
fn empty_class_in_module() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let module = PyModule::new(py, "test_module.nested").unwrap();
|
||||
module.add_class::<EmptyClassInModule>(py).unwrap();
|
||||
|
||||
let ty = module.get(py, "EmptyClassInModule").unwrap();
|
||||
assert_eq!(ty.getattr(py, "__name__").unwrap().extract::<String>(py).unwrap(), "EmptyClassInModule");
|
||||
assert_eq!(ty.getattr(py, "__module__").unwrap().extract::<String>(py).unwrap(), "test_module.nested");
|
||||
}
|
||||
|
||||
py_class!(class EmptyClassWithNew |py| {
|
||||
|
|
Loading…
Reference in a new issue