[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