Author: Brian Kearns <bdkea...@gmail.com> Branch: refactor-buffer-api Changeset: r70928:c25773816a8a Date: 2014-04-24 13:04 -0400 http://bitbucket.org/pypy/pypy/changeset/c25773816a8a/
Log: test/fix struct pack_into/unpack_from behavior diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1416,6 +1416,10 @@ @specialize.arg(1) def getarg_w(self, code, w_obj): + if code == 'z*': + if self.is_none(w_obj): + return None + code = 's*' if code == 's*': if self.isinstance_w(w_obj, self.w_str): return w_obj.readbuf_w(self) diff --git a/pypy/module/struct/__init__.py b/pypy/module/struct/__init__.py --- a/pypy/module/struct/__init__.py +++ b/pypy/module/struct/__init__.py @@ -48,13 +48,13 @@ interpleveldefs = { 'calcsize': 'interp_struct.calcsize', 'pack': 'interp_struct.pack', + 'pack_into': 'interp_struct.pack_into', 'unpack': 'interp_struct.unpack', + 'unpack_from': 'interp_struct.unpack_from', 'Struct': 'interp_struct.W_Struct', } appleveldefs = { 'error': 'app_struct.error', - 'pack_into': 'app_struct.pack_into', - 'unpack_from': 'app_struct.unpack_from', } diff --git a/pypy/module/struct/app_struct.py b/pypy/module/struct/app_struct.py --- a/pypy/module/struct/app_struct.py +++ b/pypy/module/struct/app_struct.py @@ -2,23 +2,8 @@ """ Application-level definitions for the struct module. """ -import struct class error(Exception): """Exception raised on various occasions; argument is a string describing what is wrong.""" - -# XXX inefficient -def pack_into(fmt, buf, offset, *args): - data = struct.pack(fmt, *args) - buffer(buf)[offset:offset+len(data)] = data - -# XXX inefficient -def unpack_from(fmt, buf, offset=0): - size = struct.calcsize(fmt) - data = buffer(buf)[offset:offset+size] - if len(data) != size: - raise error("unpack_from requires a buffer of at least %d bytes" - % (size,)) - return struct.unpack(fmt, data) 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 @@ -5,7 +5,7 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.error import OperationError +from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.typedef import TypeDef, interp_attrproperty from pypy.module.struct.formatiterator import ( PackFormatIterator, UnpackFormatIterator @@ -29,6 +29,7 @@ raise OperationError(w_error, space.wrap(e.msg)) return fmtiter.totalsize + @unwrap_spec(format=str) def pack(space, format, args_w): if jit.isconstant(format): @@ -47,6 +48,23 @@ return space.wrap(fmtiter.result.build()) +# XXX inefficient +@unwrap_spec(format=str, offset=int) +def pack_into(space, format, w_buf, offset, args_w): + res = pack(space, format, args_w).str_w(space) + buf = space.writebuf_w(w_buf) + if offset < 0: + offset += buf.getlength() + size = len(res) + if offset < 0 or (buf.getlength() - offset) < size: + w_module = space.getbuiltinmodule('struct') + w_error = space.getattr(w_module, space.wrap('error')) + raise oefmt(w_error, + "pack_into requires a buffer of at least %d bytes", + size) + buf.setslice(offset, res) + + @unwrap_spec(format=str, input='bufferstr') def unpack(space, format, input): fmtiter = UnpackFormatIterator(space, input) @@ -61,6 +79,27 @@ return space.newtuple(fmtiter.result_w[:]) +# XXX inefficient +@unwrap_spec(format=str, offset=int) +def unpack_from(space, format, w_buf, offset=0): + size = _calcsize(space, format) + buf = space.getarg_w('z*', w_buf) + if buf is None: + w_module = space.getbuiltinmodule('struct') + w_error = space.getattr(w_module, space.wrap('error')) + raise oefmt(w_error, "unpack_from requires a buffer argument") + if offset < 0: + offset += buf.getlength() + if offset < 0 or (buf.getlength() - offset) < size: + w_module = space.getbuiltinmodule('struct') + w_error = space.getattr(w_module, space.wrap('error')) + raise oefmt(w_error, + "unpack_from requires a buffer of at least %d bytes", + size) + data = buf.getslice(offset, offset + size, 1, size) + return unpack(space, format, data) + + class W_Struct(W_Root): _immutable_fields_ = ["format", "size"] 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 @@ -2,12 +2,11 @@ Tests for the struct module implemented at interp-level in pypy/module/struct. """ -import py from rpython.rlib.rstruct.nativefmttable import native_is_bigendian class AppTestStruct(object): - spaceconfig = dict(usemodules=['struct']) + spaceconfig = dict(usemodules=['struct', 'array']) def setup_class(cls): """ @@ -26,7 +25,6 @@ """ assert issubclass(self.struct.error, Exception) - def test_calcsize_standard(self): """ Check the standard size of the various format characters. @@ -52,14 +50,12 @@ # test with some repetitions and multiple format characters assert calcsize('=bQ3i') == 1 + 8 + 3*4 - def test_index(self): class X(object): def __index__(self): return 3 assert self.struct.unpack("i", self.struct.pack("i", X()))[0] == 3 - def test_deprecation_warning(self): import warnings for code in 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q': @@ -70,7 +66,6 @@ assert str(w[0].message) == "integer argument expected, got non-integer" assert w[0].category is DeprecationWarning - def test_pack_standard_little(self): """ Check packing with the '<' format specifier. @@ -84,7 +79,6 @@ assert pack("<q", -0x41B2B3B4B5B6B7B8) == 'HHIJKLM\xbe' assert pack("<Q", 0x8142434445464748) == 'HGFEDCB\x81' - def test_unpack_standard_little(self): """ Check unpacking with the '<' format specifier. @@ -98,7 +92,6 @@ assert unpack("<q", 'HHIJKLM\xbe') == (-0x41B2B3B4B5B6B7B8,) assert unpack("<Q", 'HGFEDCB\x81') == (0x8142434445464748,) - def test_pack_standard_big(self): """ Check packing with the '>' format specifier. @@ -112,7 +105,6 @@ assert pack(">q", -0x41B2B3B4B5B6B7B8) == '\xbeMLKJIHH' assert pack(">Q", 0x8142434445464748) == '\x81BCDEFGH' - def test_unpack_standard_big(self): """ Check unpacking with the '>' format specifier. @@ -126,7 +118,6 @@ assert unpack(">q", '\xbeMLKJIHH') == (-0x41B2B3B4B5B6B7B8,) assert unpack(">Q", '\x81BCDEFGH') == (0x8142434445464748,) - def test_calcsize_native(self): """ Check that the size of the various format characters is reasonable. @@ -156,7 +147,6 @@ assert calcsize('ibb') == calcsize('i') + 2 * calcsize('b') assert calcsize('ih') == calcsize('i') + calcsize('h') - def test_pack_native(self): """ Check packing with the native format. @@ -174,7 +164,6 @@ assert res[sizeofi:] == '\x05' + '\x00' * (sizeofi-1) assert pack("q", -1) == '\xff' * calcsize("q") - def test_unpack_native(self): """ Check unpacking with the native format. @@ -185,7 +174,6 @@ assert unpack("bi", pack("bi", -2, 5)) == (-2, 5) assert unpack("q", '\xff' * calcsize("q")) == (-1,) - def test_string_format(self): """ Check the 's' format character. @@ -200,7 +188,6 @@ assert unpack("5s3s", "worldspa") == ("world", "spa") assert unpack("0s", "") == ("",) - def test_pascal_format(self): """ Check the 'p' format character. @@ -220,7 +207,6 @@ assert unpack("1p", "\x03") == ("",) assert unpack("300p", longpacked300) == (longstring[:255],) - def test_char_format(self): """ Check the 'c' format character. @@ -232,7 +218,6 @@ assert unpack("c", "?") == ("?",) assert unpack("5c", "a\xc0\x00\n-") == ("a", "\xc0", "\x00", "\n", "-") - def test_pad_format(self): """ Check the 'x' format character. @@ -244,7 +229,6 @@ assert unpack("x", "?") == () assert unpack("5x", "hello") == () - def test_native_floats(self): """ Check the 'd' and 'f' format characters on native packing. @@ -261,7 +245,6 @@ assert res != 12.34 # precision lost assert abs(res - 12.34) < 1E-6 - def test_standard_floats(self): """ Check the 'd' and 'f' format characters on standard packing. @@ -280,7 +263,6 @@ def test_bool(self): pack = self.struct.pack - unpack = self.struct.unpack assert pack("!?", True) == '\x01' assert pack(">?", True) == '\x01' assert pack("!?", False) == '\x00' @@ -343,15 +325,12 @@ raises(error, pack, "b", 150) # argument out of range # XXX the accepted ranges still differs between PyPy and CPython - def test_overflow_error(self): """ Check OverflowError cases. """ import sys calcsize = self.struct.calcsize - pack = self.struct.pack - unpack = self.struct.unpack someerror = (OverflowError, self.struct.error) raises(someerror, calcsize, "%dc" % (sys.maxint+1,)) raises(someerror, calcsize, "999999999999999999999999999c") @@ -360,7 +339,6 @@ raises(someerror, calcsize, "c%dc" % (sys.maxint,)) raises(someerror, calcsize, "%dci" % (sys.maxint,)) - def test_unicode(self): """ A PyPy extension: accepts the 'u' format character in native mode, @@ -374,7 +352,6 @@ assert data == str(buffer(u'XYZ')) assert self.struct.unpack("uuu", data) == (u'X', u'Y', u'Z') - def test_unpack_buffer(self): """ Buffer objects can be passed to struct.unpack(). @@ -383,6 +360,34 @@ assert self.struct.unpack("ii", b) == (62, 12) raises(self.struct.error, self.struct.unpack, "i", b) + def test_pack_unpack_buffer(self): + import array + b = array.array('c', '\x00' * 19) + sz = self.struct.calcsize("ii") + for offset in [2, -17]: + self.struct.pack_into("ii", b, offset, 17, 42) + assert str(buffer(b)) == ('\x00' * 2 + + self.struct.pack("ii", 17, 42) + + '\x00' * (19-sz-2)) + exc = raises(TypeError, self.struct.pack_into, "ii", 'test', 0, 17, 42) + assert str(exc.value) == "Cannot use string as modifiable buffer" + exc = raises(self.struct.error, self.struct.pack_into, "ii", b[0:1], 0, 17, 42) + assert str(exc.value) == "pack_into requires a buffer of at least 8 bytes" + + assert self.struct.unpack_from("ii", b, 2) == (17, 42) + assert self.struct.unpack_from("ii", b, -17) == (17, 42) + assert self.struct.unpack_from("ii", buffer(b, 2)) == (17, 42) + assert self.struct.unpack_from("ii", buffer(b), 2) == (17, 42) + assert self.struct.unpack_from("ii", memoryview(buffer(b)), 2) == (17, 42) + exc = raises(TypeError, self.struct.unpack_from, "ii", 123) + assert 'must be string or buffer, not int' in str(exc.value) + exc = raises(self.struct.error, self.struct.unpack_from, "ii", None) + assert str(exc.value) == "unpack_from requires a buffer argument" + exc = raises(self.struct.error, self.struct.unpack_from, "ii", '') + assert str(exc.value) == "unpack_from requires a buffer of at least 8 bytes" + exc = raises(self.struct.error, self.struct.unpack_from, "ii", memoryview('')) + assert str(exc.value) == "unpack_from requires a buffer of at least 8 bytes" + def test___float__(self): class MyFloat(object): def __init__(self, x): diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -449,8 +449,11 @@ def readbuf_w(self, space): return StringBuffer(self._value) - def charbuf_w(self, space): - return self._value + def writebuf_w(self, space): + raise OperationError(space.w_TypeError, space.wrap( + "Cannot use string as modifiable buffer")) + + charbuf_w = str_w def listview_bytes(self): return _create_list_from_bytes(self._value) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit