PyModule::add_class(): set __module__ on the type object

This commit is contained in:
Daniel Grunwald 2017-02-19 16:15:33 +01:00
parent b6939f158b
commit 508937bada
7 changed files with 69 additions and 24 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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