[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