1cimport cython
2
3import sys
4IS_PYTHON2 = sys.version_info[0] == 2
5
6__doc__ = ""
7
8
9@cython.c_api_binop_methods(False)
10@cython.cclass
11class Base(object):
12    """
13    >>> Base() + 2
14    'Base.__add__(Base(), 2)'
15    >>> 2 + Base()
16    'Base.__radd__(Base(), 2)'
17
18    >>> Base(implemented=False) + 2  #doctest: +ELLIPSIS
19    Traceback (most recent call last):
20    ...
21    TypeError: unsupported operand type...
22    >>> 2 + Base(implemented=False)  #doctest: +ELLIPSIS
23    Traceback (most recent call last):
24    ...
25    TypeError: unsupported operand type...
26
27    >>> Base() ** 2
28    'Base.__pow__(Base(), 2, None)'
29    >>> 2 ** Base()
30    'Base.__rpow__(Base(), 2, None)'
31    >>> pow(Base(), 2, 100)
32    'Base.__pow__(Base(), 2, 100)'
33    """
34    implemented: cython.bint
35
36    def __init__(self, *, implemented=True):
37        self.implemented = implemented
38
39    def __add__(self, other):
40        if (<Base>self).implemented:
41            return "Base.__add__(%s, %s)" % (self, other)
42        else:
43            return NotImplemented
44
45    def __radd__(self, other):
46        if (<Base>self).implemented:
47            return "Base.__radd__(%s, %s)" % (self, other)
48        else:
49            return NotImplemented
50
51    def __pow__(self, other, mod):
52        if (<Base>self).implemented:
53            return "Base.__pow__(%s, %s, %s)" % (self, other, mod)
54        else:
55            return NotImplemented
56
57    def __rpow__(self, other, mod):
58        if (<Base>self).implemented:
59            return "Base.__rpow__(%s, %s, %s)" % (self, other, mod)
60        else:
61            return NotImplemented
62
63    def __repr__(self):
64        return "%s()" % (self.__class__.__name__)
65
66
67@cython.c_api_binop_methods(False)
68@cython.cclass
69class OverloadLeft(Base):
70    """
71    >>> OverloadLeft() + 2
72    'OverloadLeft.__add__(OverloadLeft(), 2)'
73    >>> 2 + OverloadLeft()
74    'Base.__radd__(OverloadLeft(), 2)'
75
76    >>> OverloadLeft() + Base()
77    'OverloadLeft.__add__(OverloadLeft(), Base())'
78    >>> Base() + OverloadLeft()
79    'Base.__add__(Base(), OverloadLeft())'
80
81    >>> OverloadLeft(implemented=False) + Base(implemented=False)  #doctest: +ELLIPSIS
82    Traceback (most recent call last):
83    ...
84    TypeError: unsupported operand type...
85    >>> Base(implemented=False) + OverloadLeft(implemented=False)  #doctest: +ELLIPSIS
86    Traceback (most recent call last):
87    ...
88    TypeError: unsupported operand type...
89    """
90    derived_implemented: cython.bint
91
92    def __init__(self, *, implemented=True):
93        super().__init__(implemented=implemented)
94        self.derived_implemented = implemented
95
96    def __add__(self, other):
97        if (<OverloadLeft>self).derived_implemented:
98            return "OverloadLeft.__add__(%s, %s)" % (self, other)
99        else:
100            return NotImplemented
101
102
103@cython.c_api_binop_methods(False)
104@cython.cclass
105class OverloadRight(Base):
106    """
107    >>> OverloadRight() + 2
108    'Base.__add__(OverloadRight(), 2)'
109    >>> 2 + OverloadRight()
110    'OverloadRight.__radd__(OverloadRight(), 2)'
111
112    >>> OverloadRight() + Base()
113    'Base.__add__(OverloadRight(), Base())'
114    >>> Base() + OverloadRight()
115    'OverloadRight.__radd__(OverloadRight(), Base())'
116
117    >>> OverloadRight(implemented=False) + Base(implemented=False)  #doctest: +ELLIPSIS
118    Traceback (most recent call last):
119    ...
120    TypeError: unsupported operand type...
121    >>> Base(implemented=False) + OverloadRight(implemented=False)  #doctest: +ELLIPSIS
122    Traceback (most recent call last):
123    ...
124    TypeError: unsupported operand type...
125    """
126    derived_implemented: cython.bint
127
128    def __init__(self, *, implemented=True):
129        super().__init__(implemented=implemented)
130        self.derived_implemented = implemented
131
132    def __radd__(self, other):
133        if (<OverloadRight>self).derived_implemented:
134            return "OverloadRight.__radd__(%s, %s)" % (self, other)
135        else:
136            return NotImplemented
137
138
139@cython.c_api_binop_methods(True)
140@cython.cclass
141class OverloadCApi(Base):
142    """
143    >>> OverloadCApi() + 2
144    'OverloadCApi.__add__(OverloadCApi(), 2)'
145    >>> 2 + OverloadCApi()
146    'OverloadCApi.__add__(2, OverloadCApi())'
147
148    >>> OverloadCApi() + Base()
149    'OverloadCApi.__add__(OverloadCApi(), Base())'
150    >>> Base() + OverloadCApi()
151    'OverloadCApi.__add__(Base(), OverloadCApi())'
152
153    >>> OverloadCApi(derived_implemented=False) + 2 #doctest: +ELLIPSIS
154    Traceback (most recent call last):
155    ...
156    TypeError: unsupported operand type...
157    >>> 2 + OverloadCApi(derived_implemented=False) #doctest: +ELLIPSIS
158    Traceback (most recent call last):
159    ...
160    TypeError: unsupported operand type...
161    """
162    derived_implemented: cython.bint
163
164    def __init__(self, *, derived_implemented=True):
165        super().__init__(implemented=True)
166        self.derived_implemented = derived_implemented
167
168    def __add__(self, other):
169        if isinstance(self, OverloadCApi):
170            derived_implemented = (<OverloadCApi>self).derived_implemented
171        else:
172            derived_implemented = (<OverloadCApi>other).derived_implemented
173        if derived_implemented:
174            return "OverloadCApi.__add__(%s, %s)" % (self, other)
175        else:
176            return NotImplemented
177
178
179if sys.version_info >= (3, 5):
180    __doc__ += """
181    >>> d = PyVersionDependent()
182    >>> d @ 2
183    9
184    >>> 2 @ d
185    99
186    >>> i = d
187    >>> i @= 2
188    >>> i
189    999
190"""
191
192
193@cython.c_api_binop_methods(False)
194@cython.cclass
195class PyVersionDependent:
196    """
197    >>> d = PyVersionDependent()
198    >>> d / 2
199    5
200    >>> 2 / d
201    2
202    >>> d // 2
203    55
204    >>> 2 // d
205    22
206    >>> i = d
207    >>> i /= 2
208    >>> i
209    4
210    >>> i = d
211    >>> i //= 2
212    >>> i
213    44
214    """
215    def __div__(self, other):
216        assert IS_PYTHON2
217        return 5
218
219    def __rdiv__(self, other):
220        assert IS_PYTHON2
221        return 2
222
223    def __idiv__(self, other):
224        assert IS_PYTHON2
225        return 4
226
227    def __truediv__(self, other):
228        assert not IS_PYTHON2
229        return 5
230
231    def __rtruediv__(self, other):
232        assert not IS_PYTHON2
233        return 2
234
235    def __itruediv__(self, other):
236        assert not IS_PYTHON2
237        return 4
238
239    def __floordiv__(self, other):
240        return 55
241
242    def __rfloordiv__(self, other):
243        return 22
244
245    def __ifloordiv__(self, other):
246        return 44
247
248    def __matmul__(self, other):
249        return 9
250
251    def __rmatmul__(self, other):
252        return 99
253
254    def __imatmul__(self, other):
255        return 999
256
257
258# TODO: Test a class that only defines the `__r...__()` methods.
259