Skip to content

Commit 4de9b95

Browse files
committed
Add test for calling all nb slots
1 parent cd0c18a commit 4de9b95

File tree

1 file changed

+278
-1
lines changed

1 file changed

+278
-1
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tp_slots.py

Lines changed: 278 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -36,7 +36,9 @@
3636
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
39+
import operator
3940
import sys
41+
4042
from . import CPyExtType, CPyExtHeapType, compile_module_from_string, assert_raises, compile_module_from_file
4143

4244
SlotsGetter = CPyExtType("SlotsGetter",
@@ -627,3 +629,278 @@ def test_PyType_Modified_doesnt_change_slots():
627629
assert tester[1] == 'mp_subscript'
628630
assert tester.call_PySequence_GetItem(1) == 'sq_item'
629631

632+
633+
def test_nb_slot_calls():
634+
slots = [
635+
('proxy_nb_binary_slot', 'nb_add'),
636+
('proxy_nb_binary_slot', 'nb_subtract'),
637+
('proxy_nb_binary_slot', 'nb_multiply'),
638+
('proxy_nb_binary_slot', 'nb_remainder'),
639+
('proxy_nb_binary_slot', 'nb_divmod'),
640+
('proxy_nb_ternary_slot', 'nb_power'),
641+
('proxy_nb_unary_slot', 'nb_negative'),
642+
('proxy_nb_unary_slot', 'nb_positive'),
643+
('proxy_nb_unary_slot', 'nb_absolute'),
644+
('proxy_nb_inquiry_slot', 'nb_bool'),
645+
('proxy_nb_unary_slot', 'nb_invert'),
646+
('proxy_nb_binary_slot', 'nb_lshift'),
647+
('proxy_nb_binary_slot', 'nb_rshift'),
648+
('proxy_nb_binary_slot', 'nb_and'),
649+
('proxy_nb_binary_slot', 'nb_xor'),
650+
('proxy_nb_binary_slot', 'nb_or'),
651+
('proxy_nb_unary_slot', 'nb_int'),
652+
('proxy_nb_unary_slot', 'nb_float'),
653+
('proxy_nb_binary_slot', 'nb_inplace_add'),
654+
('proxy_nb_binary_slot', 'nb_inplace_subtract'),
655+
('proxy_nb_binary_slot', 'nb_inplace_multiply'),
656+
('proxy_nb_binary_slot', 'nb_inplace_remainder'),
657+
('proxy_nb_ternary_slot', 'nb_inplace_power'),
658+
('proxy_nb_binary_slot', 'nb_inplace_lshift'),
659+
('proxy_nb_binary_slot', 'nb_inplace_rshift'),
660+
('proxy_nb_binary_slot', 'nb_inplace_and'),
661+
('proxy_nb_binary_slot', 'nb_inplace_xor'),
662+
('proxy_nb_binary_slot', 'nb_inplace_or'),
663+
('proxy_nb_binary_slot', 'nb_floor_divide'),
664+
('proxy_nb_binary_slot', 'nb_true_divide'),
665+
('proxy_nb_binary_slot', 'nb_inplace_floor_divide'),
666+
('proxy_nb_binary_slot', 'nb_inplace_true_divide'),
667+
('proxy_nb_unary_slot', 'nb_index'),
668+
('proxy_nb_binary_slot', 'nb_matrix_multiply'),
669+
('proxy_nb_binary_slot', 'nb_inplace_matrix_multiply'),
670+
]
671+
NativeSlotProxy = CPyExtType(
672+
name='NativeSlotProxy',
673+
cmembers='PyObject* delegate;',
674+
code=r'''
675+
static PyObject* proxy_tp_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) {
676+
PyObject* delegate;
677+
if (!PyArg_UnpackTuple(args, "NativeSlotProxy", 0, 1, &delegate))
678+
return NULL;
679+
NativeSlotProxyObject* obj = (NativeSlotProxyObject*)type->tp_alloc(type, 0);
680+
if (!obj)
681+
return NULL;
682+
obj->delegate = Py_NewRef(delegate); // leaked
683+
return (PyObject*)obj;
684+
}
685+
static PyTypeObject NativeSlotProxyType;
686+
static void unpack(PyObject** obj) {
687+
if (Py_TYPE(*obj) == &NativeSlotProxyType) {
688+
*obj = ((NativeSlotProxyObject*)*obj)->delegate;
689+
}
690+
}
691+
#define proxy_nb_unary_slot(slot) \
692+
static PyObject* proxy_##slot(PyObject *a) { \
693+
unpack(&a); \
694+
return Py_TYPE(a)->tp_as_number->slot(a); \
695+
}
696+
#define proxy_nb_inquiry_slot(slot) \
697+
static int proxy_##slot(PyObject *a) { \
698+
unpack(&a); \
699+
return Py_TYPE(a)->tp_as_number->slot(a); \
700+
}
701+
#define proxy_nb_binary_slot(slot) \
702+
static PyObject* proxy_##slot(PyObject *a, PyObject *b) { \
703+
if (Py_TYPE(a) == &NativeSlotProxyType) { \
704+
unpack(&a); \
705+
return Py_TYPE(a)->tp_as_number->slot(a, b); \
706+
} else { \
707+
unpack(&b); \
708+
return Py_TYPE(b)->tp_as_number->slot(a, b); \
709+
} \
710+
}
711+
#define proxy_nb_ternary_slot(slot) \
712+
static PyObject* proxy_##slot(PyObject *a, PyObject *b, PyObject* c) { \
713+
if (Py_TYPE(a) == &NativeSlotProxyType) { \
714+
unpack(&a); \
715+
return Py_TYPE(a)->tp_as_number->slot(a, b, c); \
716+
} else if (Py_TYPE(b) == &NativeSlotProxyType) { \
717+
unpack(&b); \
718+
return Py_TYPE(b)->tp_as_number->slot(a, b, c); \
719+
} else { \
720+
unpack(&c); \
721+
return Py_TYPE(c)->tp_as_number->slot(a, b, c); \
722+
} \
723+
}
724+
''' + '\n'.join(f'{macro}({slot})' for macro, slot in slots),
725+
tp_new='proxy_tp_new',
726+
tp_members='{"delegate", T_OBJECT, offsetof(NativeSlotProxyObject, delegate), 0, NULL}',
727+
**{slot: f'proxy_{slot}' for _, slot in slots},
728+
)
729+
730+
def _unary_op(op):
731+
def fn(a):
732+
return op(a.delegate)
733+
734+
return fn
735+
736+
def _binary_op(op):
737+
def fn(a, b):
738+
return op(a.delegate, b)
739+
740+
return fn
741+
742+
def _binary_op_r(op):
743+
def fn(a, b):
744+
return op(b, a.delegate)
745+
746+
return fn
747+
748+
def _binary_op_inplace(op):
749+
def fn(a, b):
750+
a.delegate = op(a.delegate, b)
751+
return a
752+
753+
return fn
754+
755+
class PureSlotProxy:
756+
def __init__(self, delegate):
757+
self.delegate = delegate
758+
759+
__add__ = _binary_op(operator.add)
760+
__radd__ = _binary_op_r(operator.add)
761+
__iadd__ = _binary_op_inplace(operator.add)
762+
__sub__ = _binary_op(operator.sub)
763+
__rsub__ = _binary_op_r(operator.sub)
764+
__isub__ = _binary_op_inplace(operator.sub)
765+
__mul__ = _binary_op(operator.mul)
766+
__rmul__ = _binary_op_r(operator.mul)
767+
__imul__ = _binary_op_inplace(operator.mul)
768+
__mod__ = _binary_op(operator.mod)
769+
__rmod__ = _binary_op_r(operator.mod)
770+
__imod__ = _binary_op_inplace(operator.mod)
771+
__floordiv__ = _binary_op(operator.floordiv)
772+
__rfloordiv__ = _binary_op_r(operator.floordiv)
773+
__ifloordiv__ = _binary_op_inplace(operator.floordiv)
774+
__truediv__ = _binary_op(operator.truediv)
775+
__rtruediv__ = _binary_op_r(operator.truediv)
776+
__itruediv__ = _binary_op_inplace(operator.truediv)
777+
__divmod__ = _binary_op(divmod)
778+
__rdivmod__ = _binary_op_r(divmod)
779+
780+
def __pow__(self, power, modulo=None):
781+
return pow(self.delegate, power, modulo)
782+
783+
def __rpow__(self, other):
784+
return other ** self.delegate
785+
786+
def __ipow__(self, other):
787+
self.delegate = self.delegate ** other
788+
return self
789+
790+
__pos__ = _unary_op(operator.pos)
791+
__neg__ = _unary_op(operator.neg)
792+
__abs__ = _unary_op(abs)
793+
__bool__ = _unary_op(bool)
794+
__invert__ = _unary_op(operator.invert)
795+
__index__ = _unary_op(operator.index)
796+
__int__ = _unary_op(int)
797+
__float__ = _unary_op(float)
798+
__lshift__ = _binary_op(operator.lshift)
799+
__rlshift__ = _binary_op_r(operator.lshift)
800+
__ilshift__ = _binary_op_inplace(operator.lshift)
801+
__rshift__ = _binary_op(operator.rshift)
802+
__rrshift__ = _binary_op_r(operator.rshift)
803+
__irshift__ = _binary_op_inplace(operator.rshift)
804+
__and__ = _binary_op(operator.and_)
805+
__rand__ = _binary_op_r(operator.and_)
806+
__iand__ = _binary_op_inplace(operator.and_)
807+
__or__ = _binary_op(operator.or_)
808+
__ror__ = _binary_op_r(operator.or_)
809+
__ior__ = _binary_op_inplace(operator.or_)
810+
__xor__ = _binary_op(operator.xor)
811+
__rxor__ = _binary_op_r(operator.xor)
812+
__ixor__ = _binary_op_inplace(operator.xor)
813+
__matmul__ = _binary_op(operator.matmul)
814+
__rmatmul__ = _binary_op_r(operator.matmul)
815+
__imatmul__ = _binary_op_inplace(operator.matmul)
816+
817+
class ObjWithMatmul:
818+
def __matmul__(self, other):
819+
return "@"
820+
821+
def __rmatmul__(self, other):
822+
return "@"
823+
824+
for obj in [NativeSlotProxy(3), NativeSlotProxy(PureSlotProxy(3))]:
825+
assert obj + 2 == 5
826+
assert 2 + obj == 5
827+
assert obj - 2 == 1
828+
assert 2 - obj == -1
829+
assert obj * 2 == 6
830+
assert 2 * obj == 6
831+
assert obj % 2 == 1
832+
assert 2 % obj == 2
833+
assert divmod(obj, 2) == (1, 1)
834+
assert divmod(2, obj) == (0, 2)
835+
assert obj ** 2 == 9
836+
assert 2 ** obj == 8
837+
assert pow(obj, 2, 2) == 1
838+
if isinstance(obj.delegate, int): # pow doesn't call __rpow__
839+
assert pow(2, obj, 2) == 0
840+
assert pow(2, 2, obj) == 1
841+
assert -obj == -3
842+
assert +obj == 3
843+
assert abs(obj) == 3
844+
assert bool(obj)
845+
assert ~obj == -4
846+
assert obj << 2 == 12
847+
assert 2 << obj == 16
848+
assert obj >> 2 == 0
849+
assert 2 >> obj == 0
850+
assert obj & 2 == 2
851+
assert 2 & obj == 2
852+
assert obj | 2 == 3
853+
assert 2 | obj == 3
854+
assert obj ^ 2 == 1
855+
assert 2 ^ obj == 1
856+
assert int(obj) == 3
857+
assert float(obj) == 3.0
858+
assert operator.index(obj) == 3
859+
assert obj // 2 == 1
860+
assert 2 // obj == 0
861+
assert obj / 2 == 1.5
862+
assert 2 / obj == 2 / 3
863+
864+
obj = NativeSlotProxy(ObjWithMatmul())
865+
assert obj @ 1 == '@'
866+
assert 1 @ obj == '@'
867+
868+
obj = NativeSlotProxy(PureSlotProxy(3))
869+
obj += 2
870+
assert obj.delegate == 5
871+
obj = NativeSlotProxy(PureSlotProxy(3))
872+
obj -= 2
873+
assert obj.delegate == 1
874+
obj = NativeSlotProxy(PureSlotProxy(3))
875+
obj *= 2
876+
assert obj.delegate == 6
877+
obj = NativeSlotProxy(PureSlotProxy(3))
878+
obj %= 2
879+
assert obj.delegate == 1
880+
obj = NativeSlotProxy(PureSlotProxy(3))
881+
obj **= 2
882+
assert obj.delegate == 9
883+
obj = NativeSlotProxy(PureSlotProxy(3))
884+
obj <<= 2
885+
assert obj.delegate == 12
886+
obj = NativeSlotProxy(PureSlotProxy(3))
887+
obj >>= 2
888+
assert obj.delegate == 0
889+
obj = NativeSlotProxy(PureSlotProxy(3))
890+
obj &= 2
891+
assert obj.delegate == 2
892+
obj = NativeSlotProxy(PureSlotProxy(3))
893+
obj |= 2
894+
assert obj.delegate == 3
895+
obj = NativeSlotProxy(PureSlotProxy(3))
896+
obj ^= 2
897+
assert obj.delegate == 1
898+
obj = NativeSlotProxy(PureSlotProxy(3))
899+
obj //= 2
900+
assert obj.delegate == 1
901+
obj = NativeSlotProxy(PureSlotProxy(3))
902+
obj /= 2
903+
assert obj.delegate == 1.5
904+
obj = NativeSlotProxy(PureSlotProxy(ObjWithMatmul()))
905+
obj @= 1
906+
assert obj.delegate == '@'

0 commit comments

Comments
 (0)