[pypy-commit] pypy PyBuffer-backport: hg merge default

rlamy pypy.commits at gmail.com
Sat May 6 14:51:51 EDT 2017


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: PyBuffer-backport
Changeset: r91194:9d07ac7eaf1a
Date: 2017-05-06 19:40 +0100
http://bitbucket.org/pypy/pypy/changeset/9d07ac7eaf1a/

Log:	hg merge default

diff --git a/include/README b/include/README
--- a/include/README
+++ b/include/README
@@ -1,7 +1,11 @@
 This directory contains all the include files needed to build cpython
 extensions with PyPy.  Note that these are just copies of the original headers
-that are in pypy/module/cpyext/include: they are automatically copied from
-there during translation.
+that are in pypy/module/cpyext/{include,parse}: they are automatically copied
+from there during translation.
 
-Moreover, pypy_decl.h and pypy_macros.h are automatically generated, also
-during translation.
+Moreover, some pypy-specific files are automatically generated, also during
+translation. Currently they are:
+* pypy_decl.h
+* pypy_macros.h
+* pypy_numpy.h
+* pypy_structmember_decl.h
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -501,7 +501,11 @@
   the rest is kept.  If you return an unexpected string from
   ``__hex__()`` you get an exception (or a crash before CPython 2.7.13).
 
-* PyPy3: ``__class__`` attritube assignment between heaptypes and non heaptypes.
+* In PyPy, dictionaries passed as ``**kwargs`` can contain only string keys,
+  even if the called function takes ``**kwargs``. E.g. this code always
+  produces a ``TypeError``, no matter what ``f`` is: ``f(**{1: 2})``.
+
+* PyPy3: ``__class__`` attribute assignment between heaptypes and non heaptypes.
   CPython allows that for module subtypes, but not for e.g. ``int``
   or ``float`` subtypes. Currently PyPy does not support the
   ``__class__`` attribute assignment for any non heaptype subtype.
diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -38,6 +38,8 @@
                     "compile() arg 3 must be 'exec', 'eval' or 'single'")
 
     if space.isinstance_w(w_source, space.gettypeobject(ast.W_AST.typedef)):
+        if flags & consts.PyCF_ONLY_AST:
+            return w_source
         ast_node = ast.mod.from_object(space, w_source)
         return ec.compiler.compile_ast(ast_node, filename, mode, flags)
 
diff --git a/pypy/module/__builtin__/test/test_compile.py b/pypy/module/__builtin__/test/test_compile.py
--- a/pypy/module/__builtin__/test/test_compile.py
+++ b/pypy/module/__builtin__/test/test_compile.py
@@ -50,7 +50,8 @@
         co1 = compile('print 1', '<string>', 'exec', _ast.PyCF_ONLY_AST)
         raises(TypeError, compile, co1, '<ast>', 'eval')
         co2 = compile('1+1', '<string>', 'eval', _ast.PyCF_ONLY_AST)
