[pypy-svn] r46988 - in pypy/dist/pypy/module/struct: . test

arigo at codespeak.net arigo at codespeak.net
Fri Sep 28 10:36:53 CEST 2007


Author: arigo
Date: Fri Sep 28 10:36:52 2007
New Revision: 46988

Modified:
   pypy/dist/pypy/module/struct/formatiterator.py
   pypy/dist/pypy/module/struct/nativefmttable.py
   pypy/dist/pypy/module/struct/standardfmttable.py
   pypy/dist/pypy/module/struct/test/test_struct.py
Log:
Emulate CPython 2.4 accepting broken input for struct.pack().


Modified: pypy/dist/pypy/module/struct/formatiterator.py
==============================================================================
--- pypy/dist/pypy/module/struct/formatiterator.py	(original)
+++ pypy/dist/pypy/module/struct/formatiterator.py	Fri Sep 28 10:36:52 2007
@@ -1,9 +1,11 @@
 
+from pypy.interpreter.error import OperationError
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.rarithmetic import ovfcheck
 
 from pypy.module.struct.error import StructError
 from pypy.module.struct.standardfmttable import standard_fmttable
+from pypy.module.struct.standardfmttable import PACK_ACCEPTS_BROKEN_INPUT
 from pypy.module.struct.nativefmttable import native_fmttable
 from pypy.module.struct.nativefmttable import native_is_bigendian
 
@@ -136,21 +138,63 @@
         self.args_index += 1
         return w_obj
 
-    def accept_int_arg(self):
-        w_obj = self.accept_obj_arg()
-        return self.space.int_w(w_obj)
-
-    def accept_uint_arg(self):
-        w_obj = self.accept_obj_arg()
-        return self.space.uint_w(w_obj)
+    if PACK_ACCEPTS_BROKEN_INPUT:
+        # permissive version - accepts float arguments too
 
-    def accept_longlong_arg(self):
-        w_obj = self.accept_obj_arg()
-        return self.space.r_longlong_w(w_obj)
-
-    def accept_ulonglong_arg(self):
-        w_obj = self.accept_obj_arg()
-        return self.space.r_ulonglong_w(w_obj)
+        def accept_int_arg(self):
+            w_obj = self.accept_obj_arg()
+            try:
+                return self.space.int_w(w_obj)
+            except OperationError, e:
+                return self.space.int_w(self._maybe_float(e, w_obj))
+
+        def accept_uint_arg(self):
+            w_obj = self.accept_obj_arg()
+            try:
+                return self.space.uint_w(w_obj)
+            except OperationError, e:
+                return self.space.uint_w(self._maybe_float(e, w_obj))
+
+        def accept_longlong_arg(self):
+            w_obj = self.accept_obj_arg()
+            try:
+                return self.space.r_longlong_w(w_obj)
+            except OperationError, e:
+                return self.space.r_longlong_w(self._maybe_float(e, w_obj))
+
+        def accept_ulonglong_arg(self):
+            w_obj = self.accept_obj_arg()
+            try:
+                return self.space.r_ulonglong_w(w_obj)
+            except OperationError, e:
+                return self.space.r_ulonglong_w(self._maybe_float(e, w_obj))
+
+        def _maybe_float(self, e, w_obj):
+            space = self.space
+            if not e.match(space, space.w_TypeError):
+                raise e
+            if not space.is_true(space.isinstance(w_obj, space.w_float)):
+                raise e
+            return space.int(w_obj)   # wrapped float -> wrapped int or long
+
+    else:
+        # strict version
+
+        def accept_int_arg(self):
+            w_obj = self.accept_obj_arg()
+            return self.space.int_w(w_obj)
+
+        def accept_uint_arg(self):
+            w_obj = self.accept_obj_arg()
+            return self.space.uint_w(w_obj)
+
+        def accept_longlong_arg(self):
+            w_obj = self.accept_obj_arg()
+            return self.space.r_longlong_w(w_obj)
+
+        def accept_ulonglong_arg(self):
+            w_obj = self.accept_obj_arg()
+            return self.space.r_ulonglong_w(w_obj)
 
     def accept_str_arg(self):
         w_obj = self.accept_obj_arg()

