diff options
Diffstat (limited to 'Lib/test/test_struct.py')
-rw-r--r-- | Lib/test/test_struct.py | 350 |
1 files changed, 181 insertions, 169 deletions
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index c8dc6f17042..dcc73ab8983 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -1,18 +1,16 @@ -import os import array import unittest import struct -import inspect -from test import test_support as support -from test.test_support import (check_warnings, check_py3k_warnings) - import sys + +from test import support + ISBIGENDIAN = sys.byteorder == "big" IS32BIT = sys.maxsize == 0x7fffffff integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q' +byteorders = '', '@', '=', '<', '>', '!' -testmod_filename = os.path.splitext(__file__)[0] + '.py' # Native 'q' packing isn't available on systems that don't have the C # long long type. try: @@ -23,7 +21,7 @@ else: HAVE_LONG_LONG = True def string_reverse(s): - return "".join(reversed(s)) + return s[::-1] def bigendian_to_native(value): if ISBIGENDIAN: @@ -33,21 +31,8 @@ def bigendian_to_native(value): class StructTest(unittest.TestCase): - def check_float_coerce(self, format, number): - # SF bug 1530559. struct.pack raises TypeError where it used - # to convert. - with check_warnings((".*integer argument expected, got float", - DeprecationWarning)) as w: - got = struct.pack(format, number) - lineno = inspect.currentframe().f_lineno - 1 - self.assertEqual(w.filename, testmod_filename) - self.assertEqual(w.lineno, lineno) - self.assertEqual(len(w.warnings), 1) - expected = struct.pack(format, int(number)) - self.assertEqual(got, expected) - def test_isbigendian(self): - self.assertEqual((struct.pack('=i', 1)[0] == chr(0)), ISBIGENDIAN) + self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN) def test_consistence(self): self.assertRaises(struct.error, struct.calcsize, 'Z') @@ -65,13 +50,13 @@ class StructTest(unittest.TestCase): self.assertRaises(struct.error, struct.pack, 'i', 3, 3, 3) self.assertRaises((TypeError, struct.error), struct.pack, 'i', 'foo') self.assertRaises((TypeError, struct.error), struct.pack, 'P', 'foo') - self.assertRaises(struct.error, struct.unpack, 'd', 'flap') + self.assertRaises(struct.error, struct.unpack, 'd', b'flap') s = struct.pack('ii', 1, 2) self.assertRaises(struct.error, struct.unpack, 'iii', s) self.assertRaises(struct.error, struct.unpack, 'i', s) def test_transitiveness(self): - c = 'a' + c = b'a' b = 1 h = 255 i = 65535 @@ -98,43 +83,43 @@ class StructTest(unittest.TestCase): # Test some of the new features in detail # (format, argument, big-endian result, little-endian result, asymmetric) tests = [ - ('c', 'a', 'a', 'a', 0), - ('xc', 'a', '\0a', '\0a', 0), - ('cx', 'a', 'a\0', 'a\0', 0), - ('s', 'a', 'a', 'a', 0), - ('0s', 'helloworld', '', '', 1), - ('1s', 'helloworld', 'h', 'h', 1), - ('9s', 'helloworld', 'helloworl', 'helloworl', 1), - ('10s', 'helloworld', 'helloworld', 'helloworld', 0), - ('11s', 'helloworld', 'helloworld\0', 'helloworld\0', 1), - ('20s', 'helloworld', 'helloworld'+10*'\0', 'helloworld'+10*'\0', 1), - ('b', 7, '\7', '\7', 0), - ('b', -7, '\371', '\371', 0), - ('B', 7, '\7', '\7', 0), - ('B', 249, '\371', '\371', 0), - ('h', 700, '\002\274', '\274\002', 0), - ('h', -700, '\375D', 'D\375', 0), - ('H', 700, '\002\274', '\274\002', 0), - ('H', 0x10000-700, '\375D', 'D\375', 0), - ('i', 70000000, '\004,\035\200', '\200\035,\004', 0), - ('i', -70000000, '\373\323\342\200', '\200\342\323\373', 0), - ('I', 70000000L, '\004,\035\200', '\200\035,\004', 0), - ('I', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0), - ('l', 70000000, '\004,\035\200', '\200\035,\004', 0), - ('l', -70000000, '\373\323\342\200', '\200\342\323\373', 0), - ('L', 70000000L, '\004,\035\200', '\200\035,\004', 0), - ('L', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0), - ('f', 2.0, '@\000\000\000', '\000\000\000@', 0), - ('d', 2.0, '@\000\000\000\000\000\000\000', - '\000\000\000\000\000\000\000@', 0), - ('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0), - ('d', -2.0, '\300\000\000\000\000\000\000\000', - '\000\000\000\000\000\000\000\300', 0), - ('?', 0, '\0', '\0', 0), - ('?', 3, '\1', '\1', 1), - ('?', True, '\1', '\1', 0), - ('?', [], '\0', '\0', 1), - ('?', (1,), '\1', '\1', 1), + ('c', b'a', b'a', b'a', 0), + ('xc', b'a', b'\0a', b'\0a', 0), + ('cx', b'a', b'a\0', b'a\0', 0), + ('s', b'a', b'a', b'a', 0), + ('0s', b'helloworld', b'', b'', 1), + ('1s', b'helloworld', b'h', b'h', 1), + ('9s', b'helloworld', b'helloworl', b'helloworl', 1), + ('10s', b'helloworld', b'helloworld', b'helloworld', 0), + ('11s', b'helloworld', b'helloworld\0', b'helloworld\0', 1), + ('20s', b'helloworld', b'helloworld'+10*b'\0', b'helloworld'+10*b'\0', 1), + ('b', 7, b'\7', b'\7', 0), + ('b', -7, b'\371', b'\371', 0), + ('B', 7, b'\7', b'\7', 0), + ('B', 249, b'\371', b'\371', 0), + ('h', 700, b'\002\274', b'\274\002', 0), + ('h', -700, b'\375D', b'D\375', 0), + ('H', 700, b'\002\274', b'\274\002', 0), + ('H', 0x10000-700, b'\375D', b'D\375', 0), + ('i', 70000000, b'\004,\035\200', b'\200\035,\004', 0), + ('i', -70000000, b'\373\323\342\200', b'\200\342\323\373', 0), + ('I', 70000000, b'\004,\035\200', b'\200\035,\004', 0), + ('I', 0x100000000-70000000, b'\373\323\342\200', b'\200\342\323\373', 0), + ('l', 70000000, b'\004,\035\200', b'\200\035,\004', 0), + ('l', -70000000, b'\373\323\342\200', b'\200\342\323\373', 0), + ('L', 70000000, b'\004,\035\200', b'\200\035,\004', 0), + ('L', 0x100000000-70000000, b'\373\323\342\200', b'\200\342\323\373', 0), + ('f', 2.0, b'@\000\000\000', b'\000\000\000@', 0), + ('d', 2.0, b'@\000\000\000\000\000\000\000', + b'\000\000\000\000\000\000\000@', 0), + ('f', -2.0, b'\300\000\000\000', b'\000\000\000\300', 0), + ('d', -2.0, b'\300\000\000\000\000\000\000\000', + b'\000\000\000\000\000\000\000\300', 0), + ('?', 0, b'\0', b'\0', 0), + ('?', 3, b'\1', b'\1', 1), + ('?', True, b'\1', b'\1', 0), + ('?', [], b'\0', b'\0', 1), + ('?', (1,), b'\1', b'\1', 1), ] for fmt, arg, big, lil, asy in tests: @@ -158,14 +143,17 @@ class StructTest(unittest.TestCase): # standard integer sizes for code in integer_codes: - for byteorder in ('=', '<', '>', '!'): + for byteorder in '=', '<', '>', '!': format = byteorder+code size = struct.calcsize(format) self.assertEqual(size, expected_size[code]) - # native integer sizes, except 'q' and 'Q' - for format_pair in ('bB', 'hH', 'iI', 'lL'): - for byteorder in ['', '@']: + # native integer sizes + native_pairs = 'bB', 'hH', 'iI', 'lL' + if HAVE_LONG_LONG: + native_pairs += 'qQ', + for format_pair in native_pairs: + for byteorder in '', '@': signed_size = struct.calcsize(byteorder + format_pair[0]) unsigned_size = struct.calcsize(byteorder + format_pair[1]) self.assertEqual(signed_size, unsigned_size) @@ -176,10 +164,7 @@ class StructTest(unittest.TestCase): self.assertLessEqual(4, struct.calcsize('l')) self.assertLessEqual(struct.calcsize('h'), struct.calcsize('i')) self.assertLessEqual(struct.calcsize('i'), struct.calcsize('l')) - - # tests for native 'q' and 'Q' when applicable if HAVE_LONG_LONG: - self.assertEqual(struct.calcsize('q'), struct.calcsize('Q')) self.assertLessEqual(8, struct.calcsize('q')) self.assertLessEqual(struct.calcsize('l'), struct.calcsize('q')) @@ -192,20 +177,20 @@ class StructTest(unittest.TestCase): super(IntTester, self).__init__(methodName='test_one') self.format = format self.code = format[-1] - self.direction = format[:-1] - if not self.direction in ('', '@', '=', '<', '>', '!'): - raise ValueError("unrecognized packing direction: %s" % - self.direction) + self.byteorder = format[:-1] + if not self.byteorder in byteorders: + raise ValueError("unrecognized packing byteorder: %s" % + self.byteorder) self.bytesize = struct.calcsize(format) self.bitsize = self.bytesize * 8 if self.code in tuple('bhilq'): self.signed = True - self.min_value = -(2L**(self.bitsize-1)) - self.max_value = 2L**(self.bitsize-1) - 1 + self.min_value = -(2**(self.bitsize-1)) + self.max_value = 2**(self.bitsize-1) - 1 elif self.code in tuple('BHILQ'): self.signed = False self.min_value = 0 - self.max_value = 2L**self.bitsize - 1 + self.max_value = 2**self.bitsize - 1 else: raise ValueError("unrecognized format code: %s" % self.code) @@ -216,18 +201,19 @@ class StructTest(unittest.TestCase): format = self.format if self.min_value <= x <= self.max_value: - expected = long(x) + expected = x if self.signed and x < 0: - expected += 1L << self.bitsize + expected += 1 << self.bitsize self.assertGreaterEqual(expected, 0) expected = '%x' % expected if len(expected) & 1: expected = "0" + expected + expected = expected.encode('ascii') expected = unhexlify(expected) - expected = ("\x00" * (self.bytesize - len(expected)) + + expected = (b"\x00" * (self.bytesize - len(expected)) + expected) - if (self.direction == '<' or - self.direction in ('', '@', '=') and not ISBIGENDIAN): + if (self.byteorder == '<' or + self.byteorder in ('', '@', '=') and not ISBIGENDIAN): expected = string_reverse(expected) self.assertEqual(len(expected), self.bytesize) @@ -241,7 +227,7 @@ class StructTest(unittest.TestCase): # Adding any byte should cause a "too big" error. self.assertRaises((struct.error, TypeError), unpack, format, - '\x01' + got) + b'\x01' + got) else: # x is out of range -- verify pack realizes that. self.assertRaises((OverflowError, ValueError, struct.error), @@ -253,17 +239,17 @@ class StructTest(unittest.TestCase): # Create all interesting powers of 2. values = [] for exp in range(self.bitsize + 3): - values.append(1L << exp) + values.append(1 << exp) # Add some random values. for i in range(self.bitsize): - val = 0L + val = 0 for j in range(self.bytesize): val = (val << 8) | randrange(256) values.append(val) # Values absorbed from other tests - values.extend([300, 700000, sys.maxint*4]) + values.extend([300, 700000, sys.maxsize*4]) # Try all those, and their negations, and +-1 from # them. Note that this tests all power-of-2 @@ -273,24 +259,13 @@ class StructTest(unittest.TestCase): for val in -base, base: for incr in -1, 0, 1: x = val + incr - self.test_one(int(x)) - self.test_one(long(x)) + self.test_one(x) # Some error cases. - class NotAnIntNS(object): + class NotAnInt: def __int__(self): return 42 - def __long__(self): - return 1729L - - class NotAnIntOS: - def __int__(self): - return 85 - - def __long__(self): - return -163L - # Objects with an '__index__' method should be allowed # to pack as integers. That is assuming the implemented # '__index__' method returns and 'int' or 'long'. @@ -316,24 +291,15 @@ class StructTest(unittest.TestCase): self.assertRaises((TypeError, struct.error), struct.pack, self.format, randrange) - with check_warnings(("integer argument expected, " - "got non-integer", DeprecationWarning)): - with self.assertRaises((TypeError, struct.error)): - struct.pack(self.format, 3+42j) - - # an attempt to convert a non-integer (with an - # implicit conversion via __int__) should succeed, - # with a DeprecationWarning - for nonint in NotAnIntNS(), NotAnIntOS(), BadIndex(): - with check_warnings((".*integer argument expected, got non" - "-integer", DeprecationWarning)) as w: - got = struct.pack(self.format, nonint) - lineno = inspect.currentframe().f_lineno - 1 - self.assertEqual(w.filename, testmod_filename) - self.assertEqual(w.lineno, lineno) - self.assertEqual(len(w.warnings), 1) - expected = struct.pack(self.format, int(nonint)) - self.assertEqual(got, expected) + self.assertRaises((TypeError, struct.error), + struct.pack, self.format, + 3+42j) + self.assertRaises((TypeError, struct.error), + struct.pack, self.format, + NotAnInt()) + self.assertRaises((TypeError, struct.error), + struct.pack, self.format, + BadIndex()) # Check for legitimate values from '__index__'. for obj in (Indexable(0), Indexable(10), Indexable(17), @@ -345,13 +311,12 @@ class StructTest(unittest.TestCase): "with '__index__' method") # Check for bogus values from '__index__'. - for obj in (Indexable('a'), Indexable(u'b'), Indexable(None), + for obj in (Indexable(b'a'), Indexable('b'), Indexable(None), Indexable({'a': 1}), Indexable([1, 2, 3])): self.assertRaises((TypeError, struct.error), struct.pack, self.format, obj) - byteorders = '', '@', '=', '<', '>', '!' for code in integer_codes: for byteorder in byteorders: if (byteorder in ('', '@') and code in ('q', 'Q') and @@ -364,14 +329,14 @@ class StructTest(unittest.TestCase): def test_p_code(self): # Test p ("Pascal string") code. for code, input, expected, expectedback in [ - ('p','abc', '\x00', ''), - ('1p', 'abc', '\x00', ''), - ('2p', 'abc', '\x01a', 'a'), - ('3p', 'abc', '\x02ab', 'ab'), - ('4p', 'abc', '\x03abc', 'abc'), - ('5p', 'abc', '\x03abc\x00', 'abc'), - ('6p', 'abc', '\x03abc\x00\x00', 'abc'), - ('1000p', 'x'*1000, '\xff' + 'x'*999, 'x'*255)]: + ('p', b'abc', b'\x00', b''), + ('1p', b'abc', b'\x00', b''), + ('2p', b'abc', b'\x01a', b'a'), + ('3p', b'abc', b'\x02ab', b'ab'), + ('4p', b'abc', b'\x03abc', b'abc'), + ('5p', b'abc', b'\x03abc\x00', b'abc'), + ('6p', b'abc', b'\x03abc\x00\x00', b'abc'), + ('1000p', b'x'*1000, b'\xff' + b'x'*999, b'x'*255)]: got = struct.pack(code, input) self.assertEqual(got, expected) (got,) = struct.unpack(code, got) @@ -413,44 +378,58 @@ class StructTest(unittest.TestCase): self.assertRaises(OverflowError, struct.pack, ">f", big) def test_1530559(self): - # SF bug 1530559. struct.pack raises TypeError where it used to convert. - for endian in ('', '>', '<'): - for fmt in integer_codes: - self.check_float_coerce(endian + fmt, 1.0) - self.check_float_coerce(endian + fmt, 1.5) - - def test_unpack_from(self, cls=str): - data = cls('abcd01234') + for byteorder in '', '@', '=', '<', '>', '!': + for code in integer_codes: + if (byteorder in ('', '@') and code in ('q', 'Q') and + not HAVE_LONG_LONG): + continue + format = byteorder + code + self.assertRaises(struct.error, struct.pack, format, 1.0) + self.assertRaises(struct.error, struct.pack, format, 1.5) + self.assertRaises(struct.error, struct.pack, 'P', 1.0) + self.assertRaises(struct.error, struct.pack, 'P', 1.5) + + def test_unpack_from(self): + test_string = b'abcd01234' fmt = '4s' s = struct.Struct(fmt) - - self.assertEqual(s.unpack_from(data), ('abcd',)) - self.assertEqual(struct.unpack_from(fmt, data), ('abcd',)) - for i in xrange(6): - self.assertEqual(s.unpack_from(data, i), (data[i:i+4],)) - self.assertEqual(struct.unpack_from(fmt, data, i), (data[i:i+4],)) - for i in xrange(6, len(data) + 1): - self.assertRaises(struct.error, s.unpack_from, data, i) - self.assertRaises(struct.error, struct.unpack_from, fmt, data, i) + for cls in (bytes, bytearray): + data = cls(test_string) + self.assertEqual(s.unpack_from(data), (b'abcd',)) + self.assertEqual(s.unpack_from(data, 2), (b'cd01',)) + self.assertEqual(s.unpack_from(data, 4), (b'0123',)) + for i in range(6): + self.assertEqual(s.unpack_from(data, i), (data[i:i+4],)) + for i in range(6, len(test_string) + 1): + self.assertRaises(struct.error, s.unpack_from, data, i) + for cls in (bytes, bytearray): + data = cls(test_string) + self.assertEqual(struct.unpack_from(fmt, data), (b'abcd',)) + self.assertEqual(struct.unpack_from(fmt, data, 2), (b'cd01',)) + self.assertEqual(struct.unpack_from(fmt, data, 4), (b'0123',)) + for i in range(6): + self.assertEqual(struct.unpack_from(fmt, data, i), (data[i:i+4],)) + for i in range(6, len(test_string) + 1): + self.assertRaises(struct.error, struct.unpack_from, fmt, data, i) def test_pack_into(self): - test_string = 'Reykjavik rocks, eow!' - writable_buf = array.array('c', ' '*100) + test_string = b'Reykjavik rocks, eow!' + writable_buf = array.array('b', b' '*100) fmt = '21s' s = struct.Struct(fmt) # Test without offset s.pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] + from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. s.pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] + from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) # Go beyond boundaries. - small_buf = array.array('c', ' '*10) + small_buf = array.array('b', b' '*10) self.assertRaises((ValueError, struct.error), s.pack_into, small_buf, 0, test_string) self.assertRaises((ValueError, struct.error), s.pack_into, small_buf, 2, @@ -462,47 +441,43 @@ class StructTest(unittest.TestCase): None) def test_pack_into_fn(self): - test_string = 'Reykjavik rocks, eow!' - writable_buf = array.array('c', ' '*100) + test_string = b'Reykjavik rocks, eow!' + writable_buf = array.array('b', b' '*100) fmt = '21s' pack_into = lambda *args: struct.pack_into(fmt, *args) # Test without offset. pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] + from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] + from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) # Go beyond boundaries. - small_buf = array.array('c', ' '*10) + small_buf = array.array('b', b' '*10) self.assertRaises((ValueError, struct.error), pack_into, small_buf, 0, test_string) self.assertRaises((ValueError, struct.error), pack_into, small_buf, 2, test_string) def test_unpack_with_buffer(self): - with check_py3k_warnings(("buffer.. not supported in 3.x", - DeprecationWarning)): - # SF bug 1563759: struct.unpack doesn't support buffer protocol objects - data1 = array.array('B', '\x12\x34\x56\x78') - data2 = buffer('......\x12\x34\x56\x78......', 6, 4) - for data in [data1, data2]: - value, = struct.unpack('>I', data) - self.assertEqual(value, 0x12345678) - - self.test_unpack_from(cls=buffer) + # SF bug 1563759: struct.unpack doesn't support buffer protocol objects + data1 = array.array('B', b'\x12\x34\x56\x78') + data2 = memoryview(b'\x12\x34\x56\x78') # XXX b'......XXXX......', 6, 4 + for data in [data1, data2]: + value, = struct.unpack('>I', data) + self.assertEqual(value, 0x12345678) def test_bool(self): class ExplodingBool(object): - def __nonzero__(self): + def __bool__(self): raise IOError for prefix in tuple("<>!=")+('',): false = (), [], [], '', 0 - true = [1], 'test', 5, -1, 0xffffffffL+1, 0xffffffff//2 + true = [1], 'test', 5, -1, 0xffffffff+1, 0xffffffff/2 falseFormat = prefix + '?' * len(false) packedFalse = struct.pack(falseFormat, *false) @@ -528,16 +503,17 @@ class StructTest(unittest.TestCase): self.assertFalse(prefix, msg='encoded bool is not one byte: %r' %packed) - self.assertRaises(IOError, struct.pack, prefix + '?', - ExplodingBool()) + try: + struct.pack(prefix + '?', ExplodingBool()) + except IOError: + pass + else: + self.fail("Expected IOError: struct.pack(%r, " + "ExplodingBool())" % (prefix + '?')) for c in [b'\x01', b'\x7f', b'\xff', b'\x0f', b'\xf0']: self.assertTrue(struct.unpack('>?', c)[0]) - @unittest.skipUnless(IS32BIT, "Specific to 32bit machines") - def test_crasher(self): - self.assertRaises(MemoryError, struct.pack, "357913941c", "a") - def test_count_overflow(self): hugecount = '{}b'.format(sys.maxsize+1) self.assertRaises(struct.error, struct.calcsize, hugecount) @@ -545,6 +521,42 @@ class StructTest(unittest.TestCase): hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2) self.assertRaises(struct.error, struct.calcsize, hugecount2) + if IS32BIT: + def test_crasher(self): + self.assertRaises(MemoryError, struct.pack, "357913941b", "a") + + def test_trailing_counter(self): + store = array.array('b', b' '*100) + + # format lists containing only count spec should result in an error + self.assertRaises(struct.error, struct.pack, '12345') + self.assertRaises(struct.error, struct.unpack, '12345', '') + self.assertRaises(struct.error, struct.pack_into, '12345', store, 0) + self.assertRaises(struct.error, struct.unpack_from, '12345', store, 0) + + # Format lists with trailing count spec should result in an error + self.assertRaises(struct.error, struct.pack, 'c12345', 'x') + self.assertRaises(struct.error, struct.unpack, 'c12345', 'x') + self.assertRaises(struct.error, struct.pack_into, 'c12345', store, 0, + 'x') + self.assertRaises(struct.error, struct.unpack_from, 'c12345', store, + 0) + + # Mixed format tests + self.assertRaises(struct.error, struct.pack, '14s42', 'spam and eggs') + self.assertRaises(struct.error, struct.unpack, '14s42', + 'spam and eggs') + self.assertRaises(struct.error, struct.pack_into, '14s42', store, 0, + 'spam and eggs') + self.assertRaises(struct.error, struct.unpack_from, '14s42', store, 0) + + def test_Struct_reinitialization(self): + # Issue 9422: there was a memory leak when reinitializing a + # Struct instance. This test can be used to detect the leak + # when running with regrtest -L. + s = struct.Struct('i') + s.__init__('ii') + def check_sizeof(self, format_str, number_of_codes): # The size of 'PyStructObject' totalsize = support.calcobjsize('5P') |