diff --git a/Cargo.toml b/Cargo.toml index 7cfcfaf8..c9e22047 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ readme = "README.md" keywords = [ "pyo3", "python", - "cpython", ] homepage = "https://github.com/pyo3/pyo3" repository = "https://github.com/pyo3/pyo3.git" diff --git a/Makefile b/Makefile index b088970c..9ffa7ac8 100644 --- a/Makefile +++ b/Makefile @@ -23,10 +23,7 @@ CARGO_FLAGS := --features "$(FEATURES)" --no-default-features default: test -src/py_class/py_class_impl.rs: src/py_class/py_class_impl.py - PY=3 python $< >$@ - -build: src/py_class/py_class_impl.rs +build: cargo build $(CARGO_FLAGS) test: build diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index a3ceb70a..d6e38118 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -5,16 +5,36 @@ use quote::{Tokens, ToTokens}; pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens { - if let syn::Body::Enum(_) = ast.body { - panic!("#[py_class] can only be used with structs") - } + let base = syn::Ident::from("pyo3::PyObject"); let mut tokens = Tokens::new(); + + match ast.body { + syn::Body::Struct(syn::VariantData::Struct(ref mut data)) => { + impl_storage(&ast.ident, &base, data).to_tokens(&mut tokens); + + let tt = quote! { + struct Test { + _unsafe_inner: PyObject + } + }; + let t = syn::parse_item(tt.as_str()).unwrap(); + match t.node { + syn::ItemKind::Struct(syn::VariantData::Struct(fields), _) => { + data.clear(); + data.extend(fields); + } + _ => panic!("something is worng"), + } + }, + _ => + panic!("#[class] can only be used with notmal structs"), + } + impl_to_py_object(&ast.ident).to_tokens(&mut tokens); impl_from_py_object(&ast.ident).to_tokens(&mut tokens); impl_python_object(&ast.ident).to_tokens(&mut tokens); impl_checked_downcast(&ast.ident).to_tokens(&mut tokens); - impl_class_init(&ast.ident).to_tokens(&mut tokens); let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident)); quote! { @@ -23,13 +43,140 @@ pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens { const #dummy_const: () = { extern crate pyo3; use std; - use pyo3::ffi; + use pyo3::class::BaseObject; + use pyo3::{ffi, Python, PyObject, PyType, PyResult, PyModule}; #tokens }; } } +fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec) -> Tokens { + let names: Vec = fields.iter() + .map(|f| f.ident.as_ref().unwrap().clone()).collect(); + let values: Vec = fields.iter() + .map(|f| f.ident.as_ref().unwrap().clone()).collect(); + //let types: Vec = fields.iter().map(|f| f.ty.clone()).collect(); + + let mut accessors = Tokens::new(); + for field in fields.iter() { + let name = &field.ident.as_ref().unwrap(); + let name_mut = syn::Ident::from(format!("{}_mut", name.as_ref())); + let ty = &field.ty; + + let accessor = quote!{ + impl #cls { + fn #name<'a>(&'a self, py: Python<'a>) -> &'a #ty { + unsafe { + let ptr = (self._unsafe_inner.as_ptr() as *const u8) + .offset(base_offset() as isize) as *const Storage; + &(*ptr).#name + } + } + fn #name_mut<'a>(&'a self, py: Python<'a>) -> &'a mut #ty { + unsafe { + let ptr = (self._unsafe_inner.as_ptr() as *const u8) + .offset(base_offset() as isize) as *mut Storage; + &mut (*ptr).#name + } + } + } + }; + accessor.to_tokens(&mut accessors); + } + + quote! { + struct Storage { + #(#fields),* + } + + impl #cls { + fn create_instance(py: Python, #(#fields),*) -> PyResult<#cls> { + let obj = try!(unsafe { + <#cls as BaseObject>::alloc( + py, &py.get_type::<#cls>(), + Storage { #(#names: #values),*})}); + + return Ok(#cls { _unsafe_inner: obj }); + } + } + + #accessors + + impl pyo3::PythonObjectWithTypeObject for #cls { + #[inline] + fn type_object(py: Python) -> PyType { + unsafe { #cls::initialized(py, None) } + } + } + + impl pyo3::class::PyTypeObject for #cls { + + fn add_to_module(py: Python, module: &PyModule) -> PyResult<()> { + let ty = unsafe { #cls::initialized(py, module.name(py).ok()) }; + module.add(py, stringify!(#cls), ty) + } + + #[inline] + unsafe fn type_obj() -> &'static mut ffi::PyTypeObject { + static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT; + &mut TYPE_OBJECT + } + + unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType { + let mut ty = #cls::type_obj(); + + if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 { + PyType::from_type_ptr(py, ty) + } else { + // automatically initialize the class on-demand + pyo3::class::typeob::initialize_type::<#cls>( + py, module_name, ty).expect( + concat!("An error occurred while initializing class ", + stringify!(#cls))); + PyType::from_type_ptr(py, ty) + } + } + } + + #[inline] + fn base_offset() -> usize { + let align = std::mem::align_of::(); + let bs = <#base as BaseObject>::size(); + + // round base_size up to next multiple of align + (bs + align - 1) / align * align + } + + impl BaseObject for #cls { + type Type = Storage; + + #[inline] + fn size() -> usize { + base_offset() + std::mem::size_of::() + } + + unsafe fn alloc(py: Python, ty: &PyType, value: Self::Type) -> PyResult + { + let obj = try!(<#base as BaseObject>::alloc(py, ty, ())); + + let ptr = (obj.as_ptr() as *mut u8) + .offset(base_offset() as isize) as *mut Self::Type; + std::ptr::write(ptr, value); + + Ok(obj) + } + + unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) { + let ptr = (obj as *mut u8).offset(base_offset() as isize) as *mut Self::Type; + std::ptr::drop_in_place(ptr); + + <#base as BaseObject>::dealloc(py, obj) + } + } + } +} + fn impl_to_py_object(cls: &syn::Ident) -> Tokens { quote! { /// Identity conversion: allows using existing `PyObject` instances where @@ -130,13 +277,3 @@ fn impl_checked_downcast(cls: &syn::Ident) -> Tokens { } } } - -fn impl_class_init(cls: &syn::Ident) -> Tokens { - quote! { - impl pyo3::class::typeob::PyClassInit for #cls { - fn init() -> bool { - true - } - } - } -} diff --git a/pyo3cls/src/py_method.rs b/pyo3cls/src/py_method.rs index 30990a82..38c5b9fd 100644 --- a/pyo3cls/src/py_method.rs +++ b/pyo3cls/src/py_method.rs @@ -220,8 +220,8 @@ fn impl_wrap(cls: &Box, name: &syn::Ident, args: Vec) -> Tokens { { const LOCATION: &'static str = concat!( stringify!(#cls), ".", stringify!(#name), "()"); - pyo3::_detail::handle_callback( - LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py| + pyo3::callback::handle_callback( + LOCATION, pyo3::callback::PyObjectCallbackConverter, |py| { let args: pyo3::PyTuple = pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into(); @@ -248,8 +248,8 @@ fn impl_wrap_getter(cls: &Box, name: &syn::Ident, _args: Vec) -> T { const LOCATION: &'static str = concat!( stringify!(#cls), ".getter_", stringify!(#name), "()"); - pyo3::_detail::handle_callback( - LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py| + pyo3::callback::handle_callback( + LOCATION, pyo3::callback::PyObjectCallbackConverter, |py| { let slf = pyo3::PyObject::from_borrowed_ptr( py, slf).unchecked_cast_into::<#cls>(); @@ -270,8 +270,8 @@ fn impl_wrap_setter(cls: &Box, name: &syn::Ident, _args: Vec) -> T { const LOCATION: &'static str = concat!( stringify!(#cls), ".setter", stringify!(#name), "()"); - pyo3::_detail::handle_callback( - LOCATION, pyo3::py_class::slots::UnitCallbackConverter, |py| + pyo3::callback::handle_callback( + LOCATION, pyo3::callback::UnitCallbackConverter, |py| { let slf = pyo3::PyObject::from_borrowed_ptr(py, slf) .unchecked_cast_into::<#cls>(); diff --git a/src/callback.rs b/src/callback.rs new file mode 100644 index 00000000..1fff7ad1 --- /dev/null +++ b/src/callback.rs @@ -0,0 +1,242 @@ +/// Utilities for a Python callable object that invokes a Rust function. + +use std::os::raw::c_int; +use std::{any, mem, ptr, isize, io, marker, panic}; + +use libc; +use python::{Python, PythonObject}; +use objects::exc; +use conversion::ToPyObject; +use ffi::{self, Py_hash_t}; +use err::{PyErr, PyResult}; + + +pub trait CallbackConverter { + type R; + + fn convert(S, Python) -> Self::R; + fn error_value() -> Self::R; +} + +pub struct PyObjectCallbackConverter; + +impl CallbackConverter for PyObjectCallbackConverter + where S: ToPyObject +{ + type R = *mut ffi::PyObject; + + fn convert(val: S, py: Python) -> *mut ffi::PyObject { + val.into_py_object(py).into_object().steal_ptr() + } + + #[inline] + fn error_value() -> *mut ffi::PyObject { + ptr::null_mut() + } +} + +pub struct PythonObjectCallbackConverter(pub marker::PhantomData); + +impl CallbackConverter for PythonObjectCallbackConverter + where T: PythonObject, + S: ToPyObject +{ + type R = *mut ffi::PyObject; + + fn convert(val: S, py: Python) -> *mut ffi::PyObject { + val.into_py_object(py).into_object().steal_ptr() + } + + #[inline] + fn error_value() -> *mut ffi::PyObject { + ptr::null_mut() + } +} + +pub struct BoolConverter; + +impl CallbackConverter for BoolConverter { + type R = c_int; + + #[inline] + fn convert(val: bool, _py: Python) -> c_int { + val as c_int + } + + #[inline] + fn error_value() -> c_int { + -1 + } +} + +pub struct LenResultConverter; + +impl CallbackConverter for LenResultConverter { + type R = isize; + + fn convert(val: usize, py: Python) -> isize { + if val <= (isize::MAX as usize) { + val as isize + } else { + PyErr::new_lazy_init(py.get_type::(), None).restore(py); + -1 + } + } + + #[inline] + fn error_value() -> isize { + -1 + } +} + + +pub struct UnitCallbackConverter; + +impl CallbackConverter<()> for UnitCallbackConverter { + type R = c_int; + + #[inline] + fn convert(_: (), _: Python) -> c_int { + 0 + } + + #[inline] + fn error_value() -> c_int { + -1 + } +} + +pub struct VoidCallbackConverter; + +impl CallbackConverter<()> for VoidCallbackConverter { + type R = (); + + #[inline] + fn convert(_: (), _: Python) -> () { + () + } + + #[inline] + fn error_value() -> () { + () + } +} + +pub struct IterNextResultConverter; + +impl CallbackConverter> + for IterNextResultConverter + where T: ToPyObject +{ + type R = *mut ffi::PyObject; + + fn convert(val: Option, py: Python) -> *mut ffi::PyObject { + match val { + Some(val) => val.into_py_object(py).into_object().steal_ptr(), + None => unsafe { + ffi::PyErr_SetNone(ffi::PyExc_StopIteration); + ptr::null_mut() + } + } + } + + #[inline] + fn error_value() -> *mut ffi::PyObject { + ptr::null_mut() + } +} + +pub trait WrappingCastTo { + fn wrapping_cast(self) -> T; +} + +macro_rules! wrapping_cast { + ($from:ty, $to:ty) => { + impl WrappingCastTo<$to> for $from { + #[inline] + fn wrapping_cast(self) -> $to { + self as $to + } + } + } +} +wrapping_cast!(u8, Py_hash_t); +wrapping_cast!(u16, Py_hash_t); +wrapping_cast!(u32, Py_hash_t); +wrapping_cast!(usize, Py_hash_t); +wrapping_cast!(u64, Py_hash_t); +wrapping_cast!(i8, Py_hash_t); +wrapping_cast!(i16, Py_hash_t); +wrapping_cast!(i32, Py_hash_t); +wrapping_cast!(isize, Py_hash_t); +wrapping_cast!(i64, Py_hash_t); + +pub struct HashConverter; + +impl CallbackConverter for HashConverter + where T: WrappingCastTo +{ + type R = Py_hash_t; + + #[inline] + fn convert(val: T, _py: Python) -> Py_hash_t { + let hash = val.wrapping_cast(); + if hash == -1 { + -2 + } else { + hash + } + } + + #[inline] + fn error_value() -> Py_hash_t { + -1 + } +} + + +pub unsafe fn handle_callback(location: &str, _c: C, f: F) -> C::R + where F: FnOnce(Python) -> PyResult, + F: panic::UnwindSafe, + C: CallbackConverter +{ + let guard = AbortOnDrop(location); + let ret = panic::catch_unwind(|| { + let py = Python::assume_gil_acquired(); + match f(py) { + Ok(val) => { + C::convert(val, py) + } + Err(e) => { + e.restore(py); + C::error_value() + } + } + }); + let ret = match ret { + Ok(r) => r, + Err(ref err) => { + handle_panic(Python::assume_gil_acquired(), err); + C::error_value() + } + }; + mem::forget(guard); + ret +} + +fn handle_panic(_py: Python, _panic: &any::Any) { + let msg = cstr!("Rust panic"); + unsafe { + ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr()); + } +} + +pub struct AbortOnDrop<'a>(pub &'a str); + +impl <'a> Drop for AbortOnDrop<'a> { + fn drop(&mut self) { + use std::io::Write; + let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0); + unsafe { libc::abort() } + } +} diff --git a/src/class/async.rs b/src/class/async.rs index 27aa42fa..32b5afdb 100644 --- a/src/class/async.rs +++ b/src/class/async.rs @@ -10,7 +10,7 @@ use ffi; use err::PyResult; use python::{Python, PythonObject}; use objects::PyObject; -use function::PyObjectCallbackConverter; +use callback::PyObjectCallbackConverter; use class::NO_METHODS; diff --git a/src/class/basic.rs b/src/class/basic.rs index e169a32f..dbada556 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -11,10 +11,9 @@ use ::{CompareOp, Py_hash_t}; use ffi; use err::{PyErr, PyResult}; use python::{Python, PythonObject, PyDrop}; -use conversion::ToPyObject; use objects::{exc, PyObject}; -use py_class::slots::{HashConverter, UnitCallbackConverter}; -use function::{handle_callback, PyObjectCallbackConverter}; +use conversion::ToPyObject; +use callback::{handle_callback, PyObjectCallbackConverter, HashConverter, UnitCallbackConverter}; use class::{NO_METHODS, NO_PY_METHODS}; // __new__ diff --git a/src/class/buffer.rs b/src/class/buffer.rs index 42486ebd..cdc436d9 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -11,8 +11,7 @@ use ffi; use err::PyResult; use python::{Python, PythonObject}; use objects::PyObject; -use py_class::slots::UnitCallbackConverter; -use function::handle_callback; +use callback::{handle_callback, UnitCallbackConverter}; use class::NO_METHODS; diff --git a/src/class/descr.rs b/src/class/descr.rs index 3243ac83..f8832646 100644 --- a/src/class/descr.rs +++ b/src/class/descr.rs @@ -12,8 +12,7 @@ use err::{PyErr, PyResult}; use python::{Python, PythonObject}; use objects::{exc, PyObject}; use class::{NO_METHODS, NO_PY_METHODS}; -use function::PyObjectCallbackConverter; -use py_class::slots::UnitCallbackConverter; +use callback::{PyObjectCallbackConverter, UnitCallbackConverter}; /// Descriptor interface pub trait PyDescrProtocol { diff --git a/src/class/gc.rs b/src/class/gc.rs index 021772d1..5ce04e24 100644 --- a/src/class/gc.rs +++ b/src/class/gc.rs @@ -9,7 +9,7 @@ use std::os::raw::{c_int, c_void}; use ffi; use python::{Python, PythonObject, PyDrop, ToPythonPointer}; use objects::PyObject; -use function::AbortOnDrop; +use callback::AbortOnDrop; use class::NO_METHODS; pub struct PyTraverseError(c_int); diff --git a/src/class/macros.rs b/src/class/macros.rs index e6789b19..463530cc 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -11,7 +11,7 @@ macro_rules! py_unary_func { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback(LOCATION, $conv, |py| { + $crate::callback::handle_callback(LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::(); let ret = slf.$f(py); $crate::PyDrop::release_ref(slf, py); @@ -31,7 +31,7 @@ macro_rules! py_unary_func { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback(LOCATION, $conv, |py| { + $crate::callback::handle_callback(LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::(); let ret = slf.$f(py); $crate::PyDrop::release_ref(slf, py); @@ -52,7 +52,7 @@ macro_rules! py_binary_func { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback(LOCATION, $conv, |py| { + $crate::callback::handle_callback(LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::(); let arg = $crate::PyObject::from_borrowed_ptr(py, arg); let ret = slf.$f(py, &arg); @@ -78,7 +78,7 @@ macro_rules! py_ternary_func { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback(LOCATION, $conv, |py| { + $crate::callback::handle_callback(LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::(); let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1); let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2); @@ -102,7 +102,7 @@ macro_rules! py_ssizearg_func { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback(LOCATION, $conv, |py| { + $crate::callback::handle_callback(LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::(); let ret = slf.$f(py, arg as isize); $crate::PyDrop::release_ref(slf, py); @@ -123,7 +123,7 @@ macro_rules! py_objobj_proc { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback(LOCATION, $conv, |py| { + $crate::callback::handle_callback(LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::(); let arg = PyObject::from_borrowed_ptr(py, arg); let ret = slf.$f(py, &arg); @@ -148,7 +148,7 @@ macro_rules! py_ternary_slot { where T: $trait + PythonObject { const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( + $crate::callback::handle_callback( LOCATION, $conv, |py| { let slf = $crate::PyObject::from_borrowed_ptr( diff --git a/src/class/mapping.rs b/src/class/mapping.rs index ab144d48..4fe94da5 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -9,8 +9,8 @@ use ffi; use err::{PyErr, PyResult}; use python::{Python, PythonObject, PyDrop}; use objects::{exc, PyObject}; -use py_class::slots::{LenResultConverter, UnitCallbackConverter}; -use function::{handle_callback, PyObjectCallbackConverter}; +use callback::{handle_callback, PyObjectCallbackConverter, + LenResultConverter, UnitCallbackConverter}; use class::NO_METHODS; diff --git a/src/class/mod.rs b/src/class/mod.rs index 67ec718c..8b7105e3 100644 --- a/src/class/mod.rs +++ b/src/class/mod.rs @@ -1,5 +1,8 @@ // Copyright (c) 2017-present PyO3 Project and Contributors +use std::mem; +use std::os::raw::c_void; + #[macro_use] mod macros; pub mod async; @@ -23,9 +26,77 @@ pub use self::number::PyNumberProtocol; pub use self::mapping::PyMappingProtocol; pub use self::sequence::PySequenceProtocol; +pub use self::typeob::PyTypeObject; pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError}; pub use self::methods::{PyMethodDef, PyMethodDefType, PyMethodType, PyGetterDef, PySetterDef}; pub static NO_METHODS: &'static [&'static str] = &[]; pub static NO_PY_METHODS: &'static [PyMethodDefType] = &[]; + +use ffi; +use err::{self, PyResult}; +use objects::{PyObject, PyType}; +use python::{Python, PythonObject}; + + +#[derive(Debug)] +pub enum CompareOp { + Lt = ffi::Py_LT as isize, + Le = ffi::Py_LE as isize, + Eq = ffi::Py_EQ as isize, + Ne = ffi::Py_NE as isize, + Gt = ffi::Py_GT as isize, + Ge = ffi::Py_GE as isize +} + + +/// A PythonObject that is usable as a base type for #[class] +pub trait BaseObject : PythonObject { + /// Gets the size of the object, in bytes. + fn size() -> usize; + + type Type; + + /// Allocates a new object (usually by calling ty->tp_alloc), + /// and initializes it using init_val. + /// `ty` must be derived from the Self type, and the resulting object + /// must be of type `ty`. + unsafe fn alloc(py: Python, ty: &PyType, val: Self::Type) -> PyResult; + + /// 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. + unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject); +} + + +impl BaseObject for PyObject { + #[inline] + fn size() -> usize { + mem::size_of::() + } + + type Type = (); + + unsafe fn alloc(py: Python, ty: &PyType, _init_val: ()) -> PyResult { + let ptr = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0); + err::result_from_owned_ptr(py, ptr) + } + + unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) { + // Unfortunately, there is no PyType_GenericFree, so + // we have to manually un-do the work of PyType_GenericAlloc: + 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); + } + } +} diff --git a/src/class/number.rs b/src/class/number.rs index 4e6fb704..d8d0afe5 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -9,8 +9,7 @@ use ffi; use err::PyResult; use python::{Python, PythonObject}; use objects::PyObject; -use function::PyObjectCallbackConverter; -use py_class::slots::BoolConverter; +use callback::{BoolConverter, PyObjectCallbackConverter}; use class::{NO_METHODS, NO_PY_METHODS}; use class::basic::{PyObjectProtocol, PyObjectProtocolImpl}; diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 03faac11..81b0fc49 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -9,8 +9,8 @@ use ffi; use err::{PyErr, PyResult}; use python::{Python, PythonObject, PyDrop}; use objects::{exc, PyObject}; -use py_class::slots::{LenResultConverter, UnitCallbackConverter, BoolConverter}; -use function::{handle_callback, PyObjectCallbackConverter}; +use callback::{handle_callback, PyObjectCallbackConverter, + LenResultConverter, UnitCallbackConverter, BoolConverter}; use class::NO_METHODS; diff --git a/src/class/typeob.rs b/src/class/typeob.rs index 4864eb8d..7d8690d3 100644 --- a/src/class/typeob.rs +++ b/src/class/typeob.rs @@ -4,144 +4,130 @@ use std::mem; use std::ffi::CString; use std::collections::HashMap; -use ::{ffi, class, py_class, PyErr, Python, PyResult, PythonObject}; -use objects::PyType; -use function::AbortOnDrop; -use class::PyMethodDefType; +use ::{ffi, class, PyErr, Python, PyResult, PythonObject}; +use objects::{PyType, PyModule}; +use callback::AbortOnDrop; +use class::{BaseObject, PyMethodDefType}; -pub trait PyClassInit { +pub trait PyTypeObject : BaseObject + PythonObject { - fn init() -> bool; + fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>; - fn type_object() -> &'static mut ffi::PyTypeObject; + unsafe fn type_obj() -> &'static mut ffi::PyTypeObject; - fn build_type(py: Python, - module_name: Option<&str>, - type_object: &mut ffi::PyTypeObject) -> PyResult; + unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType; } -impl PyClassInit for T - where T: PythonObject + py_class::BaseObject { +pub fn initialize_type(py: Python, module_name: Option<&str>, + type_object: &mut ffi::PyTypeObject) -> PyResult + where T: BaseObject + PythonObject +{ + // type name + let name = match module_name { + Some(module_name) => CString::new( + format!("{}.{}", module_name, stringify!(type_name))), + None => CString::new(stringify!(type_name)) + }; + let name = name.expect( + "Module name/type name must not contain NUL byte").into_raw(); - default fn init() -> bool { false } + type_object.tp_name = name; - default fn type_object() -> &'static mut ffi::PyTypeObject { - static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT; - unsafe { - &mut TYPE_OBJECT - } + // dealloc + type_object.tp_dealloc = Some(tp_dealloc_callback::); + + // GC support + ::update_type_object(type_object); + + // type size + type_object.tp_basicsize = ::size() as ffi::Py_ssize_t; + + // descriptor protocol + type_object.tp_descr_get = class::descr::get_descrfunc::(); + type_object.tp_descr_set = class::descr::set_descrfunc::(); + + // number methods + if let Some(meth) = ffi::PyNumberMethods::new::() { + static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT; + *(unsafe { &mut NB_METHODS }) = meth; + type_object.tp_as_number = unsafe { &mut NB_METHODS }; + } else { + type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods; } - default fn build_type(py: Python, module_name: Option<&str>, - type_object: &mut ffi::PyTypeObject) -> PyResult { - // type name - let name = match module_name { - Some(module_name) => CString::new( - format!("{}.{}", module_name, stringify!(type_name))), - None => CString::new(stringify!(type_name)) - }; - let name = name.expect( - "Module name/type name must not contain NUL byte").into_raw(); + // mapping methods + if let Some(meth) = ffi::PyMappingMethods::new::() { + static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT; + *(unsafe { &mut MP_METHODS }) = meth; + type_object.tp_as_mapping = unsafe { &mut MP_METHODS }; + } else { + type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods; + } - type_object.tp_name = name; + // sequence methods + if let Some(meth) = ffi::PySequenceMethods::new::() { + static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT; + *(unsafe { &mut SQ_METHODS }) = meth; + type_object.tp_as_sequence = unsafe { &mut SQ_METHODS }; + } else { + type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods; + } - // dealloc - type_object.tp_dealloc = Some(tp_dealloc_callback::); + // async methods + if let Some(meth) = ffi::PyAsyncMethods::new::() { + static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT; + *(unsafe { &mut ASYNC_METHODS }) = meth; + type_object.tp_as_async = unsafe { &mut ASYNC_METHODS }; + } else { + type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods; + } - // GC support - ::update_type_object(type_object); + // buffer protocol + if let Some(meth) = ffi::PyBufferProcs::new::() { + static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT; + *(unsafe { &mut BUFFER_PROCS }) = meth; + type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS }; + } else { + type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs; + } - // type size - type_object.tp_basicsize = ::size() as ffi::Py_ssize_t; + // normal methods + let mut methods = py_class_method_defs::(); + if !methods.is_empty() { + methods.push(ffi::PyMethodDef_INIT); + type_object.tp_methods = methods.as_ptr() as *mut _; - // descriptor protocol - type_object.tp_descr_get = class::descr::get_descrfunc::(); - type_object.tp_descr_set = class::descr::set_descrfunc::(); + static mut METHODS: *const ffi::PyMethodDef = 0 as *const _; + *(unsafe { &mut METHODS }) = methods.as_ptr(); + } - // number methods - if let Some(meth) = ffi::PyNumberMethods::new::() { - static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT; - *(unsafe { &mut NB_METHODS }) = meth; - type_object.tp_as_number = unsafe { &mut NB_METHODS }; + // properties + let mut props = py_class_properties::(); + if !props.is_empty() { + props.push(ffi::PyGetSetDef_INIT); + let props = props.into_boxed_slice(); + type_object.tp_getset = props.as_ptr() as *mut _; + + static mut PROPS: *const ffi::PyGetSetDef = 0 as *const _; + *(unsafe { &mut PROPS }) = props.as_ptr(); + + // strange + mem::forget(props); + } + + // register type object + unsafe { + if ffi::PyType_Ready(type_object) == 0 { + Ok(PyType::from_type_ptr(py, type_object)) } else { - type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods; - } - - // mapping methods - if let Some(meth) = ffi::PyMappingMethods::new::() { - static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT; - *(unsafe { &mut MP_METHODS }) = meth; - type_object.tp_as_mapping = unsafe { &mut MP_METHODS }; - } else { - type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods; - } - - // sequence methods - if let Some(meth) = ffi::PySequenceMethods::new::() { - static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT; - *(unsafe { &mut SQ_METHODS }) = meth; - type_object.tp_as_sequence = unsafe { &mut SQ_METHODS }; - } else { - type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods; - } - - // async methods - if let Some(meth) = ffi::PyAsyncMethods::new::() { - static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT; - *(unsafe { &mut ASYNC_METHODS }) = meth; - type_object.tp_as_async = unsafe { &mut ASYNC_METHODS }; - } else { - type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods; - } - - // buffer protocol - if let Some(meth) = ffi::PyBufferProcs::new::() { - static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT; - *(unsafe { &mut BUFFER_PROCS }) = meth; - type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS }; - } else { - type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs; - } - - // normal methods - let mut methods = py_class_method_defs::(); - if !methods.is_empty() { - methods.push(ffi::PyMethodDef_INIT); - type_object.tp_methods = methods.as_ptr() as *mut _; - - static mut METHODS: *const ffi::PyMethodDef = 0 as *const _; - *(unsafe { &mut METHODS }) = methods.as_ptr(); - } - - // properties - let mut props = py_class_properties::(); - if !props.is_empty() { - props.push(ffi::PyGetSetDef_INIT); - let props = props.into_boxed_slice(); - type_object.tp_getset = props.as_ptr() as *mut _; - - static mut PROPS: *const ffi::PyGetSetDef = 0 as *const _; - *(unsafe { &mut PROPS }) = props.as_ptr(); - - // strange - mem::forget(props); - } - - // register type object - unsafe { - if ffi::PyType_Ready(type_object) == 0 { - Ok(PyType::from_type_ptr(py, type_object)) - } else { - Err(PyErr::fetch(py)) - } + Err(PyErr::fetch(py)) } } } -pub unsafe extern "C" fn tp_dealloc_callback(obj: *mut ffi::PyObject) - where T: py_class::BaseObject +unsafe extern "C" fn tp_dealloc_callback(obj: *mut ffi::PyObject) where T: BaseObject { let guard = AbortOnDrop("Cannot unwind out of tp_dealloc"); let py = Python::assume_gil_acquired(); diff --git a/src/err.rs b/src/err.rs index 5af701aa..d99fb8cd 100644 --- a/src/err.rs +++ b/src/err.rs @@ -100,7 +100,7 @@ macro_rules! py_exception { #[inline] fn type_object(py: $crate::Python) -> $crate::PyType { unsafe { - static mut type_object: *mut $crate::_detail::ffi::PyTypeObject = 0 as *mut $crate::_detail::ffi::PyTypeObject; + static mut type_object: *mut $crate::ffi::PyTypeObject = 0 as *mut $crate::ffi::PyTypeObject; if type_object.is_null() { type_object = $crate::PyErr::new_type( diff --git a/src/function.rs b/src/function.rs index ffe5e12e..6350f47d 100644 --- a/src/function.rs +++ b/src/function.rs @@ -16,30 +16,27 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libc; -use std::{mem, ptr, io, any, marker}; -use std::panic; -use python::{Python, PythonObject}; +use std::ptr; +use python::Python; use objects::PyObject; -use conversion::ToPyObject; use ffi; -use err::{self, PyResult}; +use err; #[macro_export] #[doc(hidden)] macro_rules! py_method_def { ($name: expr, $flags: expr, $wrap: expr) => {{ - static mut METHOD_DEF: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef { + static mut METHOD_DEF: $crate::ffi::PyMethodDef = $crate::ffi::PyMethodDef { //ml_name: bytes!(stringify!($name), "\0"), - ml_name: 0 as *const $crate::_detail::libc::c_char, + ml_name: 0 as *const $crate::c_char, ml_meth: None, - ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS | $flags, - ml_doc: 0 as *const $crate::_detail::libc::c_char + ml_flags: $crate::ffi::METH_VARARGS | $crate:::METH_KEYWORDS | $flags, + ml_doc: 0 as *const $crate::c_char }; METHOD_DEF.ml_name = concat!($name, "\0").as_ptr() as *const _; METHOD_DEF.ml_meth = Some( - ::std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords, - $crate::_detail::ffi::PyCFunction>($wrap) + ::std::mem::transmute::<$crate::ffi::PyCFunctionWithKeywords, + $crate::ffi::PyCFunction>($wrap) ); &mut METHOD_DEF }} @@ -112,13 +109,13 @@ macro_rules! py_fn_impl { // Form 1: reference existing function { $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{ unsafe extern "C" fn wrap( - _slf: *mut $crate::_detail::ffi::PyObject, - args: *mut $crate::_detail::ffi::PyObject, - kwargs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject + _slf: *mut $crate::ffi::PyObject, + args: *mut $crate::ffi::PyObject, + kwargs: *mut $crate::ffi::PyObject) + -> *mut $crate::ffi::PyObject { - $crate::_detail::handle_callback( - stringify!($f), $crate::_detail::PyObjectCallbackConverter, + $crate::callback::handle_callback( + stringify!($f), $crate::callback::PyObjectCallbackConverter, |py| { py_argparse_raw!(py, Some(stringify!($f)), args, kwargs, [ $( { $pname : $ptype = $detail } )* ] @@ -128,7 +125,7 @@ macro_rules! py_fn_impl { }) } unsafe { - $crate::_detail::py_fn_impl($py, + $crate::function::py_fn_impl($py, py_method_def!(stringify!($f), 0, wrap)) } }}; @@ -139,97 +136,8 @@ macro_rules! py_fn_impl { }} } + +#[allow(dead_code)] pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject { err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut())) } - -pub trait CallbackConverter { - type R; - - fn convert(S, Python) -> Self::R; - fn error_value() -> Self::R; -} - -pub struct PyObjectCallbackConverter; - -impl CallbackConverter for PyObjectCallbackConverter - where S: ToPyObject -{ - type R = *mut ffi::PyObject; - - fn convert(val: S, py: Python) -> *mut ffi::PyObject { - val.into_py_object(py).into_object().steal_ptr() - } - - #[inline] - fn error_value() -> *mut ffi::PyObject { - ptr::null_mut() - } -} - -pub struct PythonObjectCallbackConverter(pub marker::PhantomData); - -impl CallbackConverter for PythonObjectCallbackConverter - where T: PythonObject, - S: ToPyObject -{ - type R = *mut ffi::PyObject; - - fn convert(val: S, py: Python) -> *mut ffi::PyObject { - val.into_py_object(py).into_object().steal_ptr() - } - - #[inline] - fn error_value() -> *mut ffi::PyObject { - ptr::null_mut() - } -} - -pub unsafe fn handle_callback(location: &str, _c: C, f: F) -> C::R - where F: FnOnce(Python) -> PyResult, - F: panic::UnwindSafe, - C: CallbackConverter -{ - let guard = AbortOnDrop(location); - let ret = panic::catch_unwind(|| { - let py = Python::assume_gil_acquired(); - match f(py) { - Ok(val) => { - C::convert(val, py) - } - Err(e) => { - e.restore(py); - C::error_value() - } - } - }); - let ret = match ret { - Ok(r) => r, - Err(ref err) => { - handle_panic(Python::assume_gil_acquired(), err); - C::error_value() - } - }; - mem::forget(guard); - ret -} - -fn handle_panic(_py: Python, _panic: &any::Any) { - let msg = cstr!("Rust panic"); - unsafe { - ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr()); - } -} - -pub struct AbortOnDrop<'a>(pub &'a str); - -impl <'a> Drop for AbortOnDrop<'a> { - fn drop(&mut self) { - use std::io::Write; - let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0); - unsafe { libc::abort() } - } -} - -// Tests for this file are in tests/test_function.rs - diff --git a/src/lib.rs b/src/lib.rs index da3a60ad..8f7d0661 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,7 +96,7 @@ pub use objects::*; pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone, PyDrop}; pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python}; pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject, ToPyTuple}; -pub use py_class::{CompareOp}; +pub use class::{CompareOp}; pub use objectprotocol::{ObjectProtocol}; #[allow(non_camel_case_types)] @@ -142,7 +142,7 @@ macro_rules! py_impl_to_py_object_for_python_object { #[inline] fn with_borrowed_ptr(&self, _py: $crate::Python, f: F) -> R - where F: FnOnce(*mut $crate::_detail::ffi::PyObject) -> R + where F: FnOnce(*mut $crate::ffi::PyObject) -> R { f($crate::PythonObject::as_object(self).as_ptr()) } @@ -170,7 +170,6 @@ macro_rules! py_impl_from_py_object_for_python_object { } } -pub mod py_class; mod python; mod err; mod conversion; @@ -182,24 +181,11 @@ mod function; pub mod buffer; pub mod class; pub use class::*; +pub mod callback; // re-export for simplicity pub use std::os::raw::*; -/// Private re-exports for macros. Do not use. -#[doc(hidden)] -pub mod _detail { - pub mod ffi { - pub use ::ffi::*; - } - pub mod libc { - pub use std::os::raw::{c_char, c_void, c_int}; - } - pub use err::{from_owned_ptr_or_panic, result_from_owned_ptr}; - pub use function::{handle_callback, py_fn_impl, AbortOnDrop, - PyObjectCallbackConverter, PythonObjectCallbackConverter}; -} - /// Expands to an `extern "C"` function that allows Python to load /// the rust code as a Python extension module. /// @@ -264,7 +250,7 @@ macro_rules! py_module_init { fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> { $body } - static mut MODULE_DEF: $crate::ffi::PyModuleDef = $crate::_detail::ffi::PyModuleDef_INIT; + static mut MODULE_DEF: $crate::ffi::PyModuleDef = $crate::ffi::PyModuleDef_INIT; // We can't convert &'static str to *const c_char within a static initializer, // so we'll do it here in the module initialization: MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _; @@ -278,7 +264,7 @@ pub unsafe fn py_module_init_impl( def: *mut ffi::PyModuleDef, init: fn(Python, &PyModule) -> PyResult<()>) -> *mut ffi::PyObject { - let guard = function::AbortOnDrop("py_module_init"); + let guard = callback::AbortOnDrop("py_module_init"); let py = Python::assume_gil_acquired(); ffi::PyEval_InitThreads(); let module = ffi::PyModule_Create(def); diff --git a/src/objects/module.rs b/src/objects/module.rs index 520a92ef..bf5748d0 100644 --- a/src/objects/module.rs +++ b/src/objects/module.rs @@ -23,7 +23,7 @@ use python::{Python, PythonObject}; use objectprotocol::ObjectProtocol; use conversion::{ToPyObject, ToPyTuple}; use objects::{PyObject, PyDict, exc}; -use py_class::PythonObjectFromPyClassMacro; +use class::PyTypeObject; use err::{self, PyResult, PyErr}; use std::ffi::{CStr, CString}; @@ -107,11 +107,11 @@ impl PyModule { /// Adds a new extension type to the module. /// - /// This is a convenience function that initializes the `py_class!()`, + /// This is a convenience function that initializes the `class`, /// sets `new_type.__module__` to this module's name, /// and adds the type to this module. pub fn add_class<'p, T>(&self, py: Python<'p>) -> PyResult<()> - where T: PythonObjectFromPyClassMacro + where T: PyTypeObject { T::add_to_module(py, self) } diff --git a/src/py_class/members.rs b/src/py_class/members.rs deleted file mode 100644 index ca743133..00000000 --- a/src/py_class/members.rs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2016 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 std::marker; -use python::{Python, PythonObject}; -use conversion::ToPyObject; -use objects::PyObject; -use err::{self, PyResult}; -use ffi; - -/// Represents something that can be added as a member to a Python class/type. -/// -/// T: type of rust class used for instances of the Python class/type. -pub trait TypeMember where T: PythonObject { - /// Convert the type member into a python object - /// that can be stored in the type dict. - /// - /// Because the member may expect `self` values to be of type `T`, - /// `ty` must be T::type_object() or a derived class. - /// (otherwise the behavior is undefined) - unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult; -} - -impl TypeMember for S where T: PythonObject, S: ToPyObject { - #[inline] - unsafe fn into_descriptor(self, py: Python, _ty: *mut ffi::PyTypeObject) -> PyResult { - Ok(self.into_py_object(py).into_object()) - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_init_members { - ($class:ident, $py:ident, $type_object: ident, { }) => {{}}; - ($class:ident, $py:ident, $type_object: ident, { $( $name:ident = $init:expr; )+ }) => {{ - let dict = $crate::PyDict::new($py); - $( { - // keep $init out of unsafe block; it might contain user code - let init = $init; - let descriptor = try!(unsafe { - $crate::py_class::members::TypeMember::<$class>::into_descriptor(init, $py, &mut $type_object) - }); - try!(dict.set_item($py, stringify!($name), descriptor)); - })* - unsafe { - assert!($type_object.tp_dict.is_null()); - $type_object.tp_dict = $crate::PythonObject::into_object(dict).steal_ptr(); - } - }}; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_instance_method { - ($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{ - unsafe extern "C" fn wrap_instance_method( - slf: *mut $crate::_detail::ffi::PyObject, - args: *mut $crate::_detail::ffi::PyObject, - kwargs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::_detail::PyObjectCallbackConverter, - |py| { - py_argparse_raw!(py, Some(LOCATION), args, kwargs, - [ $( { $pname : $ptype = $detail } )* ] - { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let ret = slf.$f(py $(, $pname )* ); - $crate::PyDrop::release_ref(slf, py); - ret - }) - }) - } - unsafe { - let method_def = py_method_def!(stringify!($f), 0, wrap_instance_method); - $crate::py_class::members::create_instance_method_descriptor::<$class>(method_def) - } - }} -} - -pub struct InstanceMethodDescriptor(*mut ffi::PyMethodDef, marker::PhantomData); - -#[inline] -pub unsafe fn create_instance_method_descriptor(method_def: *mut ffi::PyMethodDef) - -> InstanceMethodDescriptor -{ - InstanceMethodDescriptor(method_def, marker::PhantomData) -} - -impl TypeMember for InstanceMethodDescriptor where T: PythonObject { - #[inline] - unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult { - err::result_from_owned_ptr(py, ffi::PyDescr_NewMethod(ty, self.0)) - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_class_method { - ($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{ - unsafe extern "C" fn wrap_class_method( - cls: *mut $crate::_detail::ffi::PyObject, - args: *mut $crate::_detail::ffi::PyObject, - kwargs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::_detail::PyObjectCallbackConverter, - |py| { - py_argparse_raw!(py, Some(LOCATION), args, kwargs, - [ $( { $pname : $ptype = $detail } )* ] - { - let cls = $crate::PyObject::from_borrowed_ptr(py, cls).unchecked_cast_into::<$crate::PyType>(); - let ret = $class::$f(&cls, py $(, $pname )* ); - $crate::PyDrop::release_ref(cls, py); - ret - }) - }) - } - unsafe { - let method_def = py_method_def!(stringify!($f), - $crate::_detail::ffi::METH_CLASS, - wrap_class_method); - $crate::py_class::members::create_class_method_descriptor(method_def) - } - }} -} - -pub struct ClassMethodDescriptor(*mut ffi::PyMethodDef); - -#[inline] -pub unsafe fn create_class_method_descriptor(method_def: *mut ffi::PyMethodDef) - -> ClassMethodDescriptor -{ - ClassMethodDescriptor(method_def) -} - -impl TypeMember for ClassMethodDescriptor where T: PythonObject { - #[inline] - unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult { - err::result_from_owned_ptr(py, ffi::PyDescr_NewClassMethod(ty, self.0)) - } -} - - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_static_method { - ($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{ - unsafe extern "C" fn wrap_static_method( - _slf: *mut $crate::_detail::ffi::PyObject, - args: *mut $crate::_detail::ffi::PyObject, - kwargs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::_detail::PyObjectCallbackConverter, - |py| { - py_argparse_raw!(py, Some(LOCATION), args, kwargs, - [ $( { $pname : $ptype = $detail } )* ] - { - $class::$f(py $(, $pname )* ) - }) - }) - } - unsafe { - let method_def = py_method_def!(stringify!($f), - $crate::_detail::ffi::METH_STATIC, - wrap_static_method); - $crate::_detail::py_fn_impl($py, method_def) - } - }} -} - - diff --git a/src/py_class/mod.rs b/src/py_class/mod.rs deleted file mode 100644 index 9821c34b..00000000 --- a/src/py_class/mod.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2016 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. - -mod py_class; -mod py_class_impl; -#[doc(hidden)] pub mod slots; -#[doc(hidden)] pub mod members; - -use std::os::raw::c_void; -use std::{mem, ptr}; -use python::{self, Python, PythonObject}; -use objects::{PyObject, PyType, PyModule}; -use err::{self, PyResult}; -use ffi; - -// TODO: consider moving CompareOp to a different module, so that it isn't exported via two paths -#[derive(Debug)] -pub enum CompareOp { - Lt = ffi::Py_LT as isize, - Le = ffi::Py_LE as isize, - Eq = ffi::Py_EQ as isize, - Ne = ffi::Py_NE as isize, - Gt = ffi::Py_GT as isize, - Ge = ffi::Py_GE as isize -} - -/// 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 { - /// 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; - - /// Initializes the class and adds it to the module. - fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>; -} - -#[inline] -#[doc(hidden)] -pub fn data_offset(base_size: usize) -> usize { - let align = mem::align_of::(); - // round base_size up to next multiple of align - (base_size + align - 1) / align * align -} - -#[inline] -#[doc(hidden)] -pub fn data_new_size(base_size: usize) -> usize { - data_offset::(base_size) + mem::size_of::() -} - -#[inline] -#[doc(hidden)] -pub unsafe fn data_get<'a, T>(_py: Python<'a>, obj: &'a PyObject, offset: usize) -> &'a T { - let ptr = (obj.as_ptr() as *const u8).offset(offset as isize) as *const T; - &*ptr -} - -#[inline] -#[doc(hidden)] -pub unsafe fn data_init<'a, T>(_py: Python<'a>, obj: &'a PyObject, offset: usize, value: T) - where T: Send + 'static -{ - let ptr = (obj.as_ptr() as *mut u8).offset(offset as isize) as *mut T; - ptr::write(ptr, value) -} - -#[inline] -#[doc(hidden)] -pub unsafe fn data_drop<'a, T>(_py: Python<'a>, obj: *mut ffi::PyObject, offset: usize) { - let ptr = (obj as *mut u8).offset(offset as isize) as *mut T; - ptr::drop_in_place(ptr) -} - -#[inline] -#[doc(hidden)] -pub fn is_ready(_py: Python, ty: &ffi::PyTypeObject) -> bool { - (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 -} - -/// A PythonObject that is usable as a base type with the `py_class!()` macro. -pub trait BaseObject : PythonObject { - /// Gets the size of the object, in bytes. - fn size() -> usize; - - type InitType; - - /// Allocates a new object (usually by calling ty->tp_alloc), - /// and initializes it using init_val. - /// `ty` must be derived from the Self type, and the resulting object - /// must be of type `ty`. - unsafe fn alloc(py: Python, ty: &PyType, init_val: Self::InitType) -> PyResult; - - /// 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. - unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject); -} - -impl BaseObject for PyObject { - #[inline] - fn size() -> usize { - mem::size_of::() - } - - type InitType = (); - - unsafe fn alloc(py: Python, ty: &PyType, _init_val: ()) -> PyResult { - let ptr = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0); - //println!("BaseObject::alloc({:?}) = {:?}", ty.as_type_ptr(), ptr); - err::result_from_owned_ptr(py, ptr) - } - - unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) { - //println!("BaseObject::dealloc({:?})", ptr); - // Unfortunately, there is no PyType_GenericFree, so - // we have to manually un-do the work of PyType_GenericAlloc: - 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); - } - } -} - diff --git a/src/py_class/py_class.rs b/src/py_class/py_class.rs deleted file mode 100644 index 31a08a0a..00000000 --- a/src/py_class/py_class.rs +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (c) 2016 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. - -/** -Defines new python extension class. -A `py_class!` macro invocation generates code that declares a new Python class. -Additionally, it generates a Rust struct of the same name, which allows accessing -instances of that Python class from Rust. - -# Syntax -`py_class!(pub class MyType |py| { ... })` - -* `pub` makes the generated Rust struct visible outside the current module. It has no effect on the visibility from Python. -* `MyType` is the name of the Python class. -* `py` is an identifier that will be made available as a variable of type `Python` -in all function bodies. -* `{ ... }` is the class body, described in more detail below. - -# Example -``` -#[macro_use] extern crate pyo3; -use pyo3::{Python, PyResult, PyDict}; - -py_class!(class MyType |py| { - data number: i32; - def __new__(_cls, arg: i32) -> PyResult { - MyType::create_instance(py, arg) - } - def half(&self) -> PyResult { - println!("half() was called with self={:?}", self.number(py)); - Ok(self.number(py) / 2) - } -}); - -fn main() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let dict = PyDict::new(py); - dict.set_item(py, "MyType", py.get_type::()).unwrap(); - py.run("assert MyType(42).half() == 21", None, Some(&dict)).unwrap(); -} -``` - -# Generated Rust type - -The above example generates the following Rust type: - -```ignore -struct MyType { ... } - -impl ToPythonObject for MyType { ... } -impl PythonObject for MyType { ... } -impl PythonObjectWithCheckedDowncast for MyType { ... } -impl PythonObjectWithTypeObject for MyType { ... } -impl PythonObjectFromPyClassMacro for MyType { ... } - -impl MyType { - fn create_instance(py: Python, number: i32) -> PyResult { ... } - - // data accessors - fn number<'a>(&'a self, py: Python<'a>) -> &'a i32 { ... } - - // functions callable from python - pub fn __new__(_cls: &PyType, py: Python, arg: i32) -> PyResult { - MyType::create_instance(py, arg) - } - - pub fn half(&self, py: Python) -> PyResult { - println!("half() was called with self={:?}", self.number(py)); - Ok(self.number(py) / 2) - } -} -``` - -* The generated type implements a number of traits from the `pyo3` crate. -* The inherent `create_instance` method can create new Python objects - given the values for the data fields. -* Private accessors functions are created for the data fields. -* All functions callable from Python are also exposed as public Rust functions. -* To convert from `MyType` to `PyObject`, use `as_object()` or `into_object()` (from the `PythonObject` trait). -* To convert `PyObject` to `MyType`, use `obj.cast_as::(py)` or `obj.cast_into::(py)`. - -# py_class body -The body of a `py_class!` supports the following definitions: - -## Data declarations -`data data_name: data_type;` - -Declares a data field within the Python class. -Used to store Rust data directly in the Python object instance. - -Because Python code can pass all Python objects to other threads, -`data_type` must be `Send + 'static`. - -Because Python object instances can be freely shared (Python has no concept of "ownership"), -data fields cannot be declared as `mut`. -If mutability is required, you have to use interior mutability (`Cell` or `RefCell`). - -If data members are used to store references to other Python objects, make sure -to read the section "Garbage Collector Integration". - -Data declarations are not accessible from Python. -On the Rust side, data is accessed through the automatically generated accessor functions: -```ignore -impl MyType { - fn data_name<'a>(&'a self, py: Python<'a>) -> &'a data_type { ... } -} -``` - -## Instance methods -`def method_name(&self, parameter-list) -> PyResult<...> { ... }` - -Declares an instance method callable from Python. - -* Because Python objects are potentially shared, the self parameter must always - be a shared reference (`&self`). -* For details on `parameter-list`, see the documentation of `py_argparse!()`. -* The return type must be `PyResult` for some `T` that implements `ToPyObject`. - -## Class methods -`@classmethod def method_name(cls, parameter-list) -> PyResult<...> { ... }` - -Declares a class method callable from Python. - -* The first parameter is the type object of the class on which the method is called. - This may be the type object of a derived class. -* The first parameter implicitly has type `&PyType`. This type must not be explicitly specified. -* For details on `parameter-list`, see the documentation of `py_argparse!()`. -* The return type must be `PyResult` for some `T` that implements `ToPyObject`. - -## Static methods -`@staticmethod def method_name(parameter-list) -> PyResult<...> { ... }` - -Declares a static method callable from Python. - -* For details on `parameter-list`, see the documentation of `py_argparse!()`. -* The return type must be `PyResult` for some `T` that implements `ToPyObject`. - -## __new__ -`def __new__(cls, parameter-list) -> PyResult<...> { ... }` - -Declares a constructor method callable from Python. - -* If no `__new__` method is declared, object instances can only be created from Rust (via `MyType::create_instance`), - but not from Python. -* The first parameter is the type object of the class to create. - This may be the type object of a derived class declared in Python. -* The first parameter implicitly has type `&PyType`. This type must not be explicitly specified. -* For details on `parameter-list`, see the documentation of `py_argparse!()`. -* The return type must be `PyResult` for some `T` that implements `ToPyObject`. - Usually, `T` will be `MyType`. - -## Garbage Collector Integration - -If your type owns references to other python objects, you will need to -integrate with Python's garbage collector so that the GC is aware of -those references. -To do this, implement the special member functions `__traverse__` and `__clear__`. -These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API. - -`__traverse__` must call `visit.call()` for each reference to another python object. - -`__clear__` must clear out any mutable references to other python objects -(thus breaking reference cycles). Immutable references do not have to be cleared, -as every cycle must contain at least one mutable reference. - -Example: - -``` -#[macro_use] extern crate pyo3; -use std::{mem, cell}; -use pyo3::{PyObject, PyDrop}; - -py_class!(class ClassWithGCSupport |py| { - data obj: cell::RefCell>; - - def __traverse__(&self, visit) { - if let Some(ref obj) = *self.obj(py).borrow() { - try!(visit.call(obj)) - } - Ok(()) - } - - def __clear__(&self) { - let old_obj = mem::replace(&mut *self.obj(py).borrow_mut(), None); - // Release reference only after the mutable borrow has expired, - // see Caution note below. - old_obj.release_ref(py); - } -}); -# fn main() {} -``` - -Caution: `__traverse__` may be called by the garbage collector: - - * during any python operation that takes a `Python` token as argument - * indirectly from the `PyObject` (or derived type) `Drop` implementation - * if your code releases the GIL, at any time by other threads. - -If you are using `RefCell`, you must not perform any of the above -operations while your code holds a mutable borrow, or you may cause the borrow -in `__traverse__` to panic. - -This is why the example above uses the `mem::replace`/`release_ref` dance: -`release_ref` (or the implicit `Drop`) can only be called safely in a separate -statement, after the mutable borrow on the `RefCell` has expired. - -Note that this restriction applies not only to `__clear__`, but to all methods -that use `RefCell::borrow_mut`. - -## Iterator Types - -Iterators can be defined using the Python special methods `__iter__` and `__next__`: - - * `def __iter__(&self) -> PyResult` - * `def __next__(&self) -> PyResult>` - - Returning `Ok(None)` from `__next__` indicates that that there are no further items. - -Example: - -``` -#[macro_use] extern crate pyo3; -use std::cell::RefCell; -use pyo3::{PyObject, PyClone, PyResult}; - -py_class!(class MyIterator |py| { - data iter: RefCell + Send>>; - - def __iter__(&self) -> PyResult { - Ok(self.clone_ref(py)) - } - - def __next__(&self) -> PyResult> { - Ok(self.iter(py).borrow_mut().next()) - } -}); -# fn main() {} -``` - -## String Conversions - - * `def __repr__(&self) -> PyResult>` - * `def __str__(&self) -> PyResult>` - - Possible return types for `__str__` and `__repr__` are `PyResult` or `PyResult`. - - In Python 2.7, Unicode strings returned by `__str__` and `__repr__` will be converted to byte strings - by the Python runtime, which results in an exception if the string contains non-ASCII characters. - - * `def __bytes__(&self) -> PyResult` - - On Python 3.x, provides the conversion to `bytes`. - On Python 2.7, `__bytes__` is allowed but has no effect. - - * `def __unicode__(&self) -> PyResult` - - On Python 2.7, provides the conversion to `unicode`. - On Python 3.x, `__unicode__` is allowed but has no effect. - - * `def __format__(&self, format_spec: &str) -> PyResult>` - - Special method that is used by the `format()` builtin and the `str.format()` method. - Possible return types are `PyResult` or `PyResult`. - -## Comparison operators - - * `def __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult` - - Overloads Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`). - The `op` argument indicates the comparison operation being performed. - The return type will normally be `PyResult`, but any Python object can be returned. - - If `other` is not of the type specified in the signature, the generated code will - automatically `return NotImplemented`. - - * `def __hash__(&self) -> PyResult` - - Objects that compare equal must have the same hash value. - The return type must be `PyResult` where `T` is one of Rust's primitive integer types. - -## Emulating Container Types - - * `def __len__(&self) -> PyResult` - - Called by the built-in Python function `len()`. - - * `def __length_hint__(&self) -> PyResult` - - Should return an estimated length for the object. - This method is purely an optimization and is never required for correctness. - - `__length_hint__` is new in Python 3.4; older versions will ignore the method. - - * `def __getitem__(&self, key: impl FromPyObject) -> PyResult` - - Called by the Python subscript operator `self[key]`. - - * `def __setitem__(&self, key: impl FromPyObject, value: impl FromPyObject) -> PyResult<()>` - - Called by Python `self[key] = value`. - - * `def __delitem__(&self, key: impl FromPyObject) -> PyResult<()>` - - Called by Python `del self[key]`. - - * `def __reversed__(&self) -> PyResult` - - Called by the `reversed()` built-in. - It should return a new iterator object that iterates over all the objects in the container in reverse order. - - * `def __contains__(&self, item: impl FromPyObject) -> PyResult` - - Called by Python `item in self`. - For mapping types, this should consider the keys of the mapping rather than the values - or the key-item pairs. - - If extraction of the `item` parameter fails with `TypeError`, - `__contains__` will return `Ok(false)`. - -## Arithmetic methods - - * `def __add__(lhs, rhs) -> PyResult` - * `def __sub__(lhs, rhs) -> PyResult` - * `def __mul__(lhs, rhs) -> PyResult` - * `def __lshift__(lhs, rhs) -> PyResult` - * `def __rshift__(lhs, rhs) -> PyResult` - * `def __and__(lhs, rhs) -> PyResult` - * `def __xor__(lhs, rhs) -> PyResult` - * `def __or__(lhs, rhs) -> PyResult` - - The parameters `lhs` and `rhs` must not be given an explicit type. - Within the method bodies, both parameters will implicitly have type `&PyObject`. - - There are no separate "reversed" versions of these methods (`__radd__()`, etc.) - Instead, if the first operand cannot perform the operation, - the same method of the second operand is called, with the operands in the same order. - - This means that you can't rely on the first parameter of these methods being `self` - or being the correct type, and you should test the types of both operands before deciding what to do. - If you can't handle the combination of types you've been given, - you should return `Ok(py.NotImplemented())`. - - * `def __iadd__(&self, other: impl FromPyObject) -> PyResult` - * `def __isub__(&self, other: impl FromPyObject) -> PyResult` - * `def __imul__(&self, other: impl FromPyObject) -> PyResult` - * `def __imatmul__(&self, other: impl FromPyObject) -> PyResult` - * `def __itruediv__(&self, other: impl FromPyObject) -> PyResult` - * `def __ifloordiv__(&self, other: impl FromPyObject) -> PyResult` - * `def __imod__(&self, other: impl FromPyObject) -> PyResult` - * `def __ilshift__(&self, other: impl FromPyObject) -> PyResult` - * `def __irshift__(&self, other: impl FromPyObject) -> PyResult` - * `def __iand__(&self, other: impl FromPyObject) -> PyResult` - * `def __ixor__(&self, other: impl FromPyObject) -> PyResult` - * `def __ior__(&self, other: impl FromPyObject) -> PyResult` - - Handles inplace operations if possible, falling back to the non-inplace versions. - These methods must return a new reference! In the common case of returning the - same (mutated) object, you will want to return `Ok(self.clone_ref(py))`. - - If you can't handle the combination of types you've been given, - you should return `Ok(py.NotImplemented())`. - -## Context Manager - - * `def __enter__(&self) -> PyResult` - * `def __exit__(&self, ty: Option, value: PyObject, traceback: PyObject) -> PyResult` - -## Other Special Methods - - * `def __bool__(&self) -> PyResult` - - Determines the "truthyness" of the object. - - Note that `py_class!` always expects this member to be called `__bool__`, - even on Python 2.7 where the Python spelling was `__nonzero__`. - - * `def __call__(&self, parameter-list) -> PyResult` - - For details on `parameter-list`, see the documentation of `py_argparse!()`. - The return type must be `PyResult` for some `T` that implements `ToPyObject`. - - -*/ -#[macro_export] -macro_rules! py_class { - (class $class:ident |$py: ident| { $( $body:tt )* }) => ( - py_class_impl! { - { $( $body )* } - $class $py - /* info: */ { - /* base_type: */ $crate::PyObject, - /* size: */ <$crate::PyObject as $crate::py_class::BaseObject>::size(), - /* class_visibility: */ {}, - /* gc: */ { - /* traverse_proc: */ None, - /* traverse_data: */ [ /*name*/ ] - }, - /* data: */ [ /* { offset, name, type } */ ] - // TODO: base type, documentation, ... - } - /* slots: */ { - /* type_slots */ [ /* slot: expr, */ ] - /* as_async */ [ /* slot: expr, */ ] - /* as_number */ [ /* slot: expr, */ ] - /* as_sequence */ [ /* slot: expr, */ ] - /* as_mapping */ [ /* slot: expr, */ ] - /* as_buffer */ [ /* slot: expr, */ ] - /* setitem_delitem */ [ - sdi_setitem: {}, - sdi_delitem: {}, - ] - } - /* impls: */ { /* impl body */ } - /* members: */ { /* ident = expr; */ } - /* properties: */ { /* expr; */ } - } - ); - (pub class $class:ident |$py: ident| { $( $body:tt )* }) => ( - py_class_impl! { - { $( $body )* } - $class $py - /* info: */ { - /* base_type: */ $crate::PyObject, - /* size: */ <$crate::PyObject as $crate::py_class::BaseObject>::size(), - /* class_visibility: */ {pub}, - /* gc: */ { - /* traverse_proc: */ None, - /* traverse_data: */ [ /*name*/ ] - }, - /* data: */ [ /* { offset, name, type } */ ] - // TODO: base type, documentation, ... - } - /* slots: */ { - /* type_slots */ [ /* slot: expr, */ ] - /* as_async */ [ /* slot: expr, */ ] - /* as_number */ [ /* slot: expr, */ ] - /* as_sequence */ [ /* slot: expr, */ ] - /* as_mapping */ [ /* slot: expr, */ ] - /* as_buffer */ [ /* slot: expr, */ ] - /* setitem_delitem */ [ - sdi_setitem: {}, - sdi_delitem: {}, - ] - } - /* impls: */ { /* impl body */ } - /* members: */ { /* ident = expr; */ } - /* properties: */ { /* expr; */ } - } - ); -} - - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_impl_item { - { $class:ident, $py:ident, $name:ident( $( $selfarg:tt )* ) - $res_type:ty; $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] - } => { py_coerce_item! { - impl $class { - pub fn $name($( $selfarg )* $py: $crate::Python $( , $pname: $ptype )* ) - -> $res_type $body - } - }} -} diff --git a/src/py_class/py_class_impl.py b/src/py_class/py_class_impl.py deleted file mode 100644 index be8da0cc..00000000 --- a/src/py_class/py_class_impl.py +++ /dev/null @@ -1,827 +0,0 @@ -#!/usr/bin/env python - -""" -This python script generates the py_class_impl! macro. -""" - -from collections import namedtuple -import sys, os - -PY2 = (os.getenv('PY') == '2') - -header = ''' -// Copyright (c) 2016 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. -''' - -macro_start = ''' -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_impl { - // TT muncher macro. Results are accumulated in $info $slots $impls and $members. -''' - -base_case = ''' - // Base case: we're done munching and can start producing code: - { {} - $class:ident $py:ident - /* info: */ { - $base_type:ty, - $size:expr, - { $( $class_visibility:tt )* }, - $gc:tt, - /* data: */ [ $( { $data_offset:expr, $data_name:ident, $data_ty:ty } )* ] - } - $slots:tt { $( $imp:item )* } $members:tt $properties:tt - } => { - py_coerce_item! { - $($class_visibility)* struct $class { _unsafe_inner: $crate::PyObject } - } - - py_impl_to_py_object_for_python_object!($class); - py_impl_from_py_object_for_python_object!($class); - - impl $crate::PythonObject for $class { - #[inline] - fn as_object(&self) -> &$crate::PyObject { - &self._unsafe_inner - } - - #[inline] - fn into_object(self) -> $crate::PyObject { - self._unsafe_inner - } - - /// 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: $crate::PyObject) -> Self { - $class { _unsafe_inner: obj } - } - - /// 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 $crate::PyObject) -> &'a Self { - ::std::mem::transmute(obj) - } - } - - impl $crate::PythonObjectWithCheckedDowncast for $class { - #[inline] - fn downcast_from<'p>(py: $crate::Python<'p>, obj: $crate::PyObject) -> Result<$class, $crate::PythonObjectDowncastError<'p>> { - if py.get_type::<$class>().is_instance(py, &obj) { - Ok($class { _unsafe_inner: obj }) - } else { - Err($crate::PythonObjectDowncastError(py)) - } - } - - #[inline] - fn downcast_borrow_from<'a, 'p>(py: $crate::Python<'p>, obj: &'a $crate::PyObject) -> Result<&'a $class, $crate::PythonObjectDowncastError<'p>> { - if py.get_type::<$class>().is_instance(py, obj) { - unsafe { Ok(::std::mem::transmute(obj)) } - } else { - Err($crate::PythonObjectDowncastError(py)) - } - } - } - - py_coerce_item! { - impl $crate::py_class::BaseObject for $class { - type InitType = ( $( $data_ty, )* ); - - #[inline] - fn size() -> usize { - $size - } - - unsafe fn alloc( - py: $crate::Python, - ty: &$crate::PyType, - ( $( $data_name, )* ): Self::InitType - ) -> $crate::PyResult<$crate::PyObject> - { - let obj = try!(<$base_type as $crate::py_class::BaseObject>::alloc(py, ty, ())); - $( $crate::py_class::data_init::<$data_ty>(py, &obj, $data_offset, $data_name); )* - Ok(obj) - } - - unsafe fn dealloc(py: $crate::Python, obj: *mut $crate::_detail::ffi::PyObject) { - $( $crate::py_class::data_drop::<$data_ty>(py, obj, $data_offset); )* - <$base_type as $crate::py_class::BaseObject>::dealloc(py, obj) - } - } - } - $($imp)* - py_coerce_item! { - impl $class { - fn create_instance(py: $crate::Python $( , $data_name : $data_ty )* ) -> $crate::PyResult<$class> { - let obj = try!(unsafe { - <$class as $crate::py_class::BaseObject>::alloc( - py, &py.get_type::<$class>(), ( $($data_name,)* ) - ) - }); - return Ok($class { _unsafe_inner: obj }); - - // hide statics in create_instance to avoid name conflicts - static mut TYPE_OBJECT : $crate::_detail::ffi::PyTypeObject - = py_class_type_object_static_init!($class, $gc, $slots); - static mut INIT_ACTIVE: bool = false; - - // trait implementations that need direct access to TYPE_OBJECT - impl $crate::PythonObjectWithTypeObject for $class { - fn type_object(py: $crate::Python) -> $crate::PyType { - unsafe { - if $crate::py_class::is_ready(py, &TYPE_OBJECT) { - $crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT) - } else { - // automatically initialize the class on-demand - <$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, None) - .expect(concat!("An error occurred while initializing class ", stringify!($class))) - } - } - } - } - - impl $crate::py_class::PythonObjectFromPyClassMacro for $class { - 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)); - } - assert!(!INIT_ACTIVE, - concat!("Reentrancy detected: already initializing class ", - stringify!($class))); - INIT_ACTIVE = true; - 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, 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); - py_class_init_properties!($class, $py, TYPE_OBJECT, $properties); - unsafe { - if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 { - Ok($crate::PyType::from_type_ptr($py, &mut TYPE_OBJECT)) - } else { - Err($crate::PyErr::fetch($py)) - } - } - } - } - } - } - }; - - { { property $name:ident { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt { $( $imp:item )* } $members:tt - { $( $properties:expr; )* } - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots { $($imp)* } $members - /* properties: */ { - $( $properties; )* - py_class_property_impl! { { $($body)* } $class $py $name { } }; - } - }}; -''' - -indentation = [' '] -last_char = '\n' - -def write(text): - global last_char - for line in text.splitlines(True): - line = line.lstrip(' ') - if len(line.strip()) == 0 and last_char == '\n': - continue - if last_char == '\n': - initial_closing = 0 - for c in line: - if c in ']}': - initial_closing += 1 - else: - break - if initial_closing: - sys.stdout.write(''.join(indentation[:-initial_closing])) - else: - sys.stdout.write(''.join(indentation)) - elif last_char not in ' \n' and len(line) > 0 and line[0] not in ' \n;': - sys.stdout.write(' ') - sys.stdout.write(line) - min_indent_level = len(indentation) - for c in line: - if c in '[{': - if len(indentation) > min_indent_level: - indentation.append('') - else: - indentation.append(' ') - elif c in ']}': - indentation.pop() - if len(indentation) < min_indent_level: - min_indent_level = len(indentation) - last_char = line[-1] - -slot_groups = ( - ('tp', 'type_slots', None), - ('am', 'as_async', None), - ('nb', 'as_number', None), - ('sq', 'as_sequence', None), - ('mp', 'as_mapping', None), - ('bf', 'as_buffer', None), - ('sdi', 'setdelitem', ['sdi_setitem', 'sdi_delitem']) -) - -def generate_case(pattern, old_info=None, new_info=None, new_impl=None, new_slots=None, new_members=None): - write('{ { %s $($tail:tt)* }\n' % pattern) - write('$class:ident $py:ident') - if old_info is not None: - write(old_info) - elif new_info is not None: - write('\n/* info: */ {\n') - write('$base_type: ty,\n') - write('$size: expr,\n') - write('$class_visibility: tt,\n') - write('$gc: tt,\n') - write('[ $( $data:tt )* ]\n') - write('}\n') - else: - write('$info:tt') - if new_slots: - write('\n/* slots: */ {\n') - for prefix, group_name, explicit_slots in slot_groups: - if any(s.startswith(prefix) for s, v in new_slots): - if explicit_slots is None: - write('\n/* %s */ [ $( $%s_slot_name:ident : $%s_slot_value:expr, )* ]\n' - % (group_name, prefix, prefix)) - else: - write('\n/* %s */ [\n' % group_name) - for slot in explicit_slots: - if any(s == slot for s, v in new_slots): - write('%s: {},\n' % slot) - else: - write('%s: $%s_slot_value:tt,\n' % (slot, slot)) - write(']\n') - else: - write('$%s:tt' % group_name) - write('\n}\n') - else: - write('$slots:tt') - if new_impl is not None: - write('\n{ $( $imp:item )* }\n') - else: - write('$impls:tt') - if new_members: - write('\n{ $( $member_name:ident = $member_expr:expr; )* }') - else: - write('$members:tt') - - write('$properties:tt') - - write('\n} => { py_class_impl! {\n') - write('{ $($tail)* }\n') - write('$class $py') - write(new_info or '$info') - if new_slots: - write('\n/* slots: */ {\n') - for prefix, group_name, explicit_slots in slot_groups: - if any(s.startswith(prefix) for s, v in new_slots): - write('\n/* %s */ [\n' % group_name) - if explicit_slots is None: - write('$( $%s_slot_name : $%s_slot_value, )*\n' % (prefix, prefix)) - for s, v in new_slots: - if s.startswith(prefix): - write('%s: %s,\n' % (s, v)) - else: - for slot in explicit_slots: - slot_value = next((v for s, v in new_slots if s == slot), None) - if slot_value is None: - write('%s: $%s_slot_value,\n' % (slot, slot)) - else: - write('%s: { %s },\n' % (slot, slot_value)) - write(']\n') - else: - write('$%s' % group_name) - write('\n}\n') - else: - write('$slots') - if new_impl is not None: - write('\n/* impl: */ {\n') - write('$($imp)*\n') - write(new_impl) - write('\n}\n') - else: - write('$impls') - if new_members: - write('\n/* members: */ {\n') - write('$( $member_name = $member_expr; )*\n') - for name, val in new_members: - write('%s = %s;\n' % (name, val)) - write('}') - else: - write('$members') - write('$properties') - write('\n}};\n') - -def data_decl(): - generate_case('data $data_name:ident : $data_type:ty;', - new_info = ''' - /* info: */ { - $base_type, - /* size: */ $crate::py_class::data_new_size::<$data_type>($size), - $class_visibility, - $gc, - /* data: */ [ - $($data)* - { - $crate::py_class::data_offset::<$data_type>($size), - $data_name, - $data_type - } - ] - } - ''', - new_impl=''' - impl $class { - fn $data_name<'a>(&'a self, py: $crate::Python<'a>) -> &'a $data_type { - unsafe { - $crate::py_class::data_get::<$data_type>( - py, - &self._unsafe_inner, - $crate::py_class::data_offset::<$data_type>($size) - ) - } - } - } - ''') - -def generate_class_method(special_name=None, decoration='', - slot=None, add_member=False, value_macro=None, value_args=None): - name_pattern = special_name or '$name:ident' - name_use = special_name or '$name' - def impl(with_params): - if with_params: - param_pattern = ', $($p:tt)+' - impl = '''py_argparse_parse_plist_impl!{ - py_class_impl_item { $class, $py, %s($cls: &$crate::PyType,) $res_type; { $($body)* } } - [] ($($p)+,) - }''' % name_use - value = 'py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \ - % (value_macro, value_args) - else: - param_pattern = '' - impl = 'py_class_impl_item! { $class, $py,%s($cls: &$crate::PyType,) $res_type; { $($body)* } [] }' \ - % name_use - value = '%s!{%s []}' % (value_macro, value_args) - pattern = '%s def %s ($cls:ident%s) -> $res_type:ty { $( $body:tt )* }' \ - % (decoration, name_pattern, param_pattern) - slots = [] - if slot is not None: - slots.append((slot, value)) - members = [] - if add_member: - members.append((name_use, value)) - generate_case(pattern, new_impl=impl, new_slots=slots, new_members=members) - impl(False) # without parameters - impl(True) # with parameters - -def traverse_and_clear(): - generate_case('def __traverse__(&$slf:tt, $visit:ident) $body:block', - old_info = ''' - /* info: */ { - $base_type: ty, - $size: expr, - $class_visibility: tt, - /* gc: */ { - /* traverse_proc: */ None, - $traverse_data: tt - }, - $datas: tt - } - ''', - new_info=''' - /* info: */ { - $base_type, - $size, - $class_visibility, - /* gc: */ { - /* traverse_proc: */ $class::__traverse__, - $traverse_data - }, - $datas - } - ''', - new_impl=''' - py_coerce_item!{ - impl $class { - fn __traverse__(&$slf, - $py: $crate::Python, - $visit: $crate::py_class::gc::VisitProc) - -> Result<(), $crate::py_class::gc::TraverseError> - $body - } - } - ''') - generate_case('def __clear__ (&$slf:ident) $body:block', - new_slots=[('tp_clear', 'py_class_tp_clear!($class)')], - new_impl=''' - py_coerce_item!{ - impl $class { - fn __clear__(&$slf, $py: $crate::Python) $body - } - } - ''') - -def generate_instance_method(special_name=None, decoration='', - slot=None, add_member=False, value_macro=None, value_args=None): - name_pattern = special_name or '$name:ident' - name_use = special_name or '$name' - def impl(with_params): - if with_params: - param_pattern = ', $($p:tt)+' - impl = '''py_argparse_parse_plist_impl!{ - py_class_impl_item { $class, $py, %s(&$slf,) $res_type; { $($body)* } } - [] ($($p)+,) - }''' % name_use - value = 'py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \ - % (value_macro, value_args) - else: - param_pattern = '' - impl = 'py_class_impl_item! { $class, $py, %s(&$slf,) $res_type; { $($body)* } [] }' \ - % name_use - value = '%s!{%s []}' % (value_macro, value_args) - pattern = '%s def %s (&$slf:ident%s) -> $res_type:ty { $( $body:tt )* }' \ - % (decoration, name_pattern, param_pattern) - slots = [] - if slot is not None: - slots.append((slot, value)) - members = [] - if add_member: - members.append((name_use, value)) - generate_case(pattern, new_impl=impl, new_slots=slots, new_members=members) - impl(False) # without parameters - impl(True) # with parameters - -def static_method(): - generate_case( - '@staticmethod def $name:ident ($($p:tt)*) -> $res_type:ty { $( $body:tt )* }', - new_impl=''' - py_argparse_parse_plist!{ - py_class_impl_item { $class, $py, $name() $res_type; { $($body)* } } - ($($p)*) - } - ''', - new_members=[('$name', ''' - py_argparse_parse_plist!{ - py_class_static_method {$py, $class::$name} - ($($p)*) - } - ''')]) - -def static_data(): - generate_case('static $name:ident = $init:expr;', - new_members=[('$name', '$init')]) - -macro_end = ''' -} -''' - -def special_method(decorated_function): - def wrap1(*args, **kwargs): - def wrap2(special_name): - return decorated_function(special_name, *args, **kwargs) - return wrap2 - return wrap1 - -@special_method -def error(special_name, msg): - print(''' - { { def %s $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "%s" } - };''' % (special_name, msg)) - -@special_method -def unimplemented(special_name): - return error('%s is not supported by py_class! yet.' % special_name)(special_name) - -@special_method -def normal_method(special_name): - pass - -@special_method -def special_class_method(special_name, *args, **kwargs): - generate_class_method(special_name=special_name, *args, **kwargs) - - -class Argument(object): - def __init__(self, name, arg_type=None): - self.name = name - - if arg_type is not None: - if arg_type == 'int': - arg_type = '$crate::_detail::libc::c_int' - - self.type = arg_type - - def get_type(self): - if self.type is not None: - return self.type - else: - return '$%s_type' % self.name - - def get_arg_pattern(self): - if self.type is not None: - return ', ${0}:ident '.format(self.name) - else: - return ', ${0}:ident : ${0}_type:ty'.format(self.name) - - def get_arg_param_item(self): - return '{{ ${0} : {1} = {{}} }}'.format(self.name, self.get_type()) - - -@special_method -def operator(special_name, slot, - args=(), - wrapper=None, - res_type='PyObject', - res_conv=None, - res_ffi_type='*mut $crate::_detail::ffi::PyObject', - additional_slots=() -): - if res_conv is None: - if res_type == 'void': - res_conv = '$crate::py_class::slots::VoidCallbackConverter' - if res_type == '()': - res_conv = '$crate::py_class::slots::UnitCallbackConverter' - res_ffi_type = '$crate::_detail::libc::c_int' - elif res_type == 'bool': - res_conv = '$crate::py_class::slots::BoolConverter' - res_ffi_type = '$crate::_detail::libc::c_int' - elif res_type == 'PyObject': - res_conv = '$crate::_detail::PyObjectCallbackConverter' - else: - res_conv = '$crate::_detail::PythonObjectCallbackConverter::<$crate::%s>(::std::marker::PhantomData)' % res_type - arg_pattern = '' - param_list = [] - for arg in args: - arg_pattern += arg.get_arg_pattern() - param_list.append(arg.get_arg_param_item()) - if slot == 'sq_contains': - new_slots = [(slot, 'py_class_contains_slot!($class::%s, $%s_type)' % (special_name, args[0].name))] - elif slot == 'tp_richcompare': - new_slots = [(slot, 'py_class_richcompare_slot!($class::%s, $%s_type, %s, %s)' - % (special_name, args[0].name, res_ffi_type, res_conv))] - elif len(args) == 0: - new_slots = [(slot, 'py_class_unary_slot!($class::%s, %s, %s)' - % (special_name, res_ffi_type, res_conv))] - elif len(args) == 1: - new_slots = [(slot, 'py_class_binary_slot!($class::%s, %s, %s, %s)' - % (special_name, args[0].get_type(), res_ffi_type, res_conv))] - elif len(args) == 2: - if wrapper is None: - wrapper = 'py_class_ternary_slot' - new_slots = [(slot, '%s!($class::%s, %s, %s, %s, %s)' - % (wrapper, special_name, args[0].get_type(), args[1].get_type(), res_ffi_type, res_conv))] - else: - raise ValueError('Unsupported argument count') - generate_case( - pattern='def %s(&$slf:ident%s) -> $res_type:ty { $($body:tt)* }' % (special_name, arg_pattern), - new_impl='py_class_impl_item! { $class, $py, %s(&$slf,) $res_type; { $($body)* } [%s] }' - % (special_name, ' '.join(param_list)), - new_slots=new_slots + list(additional_slots) - ) - # Generate fall-back matcher that produces an error - # when using the wrong method signature - error('Invalid signature for operator %s' % special_name)(special_name) - -@special_method -def call_operator(special_name, slot): - generate_instance_method( - special_name=special_name, - slot=slot, - value_macro='py_class_call_slot', - value_args='$class::%s' % special_name) - -@special_method -def binary_numeric_operator(special_name, slot): - generate_case( - pattern='def %s($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* }' - % special_name, - new_impl='py_class_impl_item! { $class, $py, %s() $res_type; { $($body)* } ' % special_name - +'[ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }', - new_slots=[(slot, 'py_class_binary_numeric_slot!($class::%s)' % special_name)] - ) - error('Invalid signature for binary numeric operator %s' % special_name)(special_name) - -@special_method -def reflected_numeric_operator(special_name): - error('Reflected numeric operator %s is not supported by py_class! Use __%s__ instead!' - % (special_name, special_name[3:-2]))(special_name) - -@special_method -def inplace_numeric_operator(special_name, slot): - operator(slot=slot, - args=[Argument('other')])(special_name) - -special_names = { - '__init__': error('__init__ is not supported by py_class!; use __new__ instead.'), - '__new__': special_class_method( - slot='tp_new', - value_macro='py_class_wrap_newfunc', - value_args='$class::__new__'), - '__del__': error('__del__ is not supported by py_class!; Use a data member with a Drop impl instead.'), - '__repr__': operator('tp_repr', res_type="PyString"), - '__str__': operator('tp_str', res_type="PyString"), - '__unicode__': normal_method(), - '__bytes__': normal_method(), - '__format__': normal_method(), - # Comparison Operators - '__lt__': error('__lt__ is not supported by py_class! use __richcmp__ instead.'), - '__le__': error('__le__ is not supported by py_class! use __richcmp__ instead.'), - '__gt__': error('__gt__ is not supported by py_class! use __richcmp__ instead.'), - '__ge__': error('__ge__ is not supported by py_class! use __richcmp__ instead.'), - '__eq__': error('__eq__ is not supported by py_class! use __richcmp__ instead.'), - '__ne__': error('__ne__ is not supported by py_class! use __richcmp__ instead.'), - '__cmp__': error('__cmp__ is not supported by py_class! use __richcmp__ instead.'), - '__richcmp__': operator('tp_richcompare', - res_type='PyObject', - args=[Argument('other'), Argument('op')]), - '__hash__': operator('tp_hash', - res_conv='$crate::py_class::slots::HashConverter', - res_ffi_type='$crate::Py_hash_t'), - '__nonzero__': error('__nonzero__ is not supported by py_class!; use the Python 3 spelling __bool__ instead.'), - '__bool__': operator('nb_nonzero' if PY2 else 'nb_bool', - res_type='bool'), - # Customizing attribute access - '__getattr__': unimplemented(), - '__getattribute__': unimplemented(), - '__setattr__': unimplemented(), - '__delattr__': unimplemented(), - '__dir__': unimplemented(), - - # Implementing Descriptors - '__get__': unimplemented(), - '__set__': unimplemented(), - '__delete__': unimplemented(), - - # Customizing instance and subclass checks - '__instancecheck__': unimplemented(), - '__subclasscheck__': unimplemented(), - - # Emulating callable objects - '__call__': call_operator('tp_call'), - - # Emulating container types - '__len__': operator('sq_length', - res_ffi_type='$crate::_detail::ffi::Py_ssize_t', - res_conv='$crate::py_class::slots::LenResultConverter', - additional_slots=[ - # Use PySequence_Size to forward mp_length calls to sq_length. - ('mp_length', 'Some($crate::_detail::ffi::PySequence_Size)') - ]), - '__length_hint__': normal_method(), - '__getitem__': operator('mp_subscript', - args=[Argument('key')], - additional_slots=[ - ('sq_item', 'Some($crate::py_class::slots::sq_item)') - ]), - '__missing__': normal_method(), - '__setitem__': operator('sdi_setitem', - args=[Argument('key'), Argument('value')], - res_type='()'), - '__delitem__': operator('sdi_delitem', - args=[Argument('key')], - res_type='()'), - '__iter__': operator('tp_iter'), - '__next__': operator('tp_iternext', - res_conv='$crate::py_class::slots::IterNextResultConverter'), - '__reversed__': normal_method(), - '__contains__': operator('sq_contains', args=[Argument('item')]), - - # Emulating numeric types - '__add__': binary_numeric_operator('nb_add'), - '__sub__': binary_numeric_operator('nb_subtract'), - '__mul__': binary_numeric_operator('nb_multiply'), - '__matmul__': unimplemented(), - '__div__': unimplemented(), - '__truediv__': unimplemented(), - '__floordiv__': unimplemented(), - '__mod__': unimplemented(), - '__divmod__': unimplemented(), - '__pow__': unimplemented(), - '__lshift__': binary_numeric_operator('nb_lshift'), - '__rshift__': binary_numeric_operator('nb_rshift'), - '__and__': binary_numeric_operator('nb_and'), - '__xor__': binary_numeric_operator('nb_xor'), - '__or__': binary_numeric_operator('nb_or'), - - # Emulating numeric types - reflected - '__radd__': reflected_numeric_operator(), - '__rsub__': reflected_numeric_operator(), - '__rmul__': reflected_numeric_operator(), - '__rmatmul__': reflected_numeric_operator(), - '__rdiv__': reflected_numeric_operator(), - '__rtruediv__': reflected_numeric_operator(), - '__rfloordiv__': reflected_numeric_operator(), - '__rmod__': reflected_numeric_operator(), - '__rdivmod__': reflected_numeric_operator(), - '__rpow__': reflected_numeric_operator(), - '__rlshift__': reflected_numeric_operator(), - '__rrshift__': reflected_numeric_operator(), - '__rand__': reflected_numeric_operator(), - '__rxor__': reflected_numeric_operator(), - '__ror__': reflected_numeric_operator(), - - # Emulating numeric types - in-place - '__iadd__': inplace_numeric_operator('nb_inplace_add'), - '__isub__': inplace_numeric_operator('nb_inplace_subtract'), - '__imul__': inplace_numeric_operator('nb_inplace_multiply'), - '__imatmul__': inplace_numeric_operator('nb_inplace_matrix_multiply'), - '__idiv__': unimplemented(), - '__itruediv__': inplace_numeric_operator('nb_inplace_true_divide'), - '__ifloordiv__': inplace_numeric_operator('nb_inplace_floor_divide'), - '__imod__': inplace_numeric_operator('nb_inplace_remainder'), - '__ipow__': unimplemented(), - '__ilshift__': inplace_numeric_operator('nb_inplace_lshift'), - '__irshift__': inplace_numeric_operator('nb_inplace_rshift'), - '__iand__': inplace_numeric_operator('nb_inplace_and'), - '__ixor__': inplace_numeric_operator('nb_inplace_xor'), - '__ior__': inplace_numeric_operator('nb_inplace_or'), - - # Unary arithmetic - '__neg__': operator('nb_negative'), - '__pos__': operator('nb_positive'), - '__abs__': operator('nb_absolute'), - '__invert__': operator('nb_invert'), - '__complex__': unimplemented(), - '__int__': unimplemented(), - '__long__': unimplemented(), - '__float__': unimplemented(), - '__round__': unimplemented(), - '__index__': unimplemented(), - '__coerce__': unimplemented(), - - # With statement context managers - '__enter__': normal_method(), - '__exit__': normal_method(), -} - -def main(): - if sys.argv[1:] == ['--format']: - while True: - line = sys.stdin.readline() - if len(line) == 0: - return - write(line) - print(header) - print('') - print('// !!!!!!!!!!!!!!!!!!!!!!!!!!!') - print('// THIS IS A GENERATED FILE !!') - print('// DO NOT MODIFY !!') - print('// !!!!!!!!!!!!!!!!!!!!!!!!!!!') - print(macro_start) - print(base_case) - data_decl() - traverse_and_clear() - for name, f in sorted(special_names.items()): - f(name) - generate_instance_method( - add_member=True, - value_macro='py_class_instance_method', - value_args='$py, $class::$name') - generate_class_method(decoration='@classmethod', - add_member=True, - value_macro='py_class_class_method', - value_args='$py, $class::$name') - static_method() - static_data() - print(macro_end) - -if __name__ == '__main__': - main() - diff --git a/src/py_class/py_class_impl.rs b/src/py_class/py_class_impl.rs deleted file mode 100644 index b5ce6b71..00000000 --- a/src/py_class/py_class_impl.rs +++ /dev/null @@ -1,1753 +0,0 @@ - -// Copyright (c) 2016 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. - - -// !!!!!!!!!!!!!!!!!!!!!!!!!!! -// THIS IS A GENERATED FILE !! -// DO NOT MODIFY !! -// !!!!!!!!!!!!!!!!!!!!!!!!!!! - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_impl { - // TT muncher macro. Results are accumulated in $info $slots $impls and $members. - - // Base case: we're done munching and can start producing code: - { {} - $class:ident $py:ident - /* info: */ { - $base_type:ty, - $size:expr, - { $( $class_visibility:tt )* }, - $gc:tt, - /* data: */ [ $( { $data_offset:expr, $data_name:ident, $data_ty:ty } )* ] - } - $slots:tt { $( $imp:item )* } $members:tt $properties:tt - } => { - py_coerce_item! { - #[$crate::cls::class] - $($class_visibility)* struct $class { _unsafe_inner: $crate::PyObject } - } - - py_coerce_item! { - impl $crate::py_class::BaseObject for $class { - type InitType = ( $( $data_ty, )* ); - - #[inline] - fn size() -> usize { - $size - } - - unsafe fn alloc( - py: $crate::Python, - ty: &$crate::PyType, - ( $( $data_name, )* ): Self::InitType - ) -> $crate::PyResult<$crate::PyObject> - { - let obj = try!(<$base_type as $crate::py_class::BaseObject>::alloc(py, ty, ())); - $( $crate::py_class::data_init::<$data_ty>(py, &obj, $data_offset, $data_name); )* - Ok(obj) - } - - unsafe fn dealloc(py: $crate::Python, obj: *mut $crate::_detail::ffi::PyObject) { - $( $crate::py_class::data_drop::<$data_ty>(py, obj, $data_offset); )* - <$base_type as $crate::py_class::BaseObject>::dealloc(py, obj) - } - } - } - $($imp)* - py_coerce_item! { - impl $class { - fn create_instance(py: $crate::Python $( , $data_name : $data_ty )* ) -> $crate::PyResult<$class> { - let obj = try!(unsafe { - <$class as $crate::py_class::BaseObject>::alloc( - py, &py.get_type::<$class>(), ( $($data_name,)* ) - ) - }); - return Ok($class { _unsafe_inner: obj }); - - // hide statics in create_instance to avoid name conflicts - static mut TYPE_OBJECT : $crate::_detail::ffi::PyTypeObject - = py_class_type_object_static_init!($class, $gc, $slots); - static mut INIT_ACTIVE: bool = false; - - // trait implementations that need direct access to TYPE_OBJECT - impl $crate::PythonObjectWithTypeObject for $class { - fn type_object(py: $crate::Python) -> $crate::PyType { - unsafe { - if $crate::py_class::is_ready(py, &TYPE_OBJECT) { - $crate::PyType::from_type_ptr(py, &mut TYPE_OBJECT) - } else { - // automatically initialize the class on-demand - <$class as $crate::py_class::PythonObjectFromPyClassMacro>::initialize(py, None) - .expect(concat!("An error occurred while initializing class ", stringify!($class))) - } - } - } - } - - impl $crate::py_class::PythonObjectFromPyClassMacro for $class { - 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)); - } - assert!(!INIT_ACTIVE, - concat!("Reentrancy detected: already initializing class ", - stringify!($class))); - INIT_ACTIVE = true; - 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, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> { - py_class_init_members!($class, $py, TYPE_OBJECT, $members); - unsafe { <$class as $crate::class::typeob::PyClassInit> - ::build_type($py, module_name, &mut TYPE_OBJECT) } - } - } - } - } - }; - - { { property $name:ident { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt { $( $imp:item )* } $members:tt - { $( $properties:expr; )* } - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots { $($imp)* } $members - /* properties: */ { - $( $properties; )* - py_class_property_impl! { { $($body)* } $class $py $name { } }; - } - }}; - - { { data $data_name:ident : $data_type:ty; $($tail:tt)* } - $class:ident $py:ident - /* info: */ { - $base_type: ty, - $size: expr, - $class_visibility: tt, - $gc: tt, - [ $( $data:tt )* ] - } - $slots:tt - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py - /* info: */ { - $base_type, - /* size: */ $crate::py_class::data_new_size::<$data_type>($size), - $class_visibility, - $gc, - /* data: */ [ - $($data)* - { - $crate::py_class::data_offset::<$data_type>($size), - $data_name, - $data_type - } - ] - } - $slots - /* impl: */ { - $($imp)* - impl $class { - fn $data_name<'a>(&'a self, py: $crate::Python<'a>) -> &'a $data_type { - unsafe { - $crate::py_class::data_get::<$data_type>( - py, - &self._unsafe_inner, - $crate::py_class::data_offset::<$data_type>($size) - ) - } - } - } - } - $members $properties - }}; - { { def __abs__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_absolute: py_class_unary_slot!($class::__abs__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __abs__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __abs__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __abs__" } - }; - { { def __add__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_add: py_class_binary_numeric_slot!($class::__add__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __add__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __add__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __add__" } - }; - - { { def __aenter__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__aenter__ is not supported by py_class! yet." } - }; - - { { def __aexit__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__aexit__ is not supported by py_class! yet." } - }; - { { def __aiter__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt - /* as_async */ [ $( $am_slot_name:ident : $am_slot_value:expr, )* ] - $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots - /* as_async */ [ - $( $am_slot_name : $am_slot_value, )* - am_aiter: py_class_unary_slot!($class::__aiter__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __aiter__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __aiter__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __aiter__" } - }; - { { def __and__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_and: py_class_binary_numeric_slot!($class::__and__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __and__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __and__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __and__" } - }; - { { def __anext__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt - /* as_async */ [ $( $am_slot_name:ident : $am_slot_value:expr, )* ] - $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots - /* as_async */ [ - $( $am_slot_name : $am_slot_value, )* - am_anext: py_class_unary_slot!($class::__anext__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __anext__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __bool__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_bool: py_class_unary_slot!($class::__bool__, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __bool__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __bool__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __bool__" } - }; - { { def __call__ (&$slf:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_call: py_class_call_slot!{$class::__call__ []}, - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __call__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - { { def __call__ (&$slf:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_call: py_argparse_parse_plist_impl!{py_class_call_slot {$class::__call__} [] ($($p)+,)}, - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_argparse_parse_plist_impl!{ - py_class_impl_item { $class, $py, __call__(&$slf,) $res_type; { $($body)* } } - [] ($($p)+,) - } - } - $members $properties - }}; - - { { def __cmp__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__cmp__ is not supported by py_class! use __richcmp__ instead." } - }; - - { { def __coerce__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__coerce__ is not supported by py_class! yet." } - }; - - { { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__complex__ is not supported by py_class! yet." } - }; - { { def __contains__(&$slf:ident, $item:ident : $item_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt $as_number:tt - /* as_sequence */ [ $( $sq_slot_name:ident : $sq_slot_value:expr, )* ] - $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async $as_number - /* as_sequence */ [ - $( $sq_slot_name : $sq_slot_value, )* - sq_contains: py_class_contains_slot!($class::__contains__, $item_type), - ] - $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __contains__(&$slf,) $res_type; { $($body)* } [{ $item : $item_type = {} }] } - } - $members $properties - }}; - - { { def __contains__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __contains__" } - }; - - { { def __del__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__del__ is not supported by py_class!; Use a data member with a Drop impl instead." } - }; - - { { def __delattr__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__delattr__ is not supported by py_class! yet." } - }; - - { { def __delete__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__delete__ is not supported by py_class! yet." } - }; - { { def __delitem__(&$slf:ident, $key:ident : $key_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt - /* setdelitem */ [ - sdi_setitem: $sdi_setitem_slot_value:tt, - sdi_delitem: {}, - ] - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async $as_number $as_sequence $as_mapping $as_buffer - /* setdelitem */ [ - sdi_setitem: $sdi_setitem_slot_value, - sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) }, - ] - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __delitem__(&$slf,) $res_type; { $($body)* } [{ $key : $key_type = {} }] } - } - $members $properties - }}; - - { { def __delitem__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __delitem__" } - }; - - { { def __dir__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__dir__ is not supported by py_class! yet." } - }; - - { { def __div__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__div__ is not supported by py_class! yet." } - }; - - { { def __divmod__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__divmod__ is not supported by py_class! yet." } - }; - - { { def __eq__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__eq__ is not supported by py_class! use __richcmp__ instead." } - }; - - { { def __float__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__float__ is not supported by py_class! yet." } - }; - - { { def __floordiv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__floordiv__ is not supported by py_class! yet." } - }; - - { { def __ge__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__ge__ is not supported by py_class! use __richcmp__ instead." } - }; - - { { def __get__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__get__ is not supported by py_class! yet." } - }; - - { { def __getattr__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__getattr__ is not supported by py_class! yet." } - }; - - { { def __getattribute__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__getattribute__ is not supported by py_class! yet." } - }; - { { def __getitem__(&$slf:ident, $key:ident : $key_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt $as_number:tt - /* as_sequence */ [ $( $sq_slot_name:ident : $sq_slot_value:expr, )* ] - /* as_mapping */ [ $( $mp_slot_name:ident : $mp_slot_value:expr, )* ] - $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async $as_number - /* as_sequence */ [ - $( $sq_slot_name : $sq_slot_value, )* - sq_item: Some($crate::py_class::slots::sq_item), - ] - /* as_mapping */ [ - $( $mp_slot_name : $mp_slot_value, )* - mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __getitem__(&$slf,) $res_type; { $($body)* } [{ $key : $key_type = {} }] } - } - $members $properties - }}; - - { { def __getitem__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __getitem__" } - }; - - { { def __gt__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__gt__ is not supported by py_class! use __richcmp__ instead." } - }; - { { def __hash__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_hash: py_class_unary_slot!($class::__hash__, $crate::Py_hash_t, $crate::py_class::slots::HashConverter), - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __hash__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __hash__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __hash__" } - }; - { { def __iadd__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_add: py_class_binary_slot!($class::__iadd__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __iadd__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __iadd__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __iadd__" } - }; - { { def __iand__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_and: py_class_binary_slot!($class::__iand__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __iand__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __iand__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __iand__" } - }; - - { { def __idiv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__idiv__ is not supported by py_class! yet." } - }; - { { def __ifloordiv__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_floor_divide: py_class_binary_slot!($class::__ifloordiv__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __ifloordiv__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __ifloordiv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __ifloordiv__" } - }; - { { def __ilshift__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_lshift: py_class_binary_slot!($class::__ilshift__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __ilshift__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __ilshift__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __ilshift__" } - }; - { { def __imatmul__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_matrix_multiply: py_class_binary_slot!($class::__imatmul__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __imatmul__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __imatmul__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __imatmul__" } - }; - { { def __imod__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_remainder: py_class_binary_slot!($class::__imod__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __imod__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __imod__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __imod__" } - }; - { { def __imul__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_multiply: py_class_binary_slot!($class::__imul__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __imul__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __imul__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __imul__" } - }; - - { { def __index__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__index__ is not supported by py_class! yet." } - }; - - { { def __init__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__init__ is not supported by py_class!; use __new__ instead." } - }; - - { { def __instancecheck__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__instancecheck__ is not supported by py_class! yet." } - }; - - { { def __int__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__int__ is not supported by py_class! yet." } - }; - { { def __invert__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_invert: py_class_unary_slot!($class::__invert__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __invert__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __invert__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __invert__" } - }; - { { def __ior__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_or: py_class_binary_slot!($class::__ior__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __ior__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __ior__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __ior__" } - }; - - { { def __ipow__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__ipow__ is not supported by py_class! yet." } - }; - { { def __irshift__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_rshift: py_class_binary_slot!($class::__irshift__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __irshift__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __irshift__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __irshift__" } - }; - { { def __isub__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_subtract: py_class_binary_slot!($class::__isub__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __isub__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __isub__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __isub__" } - }; - { { def __iter__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_iter: py_class_unary_slot!($class::__iter__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __iter__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __iter__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __iter__" } - }; - { { def __itruediv__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_true_divide: py_class_binary_slot!($class::__itruediv__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __itruediv__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __itruediv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __itruediv__" } - }; - { { def __ixor__(&$slf:ident, $other:ident : $other_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_inplace_xor: py_class_binary_slot!($class::__ixor__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __ixor__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} }] } - } - $members $properties - }}; - - { { def __ixor__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __ixor__" } - }; - - { { def __le__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__le__ is not supported by py_class! use __richcmp__ instead." } - }; - { { def __len__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt $as_number:tt - /* as_sequence */ [ $( $sq_slot_name:ident : $sq_slot_value:expr, )* ] - /* as_mapping */ [ $( $mp_slot_name:ident : $mp_slot_value:expr, )* ] - $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async $as_number - /* as_sequence */ [ - $( $sq_slot_name : $sq_slot_value, )* - sq_length: py_class_unary_slot!($class::__len__, $crate::_detail::ffi::Py_ssize_t, $crate::py_class::slots::LenResultConverter), - ] - /* as_mapping */ [ - $( $mp_slot_name : $mp_slot_value, )* - mp_length: Some($crate::_detail::ffi::PySequence_Size), - ] - $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __len__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __len__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __len__" } - }; - - { { def __long__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__long__ is not supported by py_class! yet." } - }; - { { def __lshift__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_lshift: py_class_binary_numeric_slot!($class::__lshift__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __lshift__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __lshift__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __lshift__" } - }; - - { { def __lt__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__lt__ is not supported by py_class! use __richcmp__ instead." } - }; - - { { def __matmul__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__matmul__ is not supported by py_class! yet." } - }; - - { { def __mod__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__mod__ is not supported by py_class! yet." } - }; - { { def __mul__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_multiply: py_class_binary_numeric_slot!($class::__mul__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __mul__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __mul__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __mul__" } - }; - - { { def __ne__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__ne__ is not supported by py_class! use __richcmp__ instead." } - }; - { { def __neg__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_negative: py_class_unary_slot!($class::__neg__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __neg__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __neg__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __neg__" } - }; - { { def __new__ ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_new: py_class_wrap_newfunc!{$class::__new__ []}, - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py,__new__($cls: &$crate::PyType,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - { { def __new__ ($cls:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_new: py_argparse_parse_plist_impl!{py_class_wrap_newfunc {$class::__new__} [] ($($p)+,)}, - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_argparse_parse_plist_impl!{ - py_class_impl_item { $class, $py, __new__($cls: &$crate::PyType,) $res_type; { $($body)* } } - [] ($($p)+,) - } - } - $members $properties - }}; - { { def __next__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_iternext: py_class_unary_slot!($class::__next__, *mut $crate::_detail::ffi::PyObject, $crate::py_class::slots::IterNextResultConverter), - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __next__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __next__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __next__" } - }; - - { { def __nonzero__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__nonzero__ is not supported by py_class!; use the Python 3 spelling __bool__ instead." } - }; - { { def __or__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_or: py_class_binary_numeric_slot!($class::__or__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __or__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __or__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __or__" } - }; - { { def __pos__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_positive: py_class_unary_slot!($class::__pos__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __pos__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __pos__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __pos__" } - }; - - { { def __pow__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__pow__ is not supported by py_class! yet." } - }; - - { { def __radd__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __radd__ is not supported by py_class! Use __add__ instead!" } - }; - - { { def __rand__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rand__ is not supported by py_class! Use __and__ instead!" } - }; - - { { def __rdiv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rdiv__ is not supported by py_class! Use __div__ instead!" } - }; - - { { def __rdivmod__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rdivmod__ is not supported by py_class! Use __divmod__ instead!" } - }; - { { def __repr__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_repr: py_class_unary_slot!($class::__repr__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PythonObjectCallbackConverter::<$crate::PyString>(::std::marker::PhantomData)), - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __repr__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __repr__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __repr__" } - }; - - { { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rfloordiv__ is not supported by py_class! Use __floordiv__ instead!" } - }; - { { def __richcmp__(&$slf:ident, $other:ident : $other_type:ty, $op:ident : $op_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_richcompare: py_class_richcompare_slot!($class::__richcmp__, $other_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __richcmp__(&$slf,) $res_type; { $($body)* } [{ $other : $other_type = {} } { $op : $op_type = {} }] } - } - $members $properties - }}; - - { { def __richcmp__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __richcmp__" } - }; - - { { def __rlshift__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rlshift__ is not supported by py_class! Use __lshift__ instead!" } - }; - - { { def __rmatmul__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rmatmul__ is not supported by py_class! Use __matmul__ instead!" } - }; - - { { def __rmod__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rmod__ is not supported by py_class! Use __mod__ instead!" } - }; - - { { def __rmul__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rmul__ is not supported by py_class! Use __mul__ instead!" } - }; - - { { def __ror__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __ror__ is not supported by py_class! Use __or__ instead!" } - }; - - { { def __round__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__round__ is not supported by py_class! yet." } - }; - - { { def __rpow__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rpow__ is not supported by py_class! Use __pow__ instead!" } - }; - - { { def __rrshift__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rrshift__ is not supported by py_class! Use __rshift__ instead!" } - }; - { { def __rshift__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_rshift: py_class_binary_numeric_slot!($class::__rshift__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __rshift__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __rshift__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __rshift__" } - }; - - { { def __rsub__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rsub__ is not supported by py_class! Use __sub__ instead!" } - }; - - { { def __rtruediv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rtruediv__ is not supported by py_class! Use __truediv__ instead!" } - }; - - { { def __rxor__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Reflected numeric operator __rxor__ is not supported by py_class! Use __xor__ instead!" } - }; - - { { def __set__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__set__ is not supported by py_class! yet." } - }; - - { { def __setattr__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__setattr__ is not supported by py_class! yet." } - }; - { { def __setitem__(&$slf:ident, $key:ident : $key_type:ty, $value:ident : $value_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt - /* setdelitem */ [ - sdi_setitem: {}, - sdi_delitem: $sdi_delitem_slot_value:tt, - ] - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async $as_number $as_sequence $as_mapping $as_buffer - /* setdelitem */ [ - sdi_setitem: { py_class_ternary_slot!($class::__setitem__, $key_type, $value_type, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) }, - sdi_delitem: $sdi_delitem_slot_value, - ] - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __setitem__(&$slf,) $res_type; { $($body)* } [{ $key : $key_type = {} } { $value : $value_type = {} }] } - } - $members $properties - }}; - - { { def __setitem__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __setitem__" } - }; - { { def __str__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - /* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ] - $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - /* type_slots */ [ - $( $tp_slot_name : $tp_slot_value, )* - tp_str: py_class_unary_slot!($class::__str__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PythonObjectCallbackConverter::<$crate::PyString>(::std::marker::PhantomData)), - ] - $as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __str__(&$slf,) $res_type; { $($body)* } [] } - } - $members $properties - }}; - - { { def __str__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for operator __str__" } - }; - { { def __sub__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_subtract: py_class_binary_numeric_slot!($class::__sub__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __sub__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __sub__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __sub__" } - }; - - { { def __subclasscheck__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__subclasscheck__ is not supported by py_class! yet." } - }; - - { { def __truediv__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "__truediv__ is not supported by py_class! yet." } - }; - { { def __xor__($left:ident, $right:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* } - $class:ident $py:ident $info:tt - /* slots: */ { - $type_slots:tt $as_async:tt - /* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ] - $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt - } - { $( $imp:item )* } - $members:tt $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info - /* slots: */ { - $type_slots $as_async - /* as_number */ [ - $( $nb_slot_name : $nb_slot_value, )* - nb_xor: py_class_binary_numeric_slot!($class::__xor__), - ] - $as_sequence $as_mapping $as_buffer $setdelitem - } - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, __xor__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] } - } - $members $properties - }}; - - { { def __xor__ $($tail:tt)* } $( $stuff:tt )* } => { - py_error! { "Invalid signature for binary numeric operator __xor__" } - }; - { { def $name:ident (&$slf:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt - { $( $imp:item )* } - { $( $member_name:ident = $member_expr:expr; )* } $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py, $name(&$slf,) $res_type; { $($body)* } [] } - } - /* members: */ { - $( $member_name = $member_expr; )* - $name = py_class_instance_method!{$py, $class::$name []}; - } $properties - }}; - { { def $name:ident (&$slf:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt - { $( $imp:item )* } - { $( $member_name:ident = $member_expr:expr; )* } $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots - /* impl: */ { - $($imp)* - py_argparse_parse_plist_impl!{ - py_class_impl_item { $class, $py, $name(&$slf,) $res_type; { $($body)* } } - [] ($($p)+,) - } - } - /* members: */ { - $( $member_name = $member_expr; )* - $name = py_argparse_parse_plist_impl!{py_class_instance_method {$py, $class::$name} [] ($($p)+,)}; - } $properties - }}; - { { @classmethod def $name:ident ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt - { $( $imp:item )* } - { $( $member_name:ident = $member_expr:expr; )* } $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots - /* impl: */ { - $($imp)* - py_class_impl_item! { $class, $py,$name($cls: &$crate::PyType,) $res_type; { $($body)* } [] } - } - /* members: */ { - $( $member_name = $member_expr; )* - $name = py_class_class_method!{$py, $class::$name []}; - } $properties - }}; - { { @classmethod def $name:ident ($cls:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt - { $( $imp:item )* } - { $( $member_name:ident = $member_expr:expr; )* } $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots - /* impl: */ { - $($imp)* - py_argparse_parse_plist_impl!{ - py_class_impl_item { $class, $py, $name($cls: &$crate::PyType,) $res_type; { $($body)* } } - [] ($($p)+,) - } - } - /* members: */ { - $( $member_name = $member_expr; )* - $name = py_argparse_parse_plist_impl!{py_class_class_method {$py, $class::$name} [] ($($p)+,)}; - } $properties - }}; - { { @staticmethod def $name:ident ($($p:tt)*) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt - { $( $imp:item )* } - { $( $member_name:ident = $member_expr:expr; )* } $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots - /* impl: */ { - $($imp)* - py_argparse_parse_plist!{ - py_class_impl_item { $class, $py, $name() $res_type; { $($body)* } } - ($($p)*) - } - } - /* members: */ { - $( $member_name = $member_expr; )* - $name = - py_argparse_parse_plist!{ - py_class_static_method {$py, $class::$name} - ($($p)*) - } - ; - } $properties - }}; - { { static $name:ident = $init:expr; $($tail:tt)* } - $class:ident $py:ident $info:tt $slots:tt $impls:tt - { $( $member_name:ident = $member_expr:expr; )* } $properties:tt - } => { py_class_impl! { - { $($tail)* } - $class $py $info $slots $impls - /* members: */ { - $( $member_name = $member_expr; )* - $name = $init; - } $properties - }}; - -} - diff --git a/src/py_class/slots.rs b/src/py_class/slots.rs deleted file mode 100644 index ac0a3ae6..00000000 --- a/src/py_class/slots.rs +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright (c) 2016 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 ffi; -use std::{isize, ptr}; -use std::os::raw::{c_int}; -use python::{Python, PythonObject}; -use conversion::ToPyObject; -use function::CallbackConverter; -use err::{PyErr, PyResult}; -use py_class::{CompareOp}; -use exc; -use Py_hash_t; - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_type_object_static_init { - ($class_name:ident, - $gc:tt, - /* slots: */ { - /* type_slots */ [ $( $slot_name:ident : $slot_value:expr, )* ] - $as_async:tt - $as_number:tt - $as_sequence:tt - $as_mapping:tt - $as_buffer:tt - $setdelitem:tt - }) => ( - $crate::_detail::ffi::PyTypeObject { - $( $slot_name : $slot_value, )* - .. - $crate::_detail::ffi::PyTypeObject_INIT - } - ); -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_wrap_newfunc { - ($class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{ - unsafe extern "C" fn wrap_newfunc( - cls: *mut $crate::_detail::ffi::PyTypeObject, - args: *mut $crate::_detail::ffi::PyObject, - kwargs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::_detail::PyObjectCallbackConverter, - |py| { - py_argparse_raw!(py, Some(LOCATION), args, kwargs, - [ $( { $pname : $ptype = $detail } )* ] - { - let cls = $crate::PyType::from_type_ptr(py, cls); - let ret = $class::$f(&cls, py $(, $pname )* ); - $crate::PyDrop::release_ref(cls, py); - ret - }) - }) - } - Some(wrap_newfunc) - }} -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_as_number { - ([]) => (0 as *mut $crate::_detail::ffi::PyNumberMethods); - ([$( $slot_name:ident : $slot_value:expr ,)+]) => {{ - static mut NUMBER_METHODS : $crate::_detail::ffi::PyNumberMethods - = $crate::_detail::ffi::PyNumberMethods { - $( $slot_name : $slot_value, )* - .. - $crate::_detail::ffi::PyNumberMethods_INIT - }; - unsafe { &mut NUMBER_METHODS } - }} -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_unary_slot { - ($class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ - unsafe extern "C" fn wrap_unary( - slf: *mut $crate::_detail::ffi::PyObject) - -> $res_type - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $conv, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let ret = slf.$f(py); - $crate::PyDrop::release_ref(slf, py); - ret - }) - } - Some(wrap_unary) - }} -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_binary_slot { - ($class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{ - unsafe extern "C" fn wrap_binary( - slf: *mut $crate::_detail::ffi::PyObject, - arg: *mut $crate::_detail::ffi::PyObject) - -> $res_type - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $conv, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let arg = $crate::PyObject::from_borrowed_ptr(py, arg); - let ret = match <$arg_type as $crate::FromPyObject>::extract(py, &arg) { - Ok(arg) => slf.$f(py, arg), - Err(e) => Err(e) - }; - $crate::PyDrop::release_ref(arg, py); - $crate::PyDrop::release_ref(slf, py); - ret - }) - } - Some(wrap_binary) - }} -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_ternary_slot { - ($class:ident :: $f:ident, $arg1_type:ty, $arg2_type:ty, $res_type:ty, $conv:expr) => {{ - unsafe extern "C" fn wrap_binary( - slf: *mut $crate::_detail::ffi::PyObject, - arg1: *mut $crate::_detail::ffi::PyObject, - arg2: *mut $crate::_detail::ffi::PyObject) - -> $res_type - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $conv, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1); - let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2); - let ret = match <$arg1_type as $crate::FromPyObject>::extract(py, &arg1) { - Ok(arg1) => match <$arg2_type as $crate::FromPyObject>::extract(py, &arg2) { - Ok(arg2) => slf.$f(py, arg1, arg2), - Err(e) => Err(e) - }, - Err(e) => Err(e) - }; - $crate::PyDrop::release_ref(arg1, py); - $crate::PyDrop::release_ref(arg2, py); - $crate::PyDrop::release_ref(slf, py); - ret - }) - } - Some(wrap_binary) - }} -} - - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_binary_internal { - ($class:ident :: $f:ident, $arg1_type:ty, $res_type:ty, $conv:expr) => {{ - unsafe extern "C" fn wrap_binary( - slf: *mut $crate::_detail::ffi::PyObject, - arg1: $arg1_type) - -> $res_type { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $conv, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - Ok(slf.$f(py, arg1)) - }) - } - Some(wrap_binary) - }} -} - - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_ternary_internal { - ($class:ident :: $f:ident, $arg1_type:ty, $arg2_type:ty, $res_type:ty, $conv:expr) => {{ - unsafe extern "C" fn wrap( - slf: *mut $crate::_detail::ffi::PyObject, - arg1: $arg1_type, - arg2: $arg2_type) - -> $res_type - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $conv, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - Ok(slf.$f(py, arg1, arg2)) - }) - } - Some(wrap) - }} -} - - -pub fn extract_op(py: Python, op: c_int) -> PyResult { - match op { - ffi::Py_LT => Ok(CompareOp::Lt), - ffi::Py_LE => Ok(CompareOp::Le), - ffi::Py_EQ => Ok(CompareOp::Eq), - ffi::Py_NE => Ok(CompareOp::Ne), - ffi::Py_GT => Ok(CompareOp::Gt), - ffi::Py_GE => Ok(CompareOp::Ge), - _ => Err(PyErr::new_lazy_init( - py.get_type::(), - Some("tp_richcompare called with invalid comparison operator".to_py_object(py).into_object()))) - } -} - -// sq_richcompare is special-cased slot -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_richcompare_slot { - ($class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{ - unsafe extern "C" fn tp_richcompare( - slf: *mut $crate::_detail::ffi::PyObject, - arg: *mut $crate::_detail::ffi::PyObject, - op: $crate::_detail::libc::c_int) - -> $res_type - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $conv, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let arg = $crate::PyObject::from_borrowed_ptr(py, arg); - let ret = match $crate::py_class::slots::extract_op(py, op) { - Ok(op) => match <$arg_type as $crate::FromPyObject>::extract(py, &arg) { - Ok(arg) => slf.$f(py, arg, op).map(|res| { res.into_py_object(py).into_object() }), - Err(_) => Ok(py.NotImplemented()) - }, - Err(_) => Ok(py.NotImplemented()) - }; - $crate::PyDrop::release_ref(arg, py); - $crate::PyDrop::release_ref(slf, py); - ret - }) - } - Some(tp_richcompare) - }} -} - -// sq_contains is special-cased slot because it converts type errors to Ok(false) -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_contains_slot { - ($class:ident :: $f:ident, $arg_type:ty) => {{ - unsafe extern "C" fn sq_contains( - slf: *mut $crate::_detail::ffi::PyObject, - arg: *mut $crate::_detail::ffi::PyObject) - -> $crate::_detail::libc::c_int - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::py_class::slots::BoolConverter, - |py| { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let arg = $crate::PyObject::from_borrowed_ptr(py, arg); - let ret = match <$arg_type as $crate::FromPyObject>::extract(py, &arg) { - Ok(arg) => slf.$f(py, arg), - Err(e) => $crate::py_class::slots::type_error_to_false(py, e) - }; - $crate::PyDrop::release_ref(arg, py); - $crate::PyDrop::release_ref(slf, py); - ret - }) - } - Some(sq_contains) - }} -} - -pub fn type_error_to_false(py: Python, e: PyErr) -> PyResult { - if e.matches(py, py.get_type::()) { - Ok(false) - } else { - Err(e) - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_binary_numeric_slot { - ($class:ident :: $f:ident) => {{ - unsafe extern "C" fn binary_numeric( - lhs: *mut $crate::_detail::ffi::PyObject, - rhs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::_detail::PyObjectCallbackConverter, - |py| { - let lhs = $crate::PyObject::from_borrowed_ptr(py, lhs); - let rhs = $crate::PyObject::from_borrowed_ptr(py, rhs); - let ret = $class::$f(py, &lhs, &rhs); - $crate::PyDrop::release_ref(lhs, py); - $crate::PyDrop::release_ref(rhs, py); - ret - }) - } - Some(binary_numeric) - }} -} - - -pub struct VoidCallbackConverter; - -impl CallbackConverter<()> for VoidCallbackConverter { - type R = (); - - #[inline] - fn convert(_: (), _: Python) -> () { - () - } - - #[inline] - fn error_value() -> () { - () - } -} - - -pub struct UnitCallbackConverter; - -impl CallbackConverter<()> for UnitCallbackConverter { - type R = c_int; - - #[inline] - fn convert(_: (), _: Python) -> c_int { - 0 - } - - #[inline] - fn error_value() -> c_int { - -1 - } -} - -pub struct LenResultConverter; - -impl CallbackConverter for LenResultConverter { - type R = isize; - - fn convert(val: usize, py: Python) -> isize { - if val <= (isize::MAX as usize) { - val as isize - } else { - PyErr::new_lazy_init(py.get_type::(), None).restore(py); - -1 - } - } - - #[inline] - fn error_value() -> isize { - -1 - } -} - -pub struct IterNextResultConverter; - -impl CallbackConverter> - for IterNextResultConverter - where T: ToPyObject -{ - type R = *mut ffi::PyObject; - - fn convert(val: Option, py: Python) -> *mut ffi::PyObject { - match val { - Some(val) => val.into_py_object(py).into_object().steal_ptr(), - None => unsafe { - ffi::PyErr_SetNone(ffi::PyExc_StopIteration); - ptr::null_mut() - } - } - } - - #[inline] - fn error_value() -> *mut ffi::PyObject { - ptr::null_mut() - } -} - -pub trait WrappingCastTo { - fn wrapping_cast(self) -> T; -} - -macro_rules! wrapping_cast { - ($from:ty, $to:ty) => { - impl WrappingCastTo<$to> for $from { - #[inline] - fn wrapping_cast(self) -> $to { - self as $to - } - } - } -} -wrapping_cast!(u8, Py_hash_t); -wrapping_cast!(u16, Py_hash_t); -wrapping_cast!(u32, Py_hash_t); -wrapping_cast!(usize, Py_hash_t); -wrapping_cast!(u64, Py_hash_t); -wrapping_cast!(i8, Py_hash_t); -wrapping_cast!(i16, Py_hash_t); -wrapping_cast!(i32, Py_hash_t); -wrapping_cast!(isize, Py_hash_t); -wrapping_cast!(i64, Py_hash_t); - -pub struct HashConverter; - -impl CallbackConverter for HashConverter - where T: WrappingCastTo -{ - type R = Py_hash_t; - - #[inline] - fn convert(val: T, _py: Python) -> Py_hash_t { - let hash = val.wrapping_cast(); - if hash == -1 { - -2 - } else { - hash - } - } - - #[inline] - fn error_value() -> Py_hash_t { - -1 - } -} - -pub struct BoolConverter; - -impl CallbackConverter for BoolConverter { - type R = c_int; - - #[inline] - fn convert(val: bool, _py: Python) -> c_int { - val as c_int - } - - #[inline] - fn error_value() -> c_int { - -1 - } -} - - -pub struct SuccessConverter; - -impl CallbackConverter for SuccessConverter { - type R = c_int; - - #[inline] - fn convert(val: bool, _py: Python) -> c_int { - if val { 0 } else { -1 } - } - - #[inline] - fn error_value() -> c_int { - -1 - } -} - - -#[macro_export] -#[doc(hidden)] -macro_rules! py_class_call_slot { - ($class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{ - unsafe extern "C" fn wrap_call( - slf: *mut $crate::_detail::ffi::PyObject, - args: *mut $crate::_detail::ffi::PyObject, - kwargs: *mut $crate::_detail::ffi::PyObject) - -> *mut $crate::_detail::ffi::PyObject - { - const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); - $crate::_detail::handle_callback( - LOCATION, $crate::_detail::PyObjectCallbackConverter, - |py| { - py_argparse_raw!(py, Some(LOCATION), args, kwargs, - [ $( { $pname : $ptype = $detail } )* ] - { - let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>(); - let ret = slf.$f(py $(, $pname )* ); - $crate::PyDrop::release_ref(slf, py); - ret - }) - }) - } - Some(wrap_call) - }} -} - -/// Used as implementation in the `sq_item` slot to forward calls to the `mp_subscript` slot. -pub unsafe extern "C" fn sq_item(obj: *mut ffi::PyObject, index: ffi::Py_ssize_t) -> *mut ffi::PyObject { - let arg = ffi::PyLong_FromSsize_t(index); - if arg.is_null() { - return arg; - } - let ret = ffi::PyObject_GetItem(obj, arg); - ffi::Py_DECREF(arg); - ret -} -