|
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. |
2 | 2 | # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
3 | 3 | #
|
4 | 4 | # The Universal Permissive License (UPL), Version 1.0
|
|
36 | 36 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
37 | 37 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
38 | 38 | # SOFTWARE.
|
| 39 | +import operator |
39 | 40 | import sys
|
| 41 | + |
40 | 42 | from . import CPyExtType, CPyExtHeapType, compile_module_from_string, assert_raises, compile_module_from_file
|
41 | 43 |
|
42 | 44 | SlotsGetter = CPyExtType("SlotsGetter",
|
@@ -627,3 +629,278 @@ def test_PyType_Modified_doesnt_change_slots():
|
627 | 629 | assert tester[1] == 'mp_subscript'
|
628 | 630 | assert tester.call_PySequence_GetItem(1) == 'sq_item'
|
629 | 631 |
|
| 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