Author: Ronan Lamy <ronan.l...@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) +@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) +@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit