[pypy-commit] pypy refactor-buffer-api: test/fix struct pack_into/unpack_from behavior
bdkearns
noreply at buildbot.pypy.org
Thu Apr 24 19:05:49 CEST 2014
Author: Brian Kearns <bdkearns at 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
+ at 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
+ at 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)
More information about the pypy-commit
mailing list