Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r73338:6a380bc4ae37 Date: 2014-09-06 12:19 +0200 http://bitbucket.org/pypy/pypy/changeset/6a380bc4ae37/
Log: issue1861: trying to remove the "unicode + string_subclass" hack and replace it with a more general hack, based on detecting that the left-hand-side argument is a "sequence". This is meant to follow CPython, which uses sq_xxx methods which mess up with the implementation of "+" and "*". diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -671,6 +671,7 @@ left, right = specialnames errormsg = "unsupported operand type(s) for %s: '%%N' and '%%N'" % ( symbol.replace('%', '%%'),) + seq_bug_compat = (symbol == '+' or symbol == '*') def binop_impl(space, w_obj1, w_obj2): w_typ1 = space.type(w_obj1) @@ -686,20 +687,16 @@ # __xxx__ and __rxxx__ methods where found by identity. # Note that space.is_w() is potentially not happy if one of them # is None... - if w_left_src is not w_right_src: # XXX - # -- cpython bug compatibility: see objspace/std/test/ - # -- test_unicodeobject.test_str_unicode_concat_overrides. - # -- The following handles "unicode + string subclass" by - # -- pretending that the unicode is a superclass of the - # -- string, thus giving priority to the string subclass' - # -- __radd__() method. The case "string + unicode subclass" - # -- is handled directly by add__String_Unicode(). - if symbol == '+' and space.is_w(w_typ1, space.w_unicode): - w_typ1 = space.w_basestring - # -- end of bug compatibility - if space.is_true(space.issubtype(w_typ2, w_typ1)): - if (w_left_src and w_right_src and - not space.abstract_issubclass_w(w_left_src, w_right_src) and + if w_right_src and (w_left_src is not w_right_src) and w_left_src: + # 'seq_bug_compat' is for cpython bug-to-bug compatibility: + # see objspace/std/test/test_unicodeobject.*concat_overrides + # and objspace/test/test_descrobject.*rmul_overrides. + # For cases like "unicode + string subclass". + if ((seq_bug_compat and w_typ1.flag_sequence_bug_compat + and not w_typ2.flag_sequence_bug_compat) + # the non-bug-compat part is the following check: + or space.is_true(space.issubtype(w_typ2, w_typ1))): + if (not space.abstract_issubclass_w(w_left_src, w_right_src) and not space.abstract_issubclass_w(w_typ1, w_right_src)): w_obj1, w_obj2 = w_obj2, w_obj1 w_left_impl, w_right_impl = w_right_impl, w_left_impl diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py --- a/pypy/objspace/std/bytearrayobject.py +++ b/pypy/objspace/std/bytearrayobject.py @@ -1131,6 +1131,7 @@ reverse = interp2app(W_BytearrayObject.descr_reverse, doc=BytearrayDocstrings.reverse.__doc__), ) +W_BytearrayObject.typedef.flag_sequence_bug_compat = True init_signature = Signature(['source', 'encoding', 'errors'], None, None) init_defaults = [None, None, None] diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -951,6 +951,7 @@ _formatter_field_name_split = interp2app(W_BytesObject.descr_formatter_field_name_split), ) +W_BytesObject.typedef.flag_sequence_bug_compat = True def string_escape_encode(s, quote): diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -1874,3 +1874,4 @@ insert = interp2app(W_ListObject.descr_insert), remove = interp2app(W_ListObject.descr_remove), ) +W_ListObject.typedef.flag_sequence_bug_compat = True diff --git a/pypy/objspace/std/stdtypedef.py b/pypy/objspace/std/stdtypedef.py --- a/pypy/objspace/std/stdtypedef.py +++ b/pypy/objspace/std/stdtypedef.py @@ -93,6 +93,8 @@ overridetypedef=overridetypedef) if typedef is not overridetypedef: w_type.w_doc = space.wrap(typedef.doc) + if hasattr(typedef, 'flag_sequence_bug_compat'): + w_type.flag_sequence_bug_compat = typedef.flag_sequence_bug_compat w_type.lazyloaders = lazyloaders return w_type diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py --- a/pypy/objspace/std/tupleobject.py +++ b/pypy/objspace/std/tupleobject.py @@ -244,6 +244,7 @@ count = interp2app(W_AbstractTupleObject.descr_count), index = interp2app(W_AbstractTupleObject.descr_index) ) +W_AbstractTupleObject.typedef.flag_sequence_bug_compat = True class W_TupleObject(W_AbstractTupleObject): diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -67,6 +67,7 @@ _immutable_fields_ = ["flag_heaptype", "flag_cpytype", "flag_abstract?", + "flag_sequence_bug_compat", 'needsdel', 'weakrefable', 'hasdict', @@ -104,6 +105,7 @@ w_self.flag_heaptype = False w_self.flag_cpytype = False w_self.flag_abstract = False + w_self.flag_sequence_bug_compat = False w_self.instancetypedef = overridetypedef if overridetypedef is not None: diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1068,6 +1068,7 @@ _formatter_field_name_split = interp2app(W_UnicodeObject.descr_formatter_field_name_split), ) +W_UnicodeObject.typedef.flag_sequence_bug_compat = True def _create_list_from_unicode(value): diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py --- a/pypy/objspace/test/test_descroperation.py +++ b/pypy/objspace/test/test_descroperation.py @@ -734,6 +734,44 @@ assert X() == 'hello' + def test_sequence_rmul_overrides(self): + class oops(object): + def __rmul__(self, other): + return 42 + def __index__(self): + return 3 + assert '2' * oops() == 42 + assert [2] * oops() == 42 + assert (2,) * oops() == 42 + assert u'2' * oops() == 42 + assert bytearray('2') * oops() == 42 + assert 1000 * oops() == 42 + assert '2'.__mul__(oops()) == '222' + + def test_sequence_rmul_overrides_oldstyle(self): + class oops: + def __rmul__(self, other): + return 42 + def __index__(self): + return 3 + assert '2' * oops() == 42 + assert [2] * oops() == 42 + assert (2,) * oops() == 42 + assert u'2' * oops() == 42 + assert bytearray('2') * oops() == 42 + assert 1000 * oops() == 42 + assert '2'.__mul__(oops()) == '222' + + def test_sequence_radd_overrides(self): + class A1(list): + pass + class A2(list): + def __radd__(self, other): + return 42 + assert [2] + A1([3]) == [2, 3] + assert type([2] + A1([3])) is list + assert [2] + A2([3]) == 42 + class AppTestWithBuiltinShortcut(AppTest_Descroperation): spaceconfig = {'objspace.std.builtinshortcut': True} _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit