py_class!: __add__, __sub__, __mul__, __neg__, __pos__, __abs__, __invert__

This commit is contained in:
Daniel Grunwald 2016-05-08 01:01:18 +02:00
parent 04081bc8de
commit 4535827f8e
7 changed files with 628 additions and 97 deletions

View File

@ -324,6 +324,24 @@ TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `
If extraction of the `item` parameter fails with `TypeError`,
`__contains__` will return `Ok(false)`.
## Arithmetic methods
* `def __add__(lhs, rhs) -> PyResult<impl ToPyObject>`
* `def __sub__(lhs, rhs) -> PyResult<impl ToPyObject>`
* `def __mul__(lhs, rhs) -> PyResult<impl ToPyObject>`
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 right 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())`.
## Other Special Methods
* `def __bool__(&self) -> PyResult<bool>`

View File

@ -505,11 +505,9 @@ def normal_method(special_name):
def special_class_method(special_name, *args, **kwargs):
generate_class_method(special_name=special_name, *args, **kwargs)
Argument = namedtuple('Argument', ['name', 'default_type'])
class Argument(object):
def __init__(self, name, extract_err='passthrough'):
def __init__(self, name):
self.name = name
self.extract_err = 'py_class_extract_error_%s' % extract_err
@special_method
def operator(special_name, slot,
@ -535,12 +533,14 @@ def operator(special_name, slot,
for arg in args:
arg_pattern += ', ${0}:ident : ${0}_type:ty'.format(arg.name)
param_list.append('{{ ${0} : ${0}_type = {{}} }}'.format(arg.name))
if len(args) == 0:
if slot == 'sq_contains':
new_slots = [(slot, 'py_class_contains_slot!($class::%s, $%s_type)' % (special_name, args[0].name))]
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_type, %s, %s, %s)'
% (special_name, args[0].name, args[0].extract_err, res_ffi_type, res_conv))]
new_slots = [(slot, 'py_class_binary_slot!($class::%s, $%s_type, %s, %s)'
% (special_name, args[0].name, res_ffi_type, res_conv))]
elif len(args) == 2:
new_slots = [(slot, 'py_class_ternary_slot!($class::%s, $%s_type, $%s_type, %s, %s)'
% (special_name, args[0].name, args[1].name, res_ffi_type, res_conv))]
@ -564,6 +564,22 @@ def call_operator(special_name, 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_names = {
'__init__': error('__init__ is not supported by py_class!; use __new__ instead.'),
'__new__': special_class_method(
@ -634,14 +650,12 @@ special_names = {
'__next__': operator('tp_iternext',
res_conv='$crate::py_class::slots::IterNextResultConverter'),
'__reversed__': normal_method(),
'__contains__': operator('sq_contains',
args=[Argument('item', extract_err='false')],
res_type='bool'),
'__contains__': operator('sq_contains', args=[Argument('item')]),
# Emulating numeric types
'__add__': unimplemented(),
'__sub__': unimplemented(),
'__mul__': unimplemented(),
'__add__': binary_numeric_operator('nb_add'),
'__sub__': binary_numeric_operator('nb_subtract'),
'__mul__': binary_numeric_operator('nb_multiply'),
'__matmul__': unimplemented(),
'__div__': unimplemented(),
'__truediv__': unimplemented(),
@ -656,21 +670,21 @@ special_names = {
'__or__': unimplemented(),
# Emulating numeric types - reflected
'__radd__': unimplemented(),
'__rsub__': unimplemented(),
'__rmul__': unimplemented(),
'__rmatmul__': unimplemented(),
'__rdiv__': unimplemented(),
'__rtruediv__': unimplemented(),
'__rfloordiv__': unimplemented(),
'__rmod__': unimplemented(),
'__rdivmod__': unimplemented(),
'__rpow__': unimplemented(),
'__rlshift__': unimplemented(),
'__rrshift__': unimplemented(),
'__rand__': unimplemented(),
'__rxor__': unimplemented(),
'__ror__': unimplemented(),
'__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__': unimplemented(),
@ -690,10 +704,10 @@ special_names = {
'__ior__': unimplemented(),
# Unary arithmetic
'__neg__': unimplemented(),
'__pos__': unimplemented(),
'__abs__': unimplemented(),
'__invert__': unimplemented(),
'__neg__': operator('nb_negative'),
'__pos__': operator('nb_positive'),
'__abs__': operator('nb_absolute'),
'__invert__': operator('nb_invert'),
'__complex__': unimplemented(),
'__int__': unimplemented(),
'__long__': unimplemented(),

View File

@ -293,13 +293,65 @@ macro_rules! py_class_impl {
}
$members
}};
{ { def __abs__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt
/* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __abs__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __abs__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__abs__ is not supported by py_class! yet." }
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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* as_number */ [
$( $nb_slot_name : $nb_slot_value, )*
nb_add: py_class_binary_numeric_slot!($class::__add__),
]
$as_sequence $as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __add__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }
}
$members
}};
{ { def __add__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__add__ is not supported by py_class! yet." }
py_error! { "Invalid signature for binary numeric operator __add__" }
};
{ { def __aenter__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -430,7 +482,7 @@ macro_rules! py_class_impl {
$type_slots $as_number
/* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )*
sq_contains: py_class_binary_slot!($class::__contains__, $item_type, py_class_extract_error_false, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter),
sq_contains: py_class_contains_slot!($class::__contains__, $item_type),
]
$as_mapping $setdelitem
}
@ -474,7 +526,7 @@ macro_rules! py_class_impl {
$type_slots $as_number $as_sequence $as_mapping
/* setdelitem */ [
sdi_setitem: $sdi_setitem_slot_value,
sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, py_class_extract_error_passthrough, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) },
sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) },
]
}
/* impl: */ {
@ -556,7 +608,7 @@ macro_rules! py_class_impl {
]
/* as_mapping */ [
$( $mp_slot_name : $mp_slot_value, )*
mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, py_class_extract_error_passthrough, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
]
$setdelitem
}
@ -654,9 +706,35 @@ macro_rules! py_class_impl {
{ { 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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __invert__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __invert__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__invert__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __invert__" }
};
{ { def __ior__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -769,17 +847,69 @@ macro_rules! py_class_impl {
{ { 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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* as_number */ [
$( $nb_slot_name : $nb_slot_value, )*
nb_multiply: py_class_binary_numeric_slot!($class::__mul__),
]
$as_sequence $as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __mul__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }
}
$members
}};
{ { def __mul__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__mul__ is not supported by py_class! yet." }
py_error! { "Invalid signature for binary numeric operator __mul__" }
};
{ { def __ne__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__ne__ is not supported by py_class! yet." }
};
{ { def __neg__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt
/* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __neg__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __neg__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__neg__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __neg__" }
};
{ { def __new__ ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* }
$class:ident $py:ident $info:tt
@ -868,9 +998,35 @@ macro_rules! py_class_impl {
{ { def __or__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__or__ is not supported by py_class! yet." }
};
{ { def __pos__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt
/* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __pos__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __pos__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__pos__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __pos__" }
};
{ { def __pow__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -878,19 +1034,19 @@ macro_rules! py_class_impl {
};
{ { def __radd__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__radd__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __radd__ is not supported by py_class! Use __add__ instead!" }
};
{ { def __rand__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rand__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rand__ is not supported by py_class! Use __and__ instead!" }
};
{ { def __rdiv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rdiv__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rdiv__ is not supported by py_class! Use __div__ instead!" }
};
{ { def __rdivmod__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rdivmod__ is not supported by py_class! yet." }
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
@ -922,27 +1078,27 @@ macro_rules! py_class_impl {
};
{ { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rfloordiv__ is not supported by py_class! Use __floordiv__ instead!" }
};
{ { def __rlshift__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rlshift__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rlshift__ is not supported by py_class! Use __lshift__ instead!" }
};
{ { def __rmatmul__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rmatmul__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rmatmul__ is not supported by py_class! Use __matmul__ instead!" }
};
{ { def __rmod__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rmod__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rmod__ is not supported by py_class! Use __mod__ instead!" }
};
{ { def __rmul__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rmul__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rmul__ is not supported by py_class! Use __mul__ instead!" }
};
{ { def __ror__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__ror__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __ror__ is not supported by py_class! Use __or__ instead!" }
};
{ { def __round__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -950,11 +1106,11 @@ macro_rules! py_class_impl {
};
{ { def __rpow__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rpow__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rpow__ is not supported by py_class! Use __pow__ instead!" }
};
{ { def __rrshift__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rrshift__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rrshift__ is not supported by py_class! Use __rshift__ instead!" }
};
{ { def __rshift__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -962,15 +1118,15 @@ macro_rules! py_class_impl {
};
{ { def __rsub__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rsub__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rsub__ is not supported by py_class! Use __sub__ instead!" }
};
{ { def __rtruediv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rtruediv__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rtruediv__ is not supported by py_class! Use __truediv__ instead!" }
};
{ { def __rxor__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rxor__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rxor__ is not supported by py_class! Use __xor__ instead!" }
};
{ { def __set__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -1039,9 +1195,35 @@ macro_rules! py_class_impl {
{ { 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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* as_number */ [
$( $nb_slot_name : $nb_slot_value, )*
nb_subtract: py_class_binary_numeric_slot!($class::__sub__),
]
$as_sequence $as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __sub__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }
}
$members
}};
{ { def __sub__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__sub__ is not supported by py_class! yet." }
py_error! { "Invalid signature for binary numeric operator __sub__" }
};
{ { def __subclasscheck__ $($tail:tt)* } $( $stuff:tt )* } => {

View File

@ -293,13 +293,65 @@ macro_rules! py_class_impl {
}
$members
}};
{ { def __abs__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt
/* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __abs__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __abs__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__abs__ is not supported by py_class! yet." }
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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* as_number */ [
$( $nb_slot_name : $nb_slot_value, )*
nb_add: py_class_binary_numeric_slot!($class::__add__),
]
$as_sequence $as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __add__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }
}
$members
}};
{ { def __add__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__add__ is not supported by py_class! yet." }
py_error! { "Invalid signature for binary numeric operator __add__" }
};
{ { def __aenter__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -430,7 +482,7 @@ macro_rules! py_class_impl {
$type_slots $as_number
/* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )*
sq_contains: py_class_binary_slot!($class::__contains__, $item_type, py_class_extract_error_false, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter),
sq_contains: py_class_contains_slot!($class::__contains__, $item_type),
]
$as_mapping $setdelitem
}
@ -474,7 +526,7 @@ macro_rules! py_class_impl {
$type_slots $as_number $as_sequence $as_mapping
/* setdelitem */ [
sdi_setitem: $sdi_setitem_slot_value,
sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, py_class_extract_error_passthrough, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) },
sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) },
]
}
/* impl: */ {
@ -556,7 +608,7 @@ macro_rules! py_class_impl {
]
/* as_mapping */ [
$( $mp_slot_name : $mp_slot_value, )*
mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, py_class_extract_error_passthrough, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
]
$setdelitem
}
@ -654,9 +706,35 @@ macro_rules! py_class_impl {
{ { 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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __invert__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __invert__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__invert__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __invert__" }
};
{ { def __ior__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -769,17 +847,69 @@ macro_rules! py_class_impl {
{ { 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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* as_number */ [
$( $nb_slot_name : $nb_slot_value, )*
nb_multiply: py_class_binary_numeric_slot!($class::__mul__),
]
$as_sequence $as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __mul__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }
}
$members
}};
{ { def __mul__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__mul__ is not supported by py_class! yet." }
py_error! { "Invalid signature for binary numeric operator __mul__" }
};
{ { def __ne__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__ne__ is not supported by py_class! yet." }
};
{ { def __neg__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt
/* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __neg__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __neg__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__neg__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __neg__" }
};
{ { def __new__ ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* }
$class:ident $py:ident $info:tt
@ -868,9 +998,35 @@ macro_rules! py_class_impl {
{ { def __or__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__or__ is not supported by py_class! yet." }
};
{ { def __pos__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt
/* as_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* 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 $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __pos__(&$slf,) $res_type; { $($body)* } [] }
}
$members
}};
{ { def __pos__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__pos__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __pos__" }
};
{ { def __pow__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -878,19 +1034,19 @@ macro_rules! py_class_impl {
};
{ { def __radd__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__radd__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __radd__ is not supported by py_class! Use __add__ instead!" }
};
{ { def __rand__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rand__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rand__ is not supported by py_class! Use __and__ instead!" }
};
{ { def __rdiv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rdiv__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rdiv__ is not supported by py_class! Use __div__ instead!" }
};
{ { def __rdivmod__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rdivmod__ is not supported by py_class! yet." }
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
@ -922,27 +1078,27 @@ macro_rules! py_class_impl {
};
{ { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rfloordiv__ is not supported by py_class! Use __floordiv__ instead!" }
};
{ { def __rlshift__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rlshift__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rlshift__ is not supported by py_class! Use __lshift__ instead!" }
};
{ { def __rmatmul__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rmatmul__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rmatmul__ is not supported by py_class! Use __matmul__ instead!" }
};
{ { def __rmod__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rmod__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rmod__ is not supported by py_class! Use __mod__ instead!" }
};
{ { def __rmul__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rmul__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rmul__ is not supported by py_class! Use __mul__ instead!" }
};
{ { def __ror__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__ror__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __ror__ is not supported by py_class! Use __or__ instead!" }
};
{ { def __round__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -950,11 +1106,11 @@ macro_rules! py_class_impl {
};
{ { def __rpow__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rpow__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rpow__ is not supported by py_class! Use __pow__ instead!" }
};
{ { def __rrshift__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rrshift__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rrshift__ is not supported by py_class! Use __rshift__ instead!" }
};
{ { def __rshift__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -962,15 +1118,15 @@ macro_rules! py_class_impl {
};
{ { def __rsub__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rsub__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rsub__ is not supported by py_class! Use __sub__ instead!" }
};
{ { def __rtruediv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rtruediv__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rtruediv__ is not supported by py_class! Use __truediv__ instead!" }
};
{ { def __rxor__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rxor__ is not supported by py_class! yet." }
py_error! { "Reflected numeric operator __rxor__ is not supported by py_class! Use __xor__ instead!" }
};
{ { def __set__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -1039,9 +1195,35 @@ macro_rules! py_class_impl {
{ { 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_number */ [ $( $nb_slot_name:ident : $nb_slot_value:expr, )* ]
$as_sequence:tt $as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots
/* as_number */ [
$( $nb_slot_name : $nb_slot_value, )*
nb_subtract: py_class_binary_numeric_slot!($class::__sub__),
]
$as_sequence $as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __sub__() $res_type; { $($body)* } [ { $left : &$crate::PyObject = {} } { $right : &$crate::PyObject = {} } ] }
}
$members
}};
{ { def __sub__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__sub__ is not supported by py_class! yet." }
py_error! { "Invalid signature for binary numeric operator __sub__" }
};
{ { def __subclasscheck__ $($tail:tt)* } $( $stuff:tt )* } => {

View File

@ -18,11 +18,12 @@
use ffi;
use std::{mem, isize, ptr};
use libc::{c_char, c_int};
use libc::{c_char, c_int, c_long};
use python::{Python, PythonObject};
use conversion::ToPyObject;
use objects::PyObject;
use function::CallbackConverter;
use err::{PyErr};
use err::{PyErr, PyResult};
use exc;
use Py_hash_t;
@ -56,17 +57,24 @@ macro_rules! py_class_type_object_flags {
/* traverse_proc: */ None,
/* traverse_data: */ [ /*name*/ ]
}) => {
$crate::_detail::ffi::Py_TPFLAGS_DEFAULT
$crate::py_class::slots::TPFLAGS_DEFAULT
};
(/* gc: */ {
$traverse_proc: expr,
$traverse_data: tt
}) => {
$crate::_detail::ffi::Py_TPFLAGS_DEFAULT
$crate::py_class::slots::TPFLAGS_DEFAULT
| $crate::_detail::ffi::Py_TPFLAGS_HAVE_GC
};
}
#[cfg(feature="python27-sys")]
pub const TPFLAGS_DEFAULT : c_long = ffi::Py_TPFLAGS_DEFAULT
| ffi::Py_TPFLAGS_CHECKTYPES;
#[cfg(feature="python3-sys")]
pub const TPFLAGS_DEFAULT : c_long = ffi::Py_TPFLAGS_DEFAULT;
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_type_object_dynamic_init {
@ -253,7 +261,7 @@ macro_rules! py_class_unary_slot {
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_binary_slot {
($class:ident :: $f:ident, $arg_type:ty, $extract_err:ident, $res_type:ty, $conv:expr) => {{
($class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{
unsafe extern "C" fn wrap_binary<DUMMY>(
slf: *mut $crate::_detail::ffi::PyObject,
arg: *mut $crate::_detail::ffi::PyObject)
@ -269,10 +277,10 @@ macro_rules! py_class_binary_slot {
Ok(prepared) => {
match <$arg_type as $crate::ExtractPyObject>::extract(py, &prepared) {
Ok(arg) => slf.$f(py, arg),
Err(e) => $extract_err!(py, e)
Err(e) => Err(e)
}
},
Err(e) => $extract_err!(py, e)
Err(e) => Err(e)
};
$crate::PyDrop::release_ref(arg, py);
$crate::PyDrop::release_ref(slf, py);
@ -329,22 +337,79 @@ macro_rules! py_class_ternary_slot {
}}
}
// sq_contains is special-cased slot because it converts type errors to Ok(false)
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_extract_error_passthrough {
($py: ident, $e:ident) => (Err($e));
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::ExtractPyObject>::prepare_extract(py, &arg) {
Ok(prepared) => {
match <$arg_type as $crate::ExtractPyObject>::extract(py, &prepared) {
Ok(arg) => slf.$f(py, arg),
Err(e) => $crate::py_class::slots::type_error_to_false(py, e)
}
},
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<bool> {
if e.matches(py, py.get_type::<exc::TypeError>()) {
Ok(false)
} else {
Err(e)
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_extract_error_false {
($py: ident, $e:ident) => {
if $e.matches($py, $py.get_type::<$crate::exc::TypeError>()) {
Ok(false)
} else {
Err($e)
macro_rules! py_class_binary_numeric_slot {
($class:ident :: $f:ident) => {{
unsafe extern "C" fn binary_numeric<DUMMY>(
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 fn type_error_to_not_implemented(py: Python, e: PyErr) -> PyResult<PyObject> {
if e.matches(py, py.get_type::<exc::TypeError>()) {
Ok(py.NotImplemented())
} else {
Err(e)
}
}
pub struct UnitCallbackConverter;

View File

@ -285,6 +285,13 @@ impl<'p> Python<'p> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_False()).unchecked_cast_into::<PyBool>() }
}
/// Gets the Python builtin value `NotImplemented`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
pub fn NotImplemented(self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) }
}
/// Gets the Python type object for type T.
pub fn get_type<T>(self) -> PyType where T: PythonObjectWithTypeObject {
T::type_object(self)

View File

@ -572,3 +572,66 @@ fn contains() {
py_run!(py, c, "assert 'wrong type' not in c");
}
py_class!(class UnaryArithmetic |py| {
def __neg__(&self) -> PyResult<&'static str> {
Ok("neg")
}
def __pos__(&self) -> PyResult<&'static str> {
Ok("pos")
}
def __abs__(&self) -> PyResult<&'static str> {
Ok("abs")
}
def __invert__(&self) -> PyResult<&'static str> {
Ok("invert")
}
});
#[test]
fn unary_arithmetic() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = UnaryArithmetic::create_instance(py).unwrap();
py_run!(py, c, "assert -c == 'neg'");
py_run!(py, c, "assert +c == 'pos'");
py_run!(py, c, "assert abs(c) == 'abs'");
py_run!(py, c, "assert ~c == 'invert'");
}
py_class!(class BinaryArithmetic |py| {
def __repr__(&self) -> PyResult<&'static str> {
Ok("BA")
}
def __add__(lhs, rhs) -> PyResult<String> {
Ok(format!("{:?} + {:?}", lhs, rhs))
}
def __sub__(lhs, rhs) -> PyResult<String> {
Ok(format!("{:?} - {:?}", lhs, rhs))
}
def __mul__(lhs, rhs) -> PyResult<String> {
Ok(format!("{:?} * {:?}", lhs, rhs))
}
});
#[test]
fn binary_arithmetic() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = BinaryArithmetic::create_instance(py).unwrap();
py_run!(py, c, "assert c + c == 'BA + BA'");
py_run!(py, c, "assert c + 1 == 'BA + 1'");
py_run!(py, c, "assert 1 + c == '1 + BA'");
py_run!(py, c, "assert c - 1 == 'BA - 1'");
py_run!(py, c, "assert 1 - c == '1 - BA'");
py_run!(py, c, "assert c * 1 == 'BA * 1'");
py_run!(py, c, "assert 1 * c == '1 * BA'");
}