Modified: pypy/dist/pypy/module/struct/nativefmttable.py
==============================================================================
--- pypy/dist/pypy/module/struct/nativefmttable.py	(original)
+++ pypy/dist/pypy/module/struct/nativefmttable.py	Fri Sep 28 10:36:52 2007
@@ -112,7 +112,8 @@
             pack = pack_double
             unpack = unpack_double
         else:
-            pack = std.make_int_packer(size, signed)
+            cpython_checks_range = fmtchar in 'bBhH'
+            pack = std.make_int_packer(size, signed, cpython_checks_range)
             unpack = std.make_int_unpacker(size, signed)
 
         native_fmttable[fmtchar] = {'size': size,

Modified: pypy/dist/pypy/module/struct/standardfmttable.py
==============================================================================
--- pypy/dist/pypy/module/struct/standardfmttable.py	(original)
+++ pypy/dist/pypy/module/struct/standardfmttable.py	Fri Sep 28 10:36:52 2007
@@ -11,6 +11,12 @@
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
 
+# In the CPython struct module, pack() unconsistently accepts inputs
+# that are out-of-range or floats instead of ints.  Should we emulate
+# this?  Let's use a flag for now:
+
+PACK_ACCEPTS_BROKEN_INPUT = True
+
 # ____________________________________________________________
 
 def pack_pad(fmtiter, count):
@@ -59,11 +65,17 @@
 
 native_int_size = struct.calcsize("l")
 
-def make_int_packer(size, signed, _memo={}):
+def make_int_packer(size, signed, cpython_checks_range, _memo={}):
+    if cpython_checks_range:
+        check_range = True
+    else:
+        check_range = not PACK_ACCEPTS_BROKEN_INPUT
+    key = (size, signed, check_range)
     try:
-        return _memo[size, signed]
+        return _memo[key]
     except KeyError:
         pass
+
     if signed:
         min = -(2 ** (8*size-1))
         max = (2 ** (8*size-1)) - 1
@@ -99,8 +111,9 @@
     def pack_int(fmtiter):
         method = getattr(fmtiter, accept_method)
         value = method()
-        if value < min or value > max:
-            raise StructError(errormsg)
+        if check_range:
+            if value < min or value > max:
+                raise StructError(errormsg)
         if fmtiter.bigendian:
             for i in unroll_revrange_size:
                 x = (value >> (8*i)) & 0xff
@@ -110,7 +123,7 @@
                 fmtiter.result.append(chr(value & 0xff))
                 value >>= 8
 
-    _memo[size, signed] = pack_int
+    _memo[key] = pack_int
     return pack_int
 
 # ____________________________________________________________
@@ -201,8 +214,9 @@
 
 for c, size in [('b', 1), ('h', 2), ('i', 4), ('l', 4), ('q', 8)]:
     standard_fmttable[c] = {'size': size,
-                            'pack': make_int_packer(size, True),
+                            'pack': make_int_packer(size, True, False),
                             'unpack': make_int_unpacker(size, True)}
     standard_fmttable[c.upper()] = {'size': size,
-                                    'pack': make_int_packer(size, False),
+                                    'pack': make_int_packer(size, False,
+                                                            False),
                                     'unpack': make_int_unpacker(size, False)}

Modified: pypy/dist/pypy/module/struct/test/test_struct.py
==============================================================================
--- pypy/dist/pypy/module/struct/test/test_struct.py	(original)
+++ pypy/dist/pypy/module/struct/test/test_struct.py	Fri Sep 28 10:36:52 2007
@@ -310,3 +310,15 @@
         raises(someerror, calcsize, "%dcc" % (sys.maxint,))
         raises(someerror, calcsize, "c%dc" % (sys.maxint,))
         raises(someerror, calcsize, "%dci" % (sys.maxint,))
+
+
+    def test_broken_input(self):
+        """
+        For compatibility: check that we also accept inputs that are
+        wrongly accepted by CPython 2.4.
+        """
+        pack = self.struct.pack
+        assert pack("!b", 0xa0) == '\xa0'
+        assert pack("!B", -1.1) == '\xff'
+        assert pack("!h", 0xa000) == '\xa0\x00'
+        assert pack("!H", -2.2) == '\xff\xfe'



More information about the Pypy-commit mailing list