-        compile(co2, '<ast>', 'eval')
+        tree = compile(co2, '<ast>', 'eval')
+        assert compile(co2, '<ast>', 'eval', _ast.PyCF_ONLY_AST) is co2
 
     def test_leading_newlines(self):
         src = """
diff --git a/pypy/module/cpyext/test/test_userslots.py b/pypy/module/cpyext/test/test_userslots.py
--- a/pypy/module/cpyext/test/test_userslots.py
+++ b/pypy/module/cpyext/test/test_userslots.py
@@ -47,6 +47,33 @@
         w_year = space.getattr(w_obj, space.newtext('year'))
         assert space.int_w(w_year) == 1
 
+    def test_descr_slots(self, space, api):
+        w_descr = space.appexec([], """():
+            class Descr(object):
+                def __get__(self, obj, type):
+                    return 42
+                def __set__(self, obj, value):
+                    obj.append('set')
+                def __delete__(self, obj):
+                    obj.append('del')
+            return Descr()
+            """)
+        w_descrtype = space.type(w_descr)
+        py_descr = make_ref(space, w_descr)
+        py_descrtype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_descrtype))
+        w_obj = space.newlist([])
+        py_obj = make_ref(space, w_obj)
+        w_res = generic_cpy_call(space, py_descrtype.c_tp_descr_get,
+                                 py_descr, py_obj, py_obj)
+        assert space.int_w(w_res) == 42
+        assert generic_cpy_call(
+            space, py_descrtype.c_tp_descr_set,
+            py_descr, py_obj, make_ref(space, space.w_None)) == 0
+        assert generic_cpy_call(
+            space, py_descrtype.c_tp_descr_set,
+            py_descr, py_obj, None) == 0
+        assert space.eq_w(w_obj, space.wrap(['set', 'del']))
+
 class AppTestUserSlots(AppTestCpythonExtensionBase):
     def test_tp_hash_from_python(self):
         # to see that the functions are being used,
diff --git a/pypy/module/cpyext/userslot.py b/pypy/module/cpyext/userslot.py
--- a/pypy/module/cpyext/userslot.py
+++ b/pypy/module/cpyext/userslot.py
@@ -109,4 +109,14 @@
 def slot_tp_getattr(space, w_obj1, w_obj2):
     return space.getattr(w_obj1, w_obj2)
 
+ at slot_function([PyObject, PyObject, PyObject], PyObject)
+def slot_tp_descr_get(space, w_self, w_obj, w_type):
+    return space.get(w_self, w_obj, w_type)
 
+ at slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1)
+def slot_tp_descr_set(space, w_self, w_obj, w_value):
+    if w_value is not None:
+        space.set(w_self, w_obj, w_value)
+    else:
+        space.delete(w_self, w_obj)
+    return 0
diff --git a/pypy/module/struct/interp_struct.py b/pypy/module/struct/interp_struct.py
--- a/pypy/module/struct/interp_struct.py
+++ b/pypy/module/struct/interp_struct.py
@@ -115,16 +115,17 @@
 class W_Struct(W_Root):
     _immutable_fields_ = ["format", "size"]
 
-    def __init__(self, space, format):
+    format = ""
+    size = -1
+
+    def descr__new__(space, w_subtype, __args__):
+        return space.allocate_instance(W_Struct, w_subtype)
+
+    @unwrap_spec(format='text')
+    def descr__init__(self, space, format):
         self.format = format
         self.size = _calcsize(space, format)
 
-    @unwrap_spec(format='text')
-    def descr__new__(space, w_subtype, format):
-        self = space.allocate_instance(W_Struct, w_subtype)
-        W_Struct.__init__(self, space, format)
-        return self
-
     def descr_pack(self, space, args_w):
         return pack(space, jit.promote_string(self.format), args_w)
 
@@ -141,6 +142,7 @@
 
 W_Struct.typedef = TypeDef("Struct",
     __new__=interp2app(W_Struct.descr__new__.im_func),
+    __init__=interp2app(W_Struct.descr__init__),
     format=interp_attrproperty("format", cls=W_Struct, wrapfn="newbytes"),
     size=interp_attrproperty("size", cls=W_Struct, wrapfn="newint"),
 
diff --git a/pypy/module/struct/test/test_struct.py b/pypy/module/struct/test/test_struct.py
--- a/pypy/module/struct/test/test_struct.py
+++ b/pypy/module/struct/test/test_struct.py
@@ -429,6 +429,14 @@
         assert s.unpack(s.pack(42)) == (42,)
         assert s.unpack_from(memoryview(s.pack(42))) == (42,)
 
+    def test_struct_subclass(self):
+        class S(self.struct.Struct):
+            def __init__(self):
+                assert self.size == -1
+                super(S, self).__init__('c')
+                assert self.size == 1
+        assert S().unpack('a') == ('a',)
+
     def test_overflow(self):
         raises(self.struct.error, self.struct.pack, 'i', 1<<65)
 
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
@@ -4,54 +4,67 @@
     import_from_mixin, newlist_hint, resizelist_hint, specialize)
 from rpython.rlib.buffer import Buffer
 from rpython.rlib.rstring import StringBuilder, ByteListBuilder
-from rpython.rlib.debug import check_list_of_chars
+from rpython.rlib.debug import check_list_of_chars, check_nonneg
 from rpython.rtyper.lltypesystem import rffi
 from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr,
         nonmoving_raw_ptr_for_resizable_list)
+from rpython.rlib import jit
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec
-from pypy.interpreter.signature import Signature
 from pypy.interpreter.typedef import TypeDef
-from pypy.objspace.std.sliceobject import W_SliceObject
+from pypy.objspace.std.sliceobject import W_SliceObject, unwrap_start_stop
 from pypy.objspace.std.stringmethods import StringMethods, _get_buffer
+from pypy.objspace.std.stringmethods import _descr_getslice_slowpath
 from pypy.objspace.std.bytesobject import W_BytesObject
 from pypy.objspace.std.util import get_positive_index
 
 
-
 class W_BytearrayObject(W_Root):
     import_from_mixin(StringMethods)
+    _KIND1 = "bytearray"
+    _KIND2 = "bytearray"
 
     def __init__(self, data):
         check_list_of_chars(data)
-        self.data = resizable_list_supporting_raw_ptr(data)
+        self._data = resizable_list_supporting_raw_ptr(data)
+        self._offset = 0
+        # NOTE: the bytearray data is in 'self._data[self._offset:]'
+        check_nonneg(self._offset)
+        _tweak_for_tests(self)
+
+    def getdata(self):
+        if self._offset > 0:
+            self._data = self._data[self._offset:]
+            self._offset = 0
+        return self._data
 
     def __repr__(self):
         """representation for debugging purposes"""
-        return "%s(%s)" % (self.__class__.__name__, ''.join(self.data))
+        return "%s(%s)" % (self.__class__.__name__,
+                           ''.join(self._data[self._offset:]))
 
     def buffer_w(self, space, flags):
-        return BytearrayBuffer(self.data, False)
+        return BytearrayBuffer(self)
 
     def readbuf_w(self, space):
-        return BytearrayBuffer(self.data, True)
+        return BytearrayBuffer(self, readonly=True)
 
     def writebuf_w(self, space):
-        return BytearrayBuffer(self.data, False)
+        return BytearrayBuffer(self)
 
     def charbuf_w(self, space):
-        return ''.join(self.data)
+        return ''.join(self.getdata())
 
     def bytearray_list_of_chars_w(self, space):
-        return self.data
+        return self.getdata()
 
     def nonmovable_carray(self, space):
-        return BytearrayBuffer(self.data, False).get_raw_address()
+        return BytearrayBuffer(self).get_raw_address()
 
     def _new(self, value):
-        if value is self.data:
+        if value is self._data:
             value = value[:]
         return W_BytearrayObject(value)
 
@@ -65,17 +78,27 @@
         return W_BytearrayObject([])
 
     def _len(self):
-        return len(self.data)
+        return len(self._data) - self._offset
+
+    def _fixindex(self, space, index, errmsg="bytearray index out of range"):
+        # for getitem/setitem/delitem of a single char
+        if index >= 0:
+            index += self._offset
+            if index >= len(self._data):
+                raise OperationError(space.w_IndexError, space.newtext(errmsg))
+        else:
+            index += len(self._data)    # count from the end
+            if index < self._offset:
+                raise OperationError(space.w_IndexError, space.newtext(errmsg))
+        check_nonneg(index)
+        return index
 
     def _getitem_result(self, space, index):
-        try:
-            character = self.data[index]
-        except IndexError:
-            raise oefmt(space.w_IndexError, "bytearray index out of range")
+        character = self._data[self._fixindex(space, index)]
         return space.newint(ord(character))
 
     def _val(self, space):
-        return self.data
+        return self.getdata()
 
     @staticmethod
     def _use_rstr_ops(space, w_other):
@@ -152,11 +175,12 @@
         return 1
 
     def ord(self, space):
-        if len(self.data) != 1:
+        length = self._len()
+        if length != 1:
             raise oefmt(space.w_TypeError,
                         "ord() expected a character, but string of length %d "
-                        "found", len(self.data))
-        return space.newint(ord(self.data[0]))
+                        "found", length)
+        return space.newint(ord(self._data[self._offset]))
 
     @staticmethod
     def descr_new(space, w_bytearraytype, __args__):
@@ -169,7 +193,7 @@
             w_dict = space.w_None
         return space.newtuple([
             space.type(self), space.newtuple([
-                space.newunicode(''.join(self.data).decode('latin-1')),
+                space.newunicode(''.join(self.getdata()).decode('latin-1')),
                 space.newtext('latin-1')]),
             w_dict])
 
@@ -202,21 +226,25 @@
         else:
             if count < 0:
                 raise oefmt(space.w_ValueError, "bytearray negative count")
-            self.data = resizable_list_supporting_raw_ptr(['\0'] * count)
+            self._data = resizable_list_supporting_raw_ptr(['\0'] * count)
+            self._offset = 0
             return
 
         data = makebytearraydata_w(space, w_source)
-        self.data = resizable_list_supporting_raw_ptr(data)
+        self._data = resizable_list_supporting_raw_ptr(data)
+        self._offset = 0
+        _tweak_for_tests(self)
 
     def descr_repr(self, space):
-        s = self.data
+        s, start, end, _ = self._convert_idx_params(space, None, None)
 
         # Good default if there are no replacements.
-        buf = StringBuilder(len("bytearray(b'')") + len(s))
+        buf = StringBuilder(len("bytearray(b'')") + (end - start))
 
         buf.append("bytearray(b")
         quote = "'"
-        for c in s:
+        for i in range(start, end):
+            c = s[i]
             if c == '"':
                 quote = "'"
                 break
@@ -224,7 +252,7 @@
                 quote = '"'
         buf.append(quote)
 
-        for i in range(len(s)):
+        for i in range(start, end):
             c = s[i]
 
             if c == '\\' or c == "'":
@@ -250,11 +278,11 @@
         return space.newtext(buf.build())
 
     def descr_str(self, space):
-        return space.newtext(''.join(self.data))
+        return space.newtext(''.join(self.getdata()))
 
     def descr_eq(self, space, w_other):
         if isinstance(w_other, W_BytearrayObject):
-            return space.newbool(self.data == w_other.data)
+            return space.newbool(self.getdata() == w_other.getdata())
 
         try:
             buffer = _get_buffer(space, w_other)
@@ -274,7 +302,7 @@
 
     def descr_ne(self, space, w_other):
         if isinstance(w_other, W_BytearrayObject):
-            return space.newbool(self.data != w_other.data)
+            return space.newbool(self.getdata() != w_other.getdata())
 
         try:
             buffer = _get_buffer(space, w_other)
@@ -296,7 +324,7 @@
         value = self._val(space)
 
         if isinstance(w_other, W_BytearrayObject):
-            other = w_other.data
+            other = w_other.getdata()
             other_len = len(other)
             cmp = _memcmp(value, other, min(len(value), len(other)))
         elif isinstance(w_other, W_BytesObject):
@@ -344,7 +372,7 @@
 
     def descr_inplace_add(self, space, w_other):
         if isinstance(w_other, W_BytearrayObject):
-            self.data += w_other.data
+            self._data += w_other.getdata()
             return self
 
         if isinstance(w_other, W_BytesObject):
@@ -356,7 +384,7 @@
     @specialize.argtype(1)
     def _inplace_add(self, other):
         for i in range(len(other)):
-            self.data.append(other[i])
+            self._data.append(other[i])
 
     def descr_inplace_mul(self, space, w_times):
         try:
@@ -365,85 +393,99 @@
             if e.match(space, space.w_TypeError):
                 return space.w_NotImplemented
             raise
-        self.data *= times
+        data = self.getdata()
+        data *= times
         return self
 
     def descr_setitem(self, space, w_index, w_other):
         if isinstance(w_index, W_SliceObject):
-            oldsize = len(self.data)
+            sequence2 = makebytearraydata_w(space, w_other)
+            oldsize = self._len()
             start, stop, step, slicelength = w_index.indices4(space, oldsize)
-            sequence2 = makebytearraydata_w(space, w_other)
-            _setitem_slice_helper(space, self.data, start, step,
+            if start == 0 and step == 1 and len(sequence2) <= slicelength:
+                self._delete_from_start(slicelength - len(sequence2))
+                slicelength = len(sequence2)
+                if slicelength == 0:
+                    return
+            data = self._data
+            start += self._offset
+            _setitem_slice_helper(space, data, start, step,
                                   slicelength, sequence2, empty_elem='\x00')
         else:
             idx = space.getindex_w(w_index, space.w_IndexError,
                                    "bytearray index")
-            try:
-                self.data[idx] = getbytevalue(space, w_other)
-            except IndexError:
-                raise oefmt(space.w_IndexError, "bytearray index out of range")
+            newvalue = getbytevalue(space, w_other)
+            self._data[self._fixindex(space, idx)] = newvalue
 
     def descr_delitem(self, space, w_idx):
         if isinstance(w_idx, W_SliceObject):
-            start, stop, step, slicelength = w_idx.indices4(space,
-                                                            len(self.data))
-            _delitem_slice_helper(space, self.data, start, step, slicelength)
+            start, stop, step, slicelength = w_idx.indices4(space, self._len())
+            if start == 0 and step == 1:
+                self._delete_from_start(slicelength)
+            else:
+                _delitem_slice_helper(space, self._data,
+                                      start + self._offset, step, slicelength)
         else:
             idx = space.getindex_w(w_idx, space.w_IndexError,
                                    "bytearray index")
-            try:
-                del self.data[idx]
-            except IndexError:
-                raise oefmt(space.w_IndexError,
-                            "bytearray deletion index out of range")
+            idx = self._fixindex(space, idx)
+            if idx == self._offset:    # fast path for del x[0] or del[-len]
+                self._delete_from_start(1)
+            else:
+                del self._data[idx]
+
+    def _delete_from_start(self, n):
+        assert n >= 0
+        self._offset += n
+        jit.conditional_call(self._offset > len(self._data) / 2,
+                             _shrink_after_delete_from_start, self)
 
     def descr_append(self, space, w_item):
-        self.data.append(getbytevalue(space, w_item))
+        self._data.append(getbytevalue(space, w_item))
 
     def descr_extend(self, space, w_other):
         if isinstance(w_other, W_BytearrayObject):
-            self.data += w_other.data
+            self._data += w_other.getdata()
         else:
-            self.data += makebytearraydata_w(space, w_other)
-        return self
+            self._inplace_add(makebytearraydata_w(space, w_other))
 
     def descr_insert(self, space, w_idx, w_other):
         where = space.int_w(w_idx)
-        length = len(self.data)
+        val = getbytevalue(space, w_other)
+        data = self.getdata()
+        length = len(data)
         index = get_positive_index(where, length)
-        val = getbytevalue(space, w_other)
-        self.data.insert(index, val)
-        return space.w_None
+        data.insert(index, val)
 
     @unwrap_spec(w_idx=WrappedDefault(-1))
     def descr_pop(self, space, w_idx):
         index = space.int_w(w_idx)
-        try:
-            result = self.data.pop(index)
-        except IndexError:
-            if not self.data:
-                raise oefmt(space.w_IndexError, "pop from empty bytearray")
-            raise oefmt(space.w_IndexError, "pop index out of range")
+        if self._len() == 0:
+            raise oefmt(space.w_IndexError, "pop from empty bytearray")
+        index = self._fixindex(space, index, "pop index out of range")
+        result = self._data.pop(index)
         return space.newint(ord(result))
 
     def descr_remove(self, space, w_char):
         char = space.int_w(space.index(w_char))
-        try:
-            self.data.remove(chr(char))
-        except ValueError:
-            raise oefmt(space.w_ValueError, "value not found in bytearray")
+        _data = self._data
+        for index in range(self._offset, len(_data)):
+            if ord(_data[index]) == char:
+                del _data[index]
+                return
+        raise oefmt(space.w_ValueError, "value not found in bytearray")
 
     _StringMethods_descr_contains = descr_contains
     def descr_contains(self, space, w_sub):
         if space.isinstance_w(w_sub, space.w_int):
             char = space.int_w(w_sub)
-            return _descr_contains_bytearray(self.data, space, char)
+            return _descr_contains_bytearray(self.getdata(), space, char)
 
         return self._StringMethods_descr_contains(space, w_sub)
 
     def descr_add(self, space, w_other):
         if isinstance(w_other, W_BytearrayObject):
-            return self._new(self.data + w_other.data)
+            return self._new(self.getdata() + w_other.getdata())
 
         if isinstance(w_other, W_BytesObject):
             return self._add(self._op_val(space, w_other))
@@ -458,11 +500,37 @@
 
     @specialize.argtype(1)
     def _add(self, other):
-        return self._new(self.data + [other[i] for i in range(len(other))])
+        return self._new(self.getdata() + [other[i] for i in range(len(other))])
 
     def descr_reverse(self, space):
-        self.data.reverse()
+        self.getdata().reverse()
 
+    def descr_alloc(self, space):
+        return space.newint(len(self._data) + 1)   # includes the _offset part
+
+    def _convert_idx_params(self, space, w_start, w_end):
+        # optimization: this version doesn't force getdata()
+        start, end = unwrap_start_stop(space, self._len(), w_start, w_end)
+        ofs = self._offset
+        return (self._data, start + ofs, end + ofs, ofs)
+
+    def descr_getitem(self, space, w_index):
+        # optimization: this version doesn't force getdata()
+        if isinstance(w_index, W_SliceObject):
+            start, stop, step, sl = w_index.indices4(space, self._len())
+            if sl == 0:
+                return self._empty()
+            elif step == 1:
+                assert start >= 0 and stop >= 0
+                ofs = self._offset
+                return self._new(self._data[start + ofs : stop + ofs])
+            else:
+                start += self._offset
+                ret = _descr_getslice_slowpath(self._data, start, step, sl)
+                return self._new_from_list(ret)
+
+        index = space.getindex_w(w_index, space.w_IndexError, self._KIND1)
+        return self._getitem_result(space, index)
 
 
 # ____________________________________________________________
@@ -1133,6 +1201,8 @@
                         doc=BytearrayDocstrings.remove.__doc__),
     reverse = interp2app(W_BytearrayObject.descr_reverse,
                          doc=BytearrayDocstrings.reverse.__doc__),
+    __alloc__ = interp2app(W_BytearrayObject.descr_alloc,
+                           doc=BytearrayDocstrings.__alloc__.__doc__),
 )
 W_BytearrayObject.typedef.flag_sequence_bug_compat = True
 
@@ -1197,21 +1267,6 @@
                     "attempt to assign sequence of size %d to extended slice "
                     "of size %d", len2, slicelength)
 
-    if sequence2 is items:
-        if step > 0:
-            # Always copy starting from the right to avoid
-            # having to make a shallow copy in the case where
-            # the source and destination lists are the same list.
-            i = len2 - 1
-            start += i*step
-            while i >= 0:
-                items[start] = sequence2[i]
-                start -= step
-                i -= 1
-            return
-        else:
-            # Make a shallow copy to more easily handle the reversal case
-            sequence2 = list(sequence2)
     for i in range(len2):
         items[start] = sequence2[i]
         start += step
@@ -1220,36 +1275,47 @@
 class BytearrayBuffer(Buffer):
     _immutable_ = True
 
-    def __init__(self, data, readonly):
-        self.data = data
+    def __init__(self, ba, readonly=False):
+        self.ba = ba     # the W_BytearrayObject
         self.readonly = readonly
 
     def getlength(self):
-        return len(self.data)
+        return self.ba._len()
 
     def getitem(self, index):
-        return self.data[index]
+        ba = self.ba
+        return ba._data[ba._offset + index]
 
     def setitem(self, index, char):
-        self.data[index] = char
+        ba = self.ba
+        ba._data[ba._offset + index] = char
 
     def getslice(self, start, stop, step, size):
         if size == 0:
             return ""
         if step == 1:
             assert 0 <= start <= stop
-            if start == 0 and stop == len(self.data):
-                return "".join(self.data)
-            return "".join(self.data[start:stop])
+            ba = self.ba
+            start += ba._offset
+            stop += ba._offset
+            data = ba._data
+            if start != 0 or stop != len(data):
+                data = data[start:stop]
+            return "".join(data)
         return Buffer.getslice(self, start, stop, step, size)
 
     def setslice(self, start, string):
         # No bounds checks.
+        ba = self.ba
+        start += ba._offset
         for i in range(len(string)):
-            self.data[start + i] = string[i]
+            ba._data[start + i] = string[i]
 
     def get_raw_address(self):
-        return nonmoving_raw_ptr_for_resizable_list(self.data)
+        ba = self.ba
+        p = nonmoving_raw_ptr_for_resizable_list(ba._data)
+        p = rffi.ptradd(p, ba._offset)
+        return p
 
 
 @specialize.argtype(1)
@@ -1261,3 +1327,9 @@
         if selfvalue[i] > buffer[i]:
             return 1
     return 0
+
+def _tweak_for_tests(w_bytearray):
+    "Patched in test_bytearray.py"
+
+def _shrink_after_delete_from_start(w_bytearray):
+    w_bytearray.getdata()
diff --git a/pypy/objspace/std/stringmethods.py b/pypy/objspace/std/stringmethods.py
--- a/pypy/objspace/std/stringmethods.py
+++ b/pypy/objspace/std/stringmethods.py
@@ -26,7 +26,8 @@
         value = self._val(space)
         lenself = len(value)
         start, end = unwrap_start_stop(space, lenself, w_start, w_end)
-        return (value, start, end)
+        # the None means "no offset"; see bytearrayobject.py
+        return (value, start, end, None)
 
     def _multi_chr(self, c):
         return c
@@ -38,18 +39,18 @@
     #    pass
 
     def descr_contains(self, space, w_sub):
-        value = self._val(space)
+        value, start, end, _ = self._convert_idx_params(space, None, None)
         if self._use_rstr_ops(space, w_sub):
             other = self._op_val(space, w_sub)
-            return space.newbool(value.find(other) >= 0)
+            return space.newbool(value.find(other, start, end) >= 0)
 
         from pypy.objspace.std.bytesobject import W_BytesObject
         if isinstance(w_sub, W_BytesObject):
             other = self._op_val(space, w_sub)
-            res = find(value, other, 0, len(value))
+            res = find(value, other, start, end)
         else:
             buffer = _get_buffer(space, w_sub)
-            res = find(value, buffer, 0, len(value))
+            res = find(value, buffer, start, end)
 
         return space.newbool(res >= 0)
 
@@ -146,7 +147,7 @@
         return self._new(centered)
 
     def descr_count(self, space, w_sub, w_start=None, w_end=None):
-        value, start, end = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, _ = self._convert_idx_params(space, w_start, w_end)
 
         if self._use_rstr_ops(space, w_sub):
             return space.newint(value.count(self._op_val(space, w_sub), start,
@@ -155,7 +156,7 @@
         from pypy.objspace.std.bytearrayobject import W_BytearrayObject
         from pypy.objspace.std.bytesobject import W_BytesObject
         if isinstance(w_sub, W_BytearrayObject):
-            res = count(value, w_sub.data, start, end)
+            res = count(value, w_sub.getdata(), start, end)
         elif isinstance(w_sub, W_BytesObject):
             res = count(value, w_sub._value, start, end)
         else:
@@ -235,7 +236,7 @@
         return distance
 
     def descr_find(self, space, w_sub, w_start=None, w_end=None):
-        (value, start, end) = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, ofs = self._convert_idx_params(space, w_start, w_end)
 
         if self._use_rstr_ops(space, w_sub):
             res = value.find(self._op_val(space, w_sub), start, end)
@@ -244,17 +245,18 @@
         from pypy.objspace.std.bytearrayobject import W_BytearrayObject
         from pypy.objspace.std.bytesobject import W_BytesObject
         if isinstance(w_sub, W_BytearrayObject):
-            res = find(value, w_sub.data, start, end)
+            res = find(value, w_sub.getdata(), start, end)
         elif isinstance(w_sub, W_BytesObject):
             res = find(value, w_sub._value, start, end)
         else:
             buffer = _get_buffer(space, w_sub)
             res = find(value, buffer, start, end)
-
+        if ofs is not None and res >= 0:
+            res -= ofs
         return space.newint(res)
 
     def descr_rfind(self, space, w_sub, w_start=None, w_end=None):
-        (value, start, end) = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, ofs = self._convert_idx_params(space, w_start, w_end)
 
         if self._use_rstr_ops(space, w_sub):
             res = value.rfind(self._op_val(space, w_sub), start, end)
@@ -263,24 +265,25 @@
         from pypy.objspace.std.bytearrayobject import W_BytearrayObject
         from pypy.objspace.std.bytesobject import W_BytesObject
         if isinstance(w_sub, W_BytearrayObject):
-            res = rfind(value, w_sub.data, start, end)
+            res = rfind(value, w_sub.getdata(), start, end)
         elif isinstance(w_sub, W_BytesObject):
             res = rfind(value, w_sub._value, start, end)
         else:
             buffer = _get_buffer(space, w_sub)
             res = rfind(value, buffer, start, end)
-
+        if ofs is not None and res >= 0:
+            res -= ofs
         return space.newint(res)
 
     def descr_index(self, space, w_sub, w_start=None, w_end=None):
-        (value, start, end) = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, ofs = self._convert_idx_params(space, w_start, w_end)
 
         from pypy.objspace.std.bytearrayobject import W_BytearrayObject
         from pypy.objspace.std.bytesobject import W_BytesObject
         if self._use_rstr_ops(space, w_sub):
             res = value.find(self._op_val(space, w_sub), start, end)
         elif isinstance(w_sub, W_BytearrayObject):
-            res = find(value, w_sub.data, start, end)
+            res = find(value, w_sub.getdata(), start, end)
         elif isinstance(w_sub, W_BytesObject):
             res = find(value, w_sub._value, start, end)
         else:
@@ -290,17 +293,19 @@
         if res < 0:
             raise oefmt(space.w_ValueError,
                         "substring not found in string.index")
+        if ofs is not None:
+            res -= ofs
         return space.newint(res)
 
     def descr_rindex(self, space, w_sub, w_start=None, w_end=None):
-        (value, start, end) = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, ofs = self._convert_idx_params(space, w_start, w_end)
 
         from pypy.objspace.std.bytearrayobject import W_BytearrayObject
         from pypy.objspace.std.bytesobject import W_BytesObject
         if self._use_rstr_ops(space, w_sub):
             res = value.rfind(self._op_val(space, w_sub), start, end)
         elif isinstance(w_sub, W_BytearrayObject):
-            res = rfind(value, w_sub.data, start, end)
+            res = rfind(value, w_sub.getdata(), start, end)
         elif isinstance(w_sub, W_BytesObject):
             res = rfind(value, w_sub._value, start, end)
         else:
@@ -310,6 +315,8 @@
         if res < 0:
             raise oefmt(space.w_ValueError,
                         "substring not found in string.rindex")
+        if ofs is not None:
+            res -= ofs
         return space.newint(res)
 
     @specialize.arg(2)
@@ -612,7 +619,7 @@
         return self._newlist_unwrapped(space, strs)
 
     def descr_startswith(self, space, w_prefix, w_start=None, w_end=None):
-        (value, start, end) = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, _ = self._convert_idx_params(space, w_start, w_end)
         if space.isinstance_w(w_prefix, space.w_tuple):
             return self._startswith_tuple(space, value, w_prefix, start, end)
         return space.newbool(self._startswith(space, value, w_prefix, start,
@@ -635,7 +642,7 @@
                          # bytearrays, but overridden for unicodes
 
     def descr_endswith(self, space, w_suffix, w_start=None, w_end=None):
-        (value, start, end) = self._convert_idx_params(space, w_start, w_end)
+        value, start, end, _ = self._convert_idx_params(space, w_start, w_end)
         if space.isinstance_w(w_suffix, space.w_tuple):
             return self._endswith_tuple(space, value, w_suffix, start, end)
         return space.newbool(self._endswith(space, value, w_suffix, start,
diff --git a/pypy/objspace/std/test/test_bytearrayobject.py b/pypy/objspace/std/test/test_bytearrayobject.py
--- a/pypy/objspace/std/test/test_bytearrayobject.py
+++ b/pypy/objspace/std/test/test_bytearrayobject.py
@@ -1,9 +1,26 @@
+import random
 from pypy import conftest
+from pypy.objspace.std import bytearrayobject
+
+class DontAccess(object):
+    pass
+dont_access = DontAccess()
+
 
 
 class AppTestBytesArray:
     def setup_class(cls):
         cls.w_runappdirect = cls.space.wrap(conftest.option.runappdirect)
+        def tweak(w_bytearray):
+            n = random.randint(-3, 16)
+            if n > 0:
+                w_bytearray._data = [dont_access] * n + w_bytearray._data
+                w_bytearray._offset += n
+        cls._old_tweak = [bytearrayobject._tweak_for_tests]
+        bytearrayobject._tweak_for_tests = tweak
+
+    def teardown_class(cls):
+        [bytearrayobject._tweak_for_tests] = cls._old_tweak
 
     def test_basics(self):
         b = bytearray()
@@ -67,6 +84,7 @@
         raises(IndexError, b.__getitem__, 4)
         assert b[1:5] == bytearray('est')
         assert b[slice(1,5)] == bytearray('est')
+        assert b[1:5:2] == bytearray(b'et')
 
     def test_arithmetic(self):
         b1 = bytearray('hello ')
@@ -204,6 +222,10 @@
         assert bytearray('ab').endswith(bytearray(''), 2) is True
         assert bytearray('ab').endswith(bytearray(''), 3) is False
 
+    def test_startswith_self(self):
+        b = bytearray(b'abcd')
+        assert b.startswith(b)
+
     def test_stringlike_conversions(self):
         # methods that should return bytearray (and not str)
         def check(result, expected):
@@ -330,6 +352,20 @@
         b.reverse()
         assert b == bytearray('olleh')
 
+    def test_delitem_from_front(self):
+        b = bytearray(b'abcdefghij')
+        del b[0]
+        del b[0]
+        assert len(b) == 8
+        assert b == bytearray(b'cdefghij')
+        del b[-8]
+        del b[-7]
+        assert len(b) == 6
+        assert b == bytearray(b'efghij')
+        del b[:3]
+        assert len(b) == 3
+        assert b == bytearray(b'hij')
+
     def test_delitem(self):
         b = bytearray('abc')
         del b[1]
@@ -412,6 +448,18 @@
         raises(TypeError, b.extend, [object()])
         raises(TypeError, b.extend, u"unicode")
 
+    def test_setitem_from_front(self):
+        b = bytearray(b'abcdefghij')
+        b[:2] = b''
+        assert len(b) == 8
+        assert b == bytearray(b'cdefghij')
+        b[:3] = b'X'
+        assert len(b) == 6
+        assert b == bytearray(b'Xfghij')
+        b[:2] = b'ABC'
+        assert len(b) == 7
+        assert b == bytearray(b'ABCghij')
+
     def test_setslice(self):
         b = bytearray('hello')
         b[:] = [ord(c) for c in 'world']
@@ -502,3 +550,78 @@
     def test_split_whitespace(self):
         b = bytearray(b'\x09\x0A\x0B\x0C\x0D\x1C\x1D\x1E\x1F')
         assert b.split() == [b'\x1c\x1d\x1e\x1f']
+
+    def test_dont_force_offset(self):
+        def make(x=b'abcdefghij', shift=3):
+            b = bytearray(b'?'*shift + x)
+            b + b''                       # force 'b'
+            del b[:shift]                 # add shift to b._offset
+            return b
+        assert make(shift=0).__alloc__() == 11
+        #
+        x = make(shift=3)
+        assert x.__alloc__() == 14
+        assert memoryview(x)[1] == 'b'
+        assert x.__alloc__() == 14
+        assert len(x) == 10
+        assert x.__alloc__() == 14
+        assert x[3] == ord('d')
+        assert x[-3] == ord('h')
+        assert x.__alloc__() == 14
+        assert x[3:-3] == b'defg'
+        assert x[-3:3:-1] == b'hgfe'
+        assert x.__alloc__() == 14
+        assert repr(x) == "bytearray(b'abcdefghij')"
+        assert x.__alloc__() == 14
+        #
+        x = make(shift=3)
+        x[3] = ord('D')
+        assert x.__alloc__() == 14
+        x[4:6] = b'EF'
+        assert x.__alloc__() == 14
+        x[6:8] = b'G'
+        assert x.__alloc__() == 13
+        x[-2:4:-2] = b'*/'
+        assert x.__alloc__() == 13
+        assert x == bytearray(b'abcDE/G*j')
+        #
+        x = make(b'abcdefghijklmnopqrstuvwxyz', shift=11)
+        assert len(x) == 26
+        assert x.__alloc__() == 38
+        del x[:1]
+        assert len(x) == 25
+        assert x.__alloc__() == 38
+        del x[0:5]
+        assert len(x) == 20
+        assert x.__alloc__() == 38
+        del x[0]
+        assert len(x) == 19
+        assert x.__alloc__() == 38
+        del x[0]                      # too much emptiness, forces now
+        assert len(x) == 18
+        assert x.__alloc__() == 19
+        #
+        x = make(b'abcdefghijklmnopqrstuvwxyz', shift=11)
+        del x[:9]                     # too much emptiness, forces now
+        assert len(x) == 17
+        assert x.__alloc__() == 18
+        #
+        x = make(b'abcdefghijklmnopqrstuvwxyz', shift=11)
+        assert x.__alloc__() == 38
+        del x[1]
+        assert x.__alloc__() == 37      # not forced, but the list shrank
+        del x[3:10:2]
+        assert x.__alloc__() == 33
+        assert x == bytearray(b'acdfhjlmnopqrstuvwxyz')
+        #
+        x = make(shift=3)
+        assert b'f' in x
+        assert b'ef' in x
+        assert b'efx' not in x
+        assert b'very long string longer than the original' not in x
+        assert x.__alloc__() == 14
+        assert x.find(b'f') == 5
+        assert x.rfind(b'f', 2, 11) == 5
+        assert x.find(b'fe') == -1
+        assert x.index(b'f', 2, 11) == 5
+        assert x.__alloc__() == 14


More information about the pypy-commit mailing list