Skip to content

Commit 5f9f231

Browse files
committed
o fix builtin inplace methods to work as MethodType.BINARY
o add missing inplace functions to operator module o fix CPython specific usage of __iadd__ in lists_tests to operator.__iadd__ for #1873148
1 parent b7007b1 commit 5f9f231

5 files changed

Lines changed: 168 additions & 25 deletions

File tree

Lib/test/list_tests.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,13 @@ def test_iadd(self):
450450
u += "eggs"
451451
self.assertEqual(u, self.type2test("spameggs"))
452452

453-
self.assertRaises(TypeError, u.__iadd__, None)
453+
# CPython specific; Jython/pypy test via the operator module
454+
# instead
455+
if not test_support.is_jython:
456+
self.assertRaises(TypeError, u.__iadd__, None)
457+
else:
458+
import operator
459+
self.assertRaises(TypeError, operator.__iadd__, u, None)
454460

455461
def test_imul(self):
456462
u = self.type2test([0, 1])

Lib/test/test_descr_jy.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,90 @@ def __rmul__(self, o):
175175
self.assertEqual(func(C()), cresult)
176176

177177

178+
class InPlaceTestCase(unittest.TestCase):
179+
180+
def test_iadd(self):
181+
class Foo(object):
182+
def __add__(self, other):
183+
return 1
184+
def __radd__(self, other):
185+
return 2
186+
class Bar(object):
187+
pass
188+
class Baz(object):
189+
def __iadd__(self, other):
190+
return NotImplemented
191+
foo = Foo()
192+
foo += Bar()
193+
self.assertEqual(foo, 1)
194+
bar = Bar()
195+
bar += Foo()
196+
self.assertEqual(bar, 2)
197+
baz = Baz()
198+
baz += Foo()
199+
self.assertEqual(baz, 2)
200+
201+
def test_imul(self):
202+
class FooInplace(list):
203+
def __imul__(self, other):
204+
return [1]
205+
class Bar(FooInplace):
206+
def __mul__(self, other):
207+
return [2]
208+
foo = FooInplace()
209+
foo *= 3
210+
self.assertEqual(foo, [1])
211+
foo = Bar([3])
212+
foo *= 3
213+
self.assertEqual(foo, [1])
214+
215+
class Baz(FooInplace):
216+
def __mul__(self, other):
217+
return [3]
218+
baz = Baz()
219+
baz *= 3
220+
self.assertEqual(baz, [1])
221+
222+
def test_list(self):
223+
class Foo(list):
224+
def __mul__(self, other):
225+
return [1]
226+
foo = Foo([2])
227+
foo *= 3
228+
if test_support.is_jython:
229+
self.assertEqual(foo, [2, 2, 2])
230+
else:
231+
# CPython ignores list.__imul__ on a subclass with __mul__
232+
# (unlike Jython and PyPy)
233+
self.assertEqual(foo, [1])
234+
235+
class Bar(object):
236+
def __radd__(self, other):
237+
return 1
238+
def __rmul__(self, other):
239+
return 2
240+
l = []
241+
l += Bar()
242+
self.assertEqual(l, 1)
243+
l = []
244+
l *= Bar()
245+
self.assertEqual(l, 2)
246+
247+
def test_iand(self):
248+
# Jython's set __iand__ (as well as isub, ixor, etc) was
249+
# previously broken
250+
class Foo(set):
251+
def __and__(self, other):
252+
return set([1])
253+
foo = Foo()
254+
foo &= 3
255+
self.assertEqual(foo, set([1]))
256+
257+
178258
def test_main():
179259
test_support.run_unittest(TestDescrTestCase,
180-
SubclassDescrTestCase)
260+
SubclassDescrTestCase,
261+
InPlaceTestCase)
181262

182263
if __name__ == '__main__':
183264
test_main()

src/org/python/core/PyList.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -250,21 +250,10 @@ final PyObject list___ge__(PyObject o) {
250250
}
251251

