import sys, os
PY2 = (os.getenv('PY') == '2')
header = '''
// Copyright (c) 2016-2021 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, $init_expr:expr, $init_ty:ty } )* ]
}
$slots:tt { $( $imp:item )* } $members:tt $props:tt
} => {
$crate::py_coerce_item! {
$($class_visibility)* struct $class { _unsafe_inner: $crate::PyObject }
}
$crate::py_impl_to_py_object_for_python_object!($class);
$crate::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) -> $crate::_detail::Result<$class, $crate::PythonObjectDowncastError<'p>> {
if py.get_type::<$class>().is_instance(py, &obj) {
Ok($class { _unsafe_inner: obj })
} else {
Err($crate::PythonObjectDowncastError::new(
py,
stringify!($class),
obj.get_type(py),
))
}
}
#[inline]
fn downcast_borrow_from<'a, 'p>(py: $crate::Python<'p>, obj: &'a $crate::PyObject) -> $crate::_detail::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::new(
py,
stringify!($class),
obj.get_type(py),
))
}
}
}
$crate::py_coerce_item! {
impl $crate::py_class::BaseObject for $class {
type InitType = ( $( $init_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 = <$base_type as $crate::py_class::BaseObject>::alloc(py, ty, ())?;
$( $crate::py_class::data_init::<$data_ty>(py, &obj, $data_offset, $init_expr); )*
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)*
$crate::py_coerce_item! {
impl $class {
$($class_visibility)* fn create_instance(py: $crate::Python $( , $data_name : $init_ty )* ) -> $crate::PyResult<$class> {
let obj = 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
= $crate::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> {
$crate::py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots $props);
$crate::py_class_init_members!($class, $py, TYPE_OBJECT, $members);
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))
}
}
}
}
}
}
};
'''
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),
('nb', 'as_number', None),
('sq', 'as_sequence', None),
('mp', 'as_mapping', 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, new_props=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')
if new_props:
write('\n{ [ $( $prop_doc:tt $prop_getter_name:ident: $prop_type:ty, )* ]')
write('\n[ $( $prop_setter_name:ident : $prop_setter_value_type:tt => $prop_setter_setter:ident, )* ] }')
else:
write('$props:tt')
write('\n} => { $crate::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')
if new_props:
getters, setters = new_props
write('\n/* props: */ {\n')
write('[ $( $prop_doc $prop_getter_name: $prop_type, )*\n')
for doc, name, ty in getters:
write('{ %s } %s: %s,\n' % (doc, name, ty))
write(']\n[ $( $prop_setter_name : $prop_setter_value_type => $prop_setter_setter, )*\n')
for name, ty, setter in setters:
write('%s : [ %s ] => %s,\n' % (name, ty, setter))
write(']\n}\n')
else:
write('$props')
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,
/* init_expr: */ $data_name,
/* init_ty: */ $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 shared_data_decl():
generate_case('@shared data $data_name:ident : $data_type:ty;',
new_info = '''
/* info: */ {
$base_type,
/* size: */ $crate::py_class::data_new_size::<$crate::PySharedRefCell<$data_type>>($size),
$class_visibility,
$gc,
/* data: */ [
$($data)*
{
$crate::py_class::data_offset::<$crate::PySharedRefCell<$data_type>>($size),
$data_name,
/* data_ty: */ $crate::PySharedRefCell<$data_type>,
/* init_expr: */ $crate::PySharedRefCell::<$data_type>::new($data_name),
/* init_ty: */ $data_type
}
]
}
''',
new_impl='''
impl $class {
fn $data_name<'a>(&'a self, py: $crate::Python<'a>) -> $crate::PySharedRef<'a, $data_type> {
unsafe {
let data = $crate::py_class::data_get::<$crate::PySharedRefCell<$data_type>>(
py,
&self._unsafe_inner,
$crate::py_class::data_offset::<$crate::PySharedRefCell<$data_type>>($size)
);
$crate::PySharedRef::new(py, &self._unsafe_inner, data)
}
}
}
''')
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, with_docs, with_visibility):
if with_docs:
doc_prefix = '$(#[doc=$doc:expr])*'
value_suffix = ', { concat!($($doc, "\\n"),*) }'
else:
doc_prefix = value_suffix = ''
if with_visibility:
visibility_capture = '$visibility:vis'
visibility_expansion = '$visibility'
else:
visibility_capture = ''
visibility_expansion = 'pub'
if with_params:
param_pattern = ', $($p:tt)+'
impl = '''$crate::py_argparse_parse_plist_impl!{
py_class_impl_item { $class, $py, %s, %s($cls: &$crate::PyType,) $res_type; { $($body)* } }
[] ($($p)+,)
}''' % (visibility_expansion, name_use)
value = '$crate::py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \
% (value_macro, value_args + value_suffix)
else:
param_pattern = ''
impl = '$crate::py_class_impl_item! { $class, $py, %s, %s($cls: &$crate::PyType,) $res_type; { $($body)* } [] }' \
% (visibility_expansion,name_use)
value = '$crate::%s!{%s []}' % (value_macro, value_args + value_suffix)
pattern = '%s %s def %s ($cls:ident%s) -> $res_type:ty { $( $body:tt )* }' \
% (doc_prefix + decoration, visibility_capture, 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)
with_docs = (value_macro == 'py_class_class_method')
for with_params in (False, True):
for with_visibility in (False, True):
impl(with_params, with_docs, with_visibility)
def traverse_and_clear():
generate_case('def __traverse__(&$slf:tt, $visit:ident) {$($body:tt)*}',
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='''
$crate::py_coerce_item!{
impl $class {
fn __traverse__(&$slf,
$py: $crate::Python,
$visit: $crate::py_class::gc::VisitProc)
-> $crate::_detail::Result<(), $crate::py_class::gc::TraverseError> {
let _ = $py;
$($body)*
}
}
}
''')
generate_case('def __clear__ (&$slf:ident) {$($body:tt)*}',
new_slots=[('tp_clear', '$crate::py_class_tp_clear!($class)')],
new_impl='''
$crate::py_coerce_item!{
impl $class {
fn __clear__(&$slf, $py: $crate::Python) {
let _ = $py;
$($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, with_docs, with_visibility):
if with_docs:
doc_prefix = '$(#[doc=$doc:expr])*'
value_suffix = ', { concat!($($doc, "\\n"),*) }'
else:
doc_prefix = value_suffix = ''
if with_visibility:
visibility_capture = '$visibility:vis'
visibility_expansion = '$visibility'
else:
visibility_capture = ''
visibility_expansion = 'pub'
if with_params:
param_pattern = ', $($p:tt)+'
impl = '''$crate::py_argparse_parse_plist_impl!{
py_class_impl_item { $class, $py, %s, %s(&$slf,) $res_type; { $($body)* } }
[] ($($p)+,)
}''' % (visibility_expansion, name_use)
value = '$crate::py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \
% (value_macro, value_args + value_suffix)
else:
param_pattern = ''
impl = '$crate::py_class_impl_item! { $class, $py, %s, %s(&$slf,) $res_type; { $($body)* } [] }' \
% (visibility_expansion, name_use)
value = '$crate::%s!{%s []}' % (value_macro, value_args + value_suffix)
pattern = '%s %s def %s (&$slf:ident%s) -> $res_type:ty { $( $body:tt )* }' \
% (doc_prefix + decoration, visibility_capture, 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)
with_docs = (value_macro == 'py_class_instance_method')
for with_params in (False, True):
for with_visibility in (False, True):
impl(with_params, with_docs, with_visibility)
def static_method():
def impl(with_visibility):
if with_visibility:
visibility_capture = '$visibility:vis'
visibility_expansion = '$visibility'
else:
visibility_capture = ''
visibility_expansion = 'pub'
generate_case(
'$(#[doc=$doc:expr])* @staticmethod %s def $name:ident ($($p:tt)*) -> $res_type:ty { $( $body:tt )* }' % (visibility_capture,),
new_impl='''
$crate::py_argparse_parse_plist!{
py_class_impl_item { $class, $py, %s, $name() $res_type; { $($body)* } }
($($p)*)
}
''' % (visibility_expansion,),
new_members=[('$name', '''
$crate::py_argparse_parse_plist!{
py_class_static_method {$py, $class::$name, {
concat!($($doc, "\\n"),*)
} }
($($p)*)
}
''')])
for with_visibility in (False, True):
impl(with_visibility)
def static_data():
generate_case('static $name:ident = $init:expr;',
new_members=[('$name', '$init')])
def property_method():
def impl(with_visibility):
if with_visibility:
visibility_capture = '$visibility:vis'
visibility_expansion = '$visibility'
else:
visibility_capture = ''
visibility_expansion = 'pub'
generate_case('$(#[doc=$doc:expr])* @property %s def $name:ident(&$slf:ident) -> $res_type:ty { $( $body:tt )* }' % (visibility_capture,),
new_impl='$crate::py_class_impl_item! { $class, $py, %s, $name(&$slf,) $res_type; { $($body)* } [] }' % (visibility_expansion,),
new_props=([('concat!($($doc, "\\n"),*)', '$name', '$res_type')], [])
)
generate_case('@$name:ident.setter %s def $setter_name:ident(&$slf:ident, $value:ident : Option<Option<&$value_type:ty>> ) -> $res_type:ty { $( $body:tt )* }' % (visibility_capture,),
new_impl='$crate::py_class_impl_item! { $class, $py, %s, $setter_name(&$slf,) $res_type; { $($body)* } [{ $value: Option<Option<&$value_type>> = {} }] }' % (visibility_expansion,),
new_props=([], [('$name', 'Option<&$value_type>', '$setter_name')])
)
generate_case('@$name:ident.setter %s def $setter_name:ident(&$slf:ident, $value:ident : Option<&$value_type:ty> ) -> $res_type:ty { $( $body:tt )* }' % (visibility_capture,),
new_impl='$crate::py_class_impl_item! { $class, $py, %s, $setter_name(&$slf,) $res_type; { $($body)* } [{ $value: Option<&$value_type> = {} }] }' % (visibility_expansion,),
new_props=([], [('$name', '&$value_type', '$setter_name')])
)
generate_case('@$name:ident.setter %s def $setter_name:ident(&$slf:ident, $value:ident : Option<$value_type:ty> ) -> $res_type:ty { $( $body:tt )* }' % (visibility_capture,),
new_impl='$crate::py_class_impl_item! { $class, $py, %s, $setter_name(&$slf,) $res_type; { $($body)* } [{ $value: Option<$value_type> = {} }] }' % (visibility_expansion,),
new_props=([], [('$name', '$value_type', '$setter_name')])
)
for with_visibility in (False, True):
impl(with_visibility)
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 )* } => {
$crate::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, allow_ref=True):
self.name = name
self.allow_ref = allow_ref
@special_method
def operator(special_name, slot,
args=(),
res_type='PyObject',
res_conv=None,
res_ffi_type='*mut $crate::_detail::ffi::PyObject',
additional_slots=()
):
def optref(arg, suffix = ""):
if arg.allow_ref:
return "Option<&$%s_name%s>" % (arg.name, suffix)
else:
return "$%s_name%s" % (arg.name, suffix)
def ref(arg, suffix = ""):
if arg.allow_ref:
return "&$%s_name%s" % (arg.name, suffix)
else:
return "$%s_name%s" % (arg.name, suffix)
def normal(arg, suffix = ""):
return "$%s_name%s" % (arg.name, suffix)
if args:
operator_impl(special_name, slot, args, res_type, res_conv, res_ffi_type, additional_slots, optref)
operator_impl(special_name, slot, args, res_type, res_conv, res_ffi_type, additional_slots, ref)
operator_impl(special_name, slot, args, res_type, res_conv, res_ffi_type, additional_slots, normal)
error('Invalid signature for operator %s' % special_name)(special_name)
def operator_impl(special_name, slot, args, res_type, res_conv, res_ffi_type, additional_slots, typefunc):
if res_conv is None:
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 += ', ${0}:ident : {1}'.format(arg.name, typefunc(arg, ":ty"))
param_list.append('{{ ${0} : {1} = {{}} }}'.format(arg.name, typefunc(arg)))
if slot == 'sq_contains':
new_slots = [(slot, '$crate::py_class_contains_slot!($class::%s, [%s])' % (special_name, typefunc(args[0])))]
elif slot == 'tp_richcompare':
new_slots = [(slot, '$crate::py_class_richcompare_slot!($class::%s, [%s], %s, %s)'
% (special_name, typefunc(args[0]), res_ffi_type, res_conv))]
elif len(args) == 0:
new_slots = [(slot, '$crate::py_class_unary_slot!($class::%s, %s, %s)'
% (special_name, res_ffi_type, res_conv))]
elif len(args) == 1:
new_slots = [(slot, '$crate::py_class_binary_slot!($class::%s, [%s], %s, %s)'
% (special_name, typefunc(args[0]), res_ffi_type, res_conv))]
elif len(args) == 2:
new_slots = [(slot, '$crate::py_class_ternary_slot!($class::%s, [%s], %s, %s, %s)'
% (special_name, typefunc(args[0]), typefunc(args[1]), 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='$crate::py_class_impl_item! { $class, $py, pub, %s(&$slf,) $res_type; { $($body)* } [%s] }'
% (special_name, ' '.join(param_list)),
new_slots=new_slots + list(additional_slots)
)
@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 numeric_operator(special_name, arity, slot):
if arity == 'binary':
argnames = ['left', 'right']
elif arity == 'ternary':
argnames = ['left', 'right', 'ex']
else:
error("Invalid arity %s" % arity)
def_args_str = ', '.join("$%s:ident" % name for name in argnames)
impl_args_str = ' '.join("{ $%s : &$crate::PyObject = {} }" % name for name in argnames)
generate_case(
pattern='def %s(%s) -> $res_type:ty { $($body:tt)* }'
% (special_name, def_args_str),
new_impl='$crate::py_class_impl_item! { $class, $py, pub, %s() $res_type; { $($body)* } [ %s ] }'
% (special_name, impl_args_str),
new_slots=[(slot, '$crate::py_class_numeric_slot!(%s $class::%s)' % (arity, special_name))]
)
error('Invalid signature for %s numeric operator %s' % (arity, 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(),
'__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', allow_ref = False)]),
'__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'),
'__getattr__': unimplemented(),
'__getattribute__': unimplemented(),
'__setattr__': unimplemented(),
'__delattr__': unimplemented(),
'__dir__': unimplemented(),
'__get__': unimplemented(),
'__set__': unimplemented(),
'__delete__': unimplemented(),
'__instancecheck__': unimplemented(),
'__subclasscheck__': unimplemented(),
'__call__': call_operator('tp_call'),
'__len__': operator('sq_length',
res_ffi_type='$crate::_detail::ffi::Py_ssize_t',
res_conv='$crate::py_class::slots::LenResultConverter',
additional_slots=[
('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', allow_ref=False)],
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')]),
'__add__': numeric_operator('binary', 'nb_add'),
'__sub__': numeric_operator('binary', 'nb_subtract'),
'__mul__': numeric_operator('binary', 'nb_multiply'),
'__matmul__': unimplemented() if PY2 else numeric_operator('binary', 'nb_matrix_multiply'),
'__div__': unimplemented(),
'__truediv__': numeric_operator('binary', 'nb_true_divide'),
'__floordiv__': numeric_operator('binary', 'nb_floor_divide'),
'__mod__': numeric_operator('binary', 'nb_remainder'),
'__divmod__': numeric_operator('binary', 'nb_divmod'),
'__pow__': numeric_operator('ternary', 'nb_power'),
'__lshift__': numeric_operator('binary', 'nb_lshift'),
'__rshift__': numeric_operator('binary', 'nb_rshift'),
'__and__': numeric_operator('binary', 'nb_and'),
'__xor__': numeric_operator('binary', 'nb_xor'),
'__or__': numeric_operator('binary', 'nb_or'),
'__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(),
'__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'),
'__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__': operator('nb_index'),
'__coerce__': unimplemented(),
'__enter__': normal_method(),
'__exit__': normal_method(),
'__await__': unimplemented(),
'__aiter__': unimplemented(),
'__aenter__': unimplemented(),
'__aexit__': unimplemented(),
}
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('//')
print('// REGENERATE USING THE MAKEFILE IN ROOT OF REPOSITORY: make build')
print(macro_start)
print(base_case)
data_decl()
shared_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()
property_method()
print(macro_end)
if __name__ == '__main__':
main()