252252
public PyObject __imul__(PyObject o) {
253-
PyObject result = list___imul__(o);
254-
if(result == null) {
255-
// We can't perform an in-place multiplication on o's
256-
// type, so let o try to rmul this list. A new list will
257-
// be created instead of modifying this one, but that's
258-
// preferable to just blowing up on this operation.
259-
result = o.__rmul__(this);
260-
if(result == null) {
261-
throw Py.TypeError(_unsupportedop("*", o));
262-
}
263-
}
264-
return result;
253+
return list___imul__(o);
265254
}
266255

267-
@ExposedMethod
256+
@ExposedMethod(type = MethodType.BINARY)
268257
final PyObject list___imul__(PyObject o) {
269258
if(!(o instanceof PyInteger || o instanceof PyLong)) {
270259
return null;
@@ -664,10 +653,24 @@ public PyObject __iadd__(PyObject o) {
664653
return list___iadd__(o);
665654
}
666655

667-
@ExposedMethod
656+
@ExposedMethod(type = MethodType.BINARY)
668657
final PyObject list___iadd__(PyObject o) {
669-
extend(fastSequence(o, "argument to += must be a sequence"));
658+
PyType oType = o.getType();
659+
if (oType == TYPE || oType == PyTuple.TYPE || this == o) {
660+
extend(fastSequence(o, "argument must be iterable"));
661+
return this;
662+
}
670663

664+
PyObject it;
665+
try {
666+
it = o.__iter__();
667+
} catch (PyException pye) {
668+
if (!Py.matchException(pye, Py.TypeError)) {
669+
throw pye;
670+
}
671+
return null;
672+
}
673+
extend(it);
671674
return this;
672675
}
673676

src/org/python/core/PySet.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,12 @@ public PyObject __ior__(PyObject other) {
155155
return set___ior__(other);
156156
}
157157

158-
@ExposedMethod
158+
@ExposedMethod(type = MethodType.BINARY)
159159
final PyObject set___ior__(PyObject other) {
160-
BaseSet bs = _binary_sanity_check(other);
161-
_set.addAll(bs._set);
160+
if (!(other instanceof BaseSet)) {
161+
return null;
162+
}
163+
_set.addAll(((BaseSet)other)._set);
162164
return this;
163165
}
164166

@@ -168,7 +170,9 @@ public PyObject __ixor__(PyObject other) {
168170

169171
@ExposedMethod(type = MethodType.BINARY)
170172
final PyObject set___ixor__(PyObject other) {
171-
_binary_sanity_check(other);
173+
if (!(other instanceof BaseSet)) {
174+
return null;
175+
}
172176
set_symmetric_difference_update(other);
173177
return this;
174178
}
@@ -179,8 +183,10 @@ public PyObject __iand__(PyObject other) {
179183

180184
@ExposedMethod(type = MethodType.BINARY)
181185
final PyObject set___iand__(PyObject other) {
182-
BaseSet bs = _binary_sanity_check(other);
183-
_set = ((BaseSet)__and__(bs))._set;
186+
if (!(other instanceof BaseSet)) {
187+
return null;
188+
}
189+
_set = ((BaseSet)__and__(other))._set;
184190
return this;
185191
}
186192

@@ -190,8 +196,10 @@ public PyObject __isub__(PyObject other) {
190196

191197
@ExposedMethod(type = MethodType.BINARY)
192198
final PyObject set___isub__(PyObject other) {
193-
BaseSet bs = _binary_sanity_check(other);
194-
_set.removeAll(bs._set);
199+
if (!(other instanceof BaseSet)) {
200+
return null;
201+
}
202+
_set.removeAll(((BaseSet)other)._set);
195203
return this;
196204
}
197205

src/org/python/modules/operator.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ public PyObject __call__(PyObject arg1, PyObject arg2) {
6464
case 36: return arg1._pow(arg2);
6565
case 37: return arg1._is(arg2);
6666
case 38: return arg1._isnot(arg2);
67+
case 39: return arg1._iadd(arg2);
68+
case 40: return arg1._iand(arg2);
69+
case 41: return arg1._idiv(arg2);
70+
case 42: return arg1._ifloordiv(arg2);
71+
case 43: return arg1._ilshift(arg2);
72+
case 44: return arg1._imod(arg2);
73+
case 45: return arg1._imul(arg2);
74+
case 46: return arg1._ior(arg2);
75+
case 47: return arg1._ipow(arg2);
76+
case 48: return arg1._irshift(arg2);
77+
case 49: return arg1._isub(arg2);
78+
case 50: return arg1._itruediv(arg2);
79+
case 51: return arg1._ixor(arg2);
6780
default:
6881
throw info.unexpectedCall(2, false);
6982
}
@@ -209,6 +222,38 @@ public static void classDictInit(PyObject dict) throws PyIgnoreMethodTag {
209222
dict.__setitem__("__pow__", new OperatorFunctions("pow", 36, 2));
210223
dict.__setitem__("is_", new OperatorFunctions("is_", 37, 2));
211224
dict.__setitem__("is_not", new OperatorFunctions("is_not", 38, 2));
225+
226+
dict.__setitem__("__iadd__", new OperatorFunctions("__iadd__", 39, 2));
227+
dict.__setitem__("iadd", new OperatorFunctions("iadd", 39, 2));
228+
dict.__setitem__("__iconcat__", new OperatorFunctions("__iconcat__", 39, 2));
229+
dict.__setitem__("iconcat", new OperatorFunctions("iconcat", 39, 2));
230+
dict.__setitem__("__iand__", new OperatorFunctions("__iand__", 40, 2));
231+
dict.__setitem__("iand", new OperatorFunctions("iand", 40, 2));
232+
dict.__setitem__("__idiv__", new OperatorFunctions("__idiv__", 41, 2));
233+
dict.__setitem__("idiv", new OperatorFunctions("idiv", 41, 2));
234+
dict.__setitem__("__ifloordiv__", new OperatorFunctions("__ifloordiv__", 42, 2));
235+
dict.__setitem__("ifloordiv", new OperatorFunctions("ifloordiv", 42, 2));
236+
dict.__setitem__("__ilshift__", new OperatorFunctions("__ilshift__", 43, 2));
237+
dict.__setitem__("ilshift", new OperatorFunctions("ilshift", 43, 2));
238+
dict.__setitem__("__imod__", new OperatorFunctions("__imod__", 44, 2));
239+
dict.__setitem__("imod", new OperatorFunctions("imod", 44, 2));
240+
dict.__setitem__("__imul__", new OperatorFunctions("__imul__", 45, 2));
241+
dict.__setitem__("imul", new OperatorFunctions("imul", 45, 2));
242+
dict.__setitem__("__irepeat__", new OperatorFunctions("__irepeat__", 45, 2));
243+
dict.__setitem__("irepeat", new OperatorFunctions("irepeat", 45, 2));
244+
dict.__setitem__("__ior__", new OperatorFunctions("__ior__", 46, 2));
245+
dict.__setitem__("ior", new OperatorFunctions("ior", 46, 2));
246+
dict.__setitem__("__ipow__", new OperatorFunctions("__ipow__", 47, 2));
247+
dict.__setitem__("ipow", new OperatorFunctions("ipow", 47, 2));
248+
dict.__setitem__("__irshift__", new OperatorFunctions("__irshift__", 48, 2));
249+
dict.__setitem__("irshift", new OperatorFunctions("irshift", 48, 2));
250+
dict.__setitem__("__isub__", new OperatorFunctions("__isub__", 49, 2));
251+
dict.__setitem__("isub", new OperatorFunctions("isub", 49, 2));
252+
dict.__setitem__("__itruediv__", new OperatorFunctions("__itruediv__", 50, 2));
253+
dict.__setitem__("itruediv", new OperatorFunctions("itruediv", 50, 2));
254+
dict.__setitem__("__ixor__", new OperatorFunctions("__ixor__", 51, 2));
255+
dict.__setitem__("ixor", new OperatorFunctions("ixor", 51, 2));
256+
212257
dict.__setitem__("attrgetter", PyAttrGetter.TYPE);
213258
dict.__setitem__("itemgetter", PyItemGetter.TYPE);
214259
}

0 commit comments

Comments
 (0)