diff options
-rw-r--r-- | Doc/library/ast.rst | 2 | ||||
-rw-r--r-- | Doc/library/ctypes.rst | 50 | ||||
-rw-r--r-- | Doc/library/numbers.rst | 8 | ||||
-rw-r--r-- | Doc/library/stdtypes.rst | 21 | ||||
-rw-r--r-- | Include/Python.h | 2 | ||||
-rw-r--r-- | Lib/test/test_heapq.py | 21 | ||||
-rw-r--r-- | Lib/test/test_set.py | 20 | ||||
-rw-r--r-- | Lib/test/test_struct.py | 1172 | ||||
-rw-r--r-- | Modules/_heapqmodule.c | 17 | ||||
-rw-r--r-- | Objects/setobject.c | 56 |
10 files changed, 669 insertions, 700 deletions
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 70840da..3c2f8d2 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -135,7 +135,7 @@ and classes for traversing abstract syntax trees: from untrusted sources without the need to parse the values oneself. -.. function:: get_docstring(node, clean=True): +.. function:: get_docstring(node, clean=True) Return the docstring of the given *node* (which must be a :class:`FunctionDef`, :class:`ClassDef` or :class:`Module` node), or ``None`` diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 61c1e61..51d340c 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1395,11 +1395,6 @@ GetLastError() and SetLastError() Windows api functions; to request and change the ctypes private copy of the windows error code. -.. versionchanged:: 2.6 - -The `use_errno` and `use_last_error` parameters were added in Python -2.6. - .. data:: RTLD_GLOBAL :noindex: @@ -1561,22 +1556,23 @@ They are instances of a private class: Assign a Python function or another callable to this attribute. The callable will be called with three or more arguments: + .. function:: callable(result, func, arguments) + :noindex: -.. function:: callable(result, func, arguments) - :noindex: + ``result`` is what the foreign function returns, as specified + by the :attr:`restype` attribute. - ``result`` is what the foreign function returns, as specified by the - :attr:`restype` attribute. + ``func`` is the foreign function object itself, this allows + to reuse the same callable object to check or post process + the results of several functions. - ``func`` is the foreign function object itself, this allows to reuse the same - callable object to check or post process the results of several functions. + ``arguments`` is a tuple containing the parameters originally + passed to the function call, this allows to specialize the + behavior on the arguments used. - ``arguments`` is a tuple containing the parameters originally passed to the - function call, this allows to specialize the behavior on the arguments used. - - The object that this function returns will be returned from the foreign - function call, but it can also check the result value and raise an exception - if the foreign function call failed. + The object that this function returns will be returned from the + foreign function call, but it can also check the result value + and raise an exception if the foreign function call failed. .. exception:: ArgumentError() @@ -1605,10 +1601,6 @@ type and the argument types of the function. variable is exchanged with the real `errno` value bafore and after the call; `use_last_error` does the same for the Windows error code. - .. versionchanged:: 2.6 - The optional `use_errno` and `use_last_error` parameters were - added. - .. function:: WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) @@ -1786,11 +1778,19 @@ Utility functions ctypes type or instance. -.. function:: byref(obj) +.. function:: byref(obj[, offset]) + + Returns a light-weight pointer to ``obj``, which must be an + instance of a ctypes type. ``offset`` defaults to zero, it must be + an integer which is added to the internal pointer value. + + ``byref(obj, offset)`` corresponds to this C code:: + + (((char *)&obj) + offset) - Returns a light-weight pointer to ``obj``, which must be an instance of a ctypes - type. The returned object can only be used as a foreign function call parameter. - It behaves similar to ``pointer(obj)``, but the construction is a lot faster. + The returned object can only be used as a foreign function call + parameter. It behaves similar to ``pointer(obj)``, but the + construction is a lot faster. .. function:: cast(obj, type) diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index c74bd47..ef4bd80 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -71,10 +71,10 @@ The numeric tower .. class:: Integral - Subtypes :class:`Rational` and adds a conversion to :class:`long`, the - 3-argument form of :func:`pow`, and the bit-string operations: ``<<``, - ``>>``, ``&``, ``^``, ``|``, ``~``. Provides defaults for :func:`float`, - :attr:`Rational.numerator`, and :attr:`Rational.denominator`. + Subtypes :class:`Rational` and adds a conversion to :class:`int`. + Provides defaults for :func:`float`, :attr:`Rational.numerator`, and + :attr:`Rational.denominator`, and bit-string operations: ``<<``, + ``>>``, ``&``, ``^``, ``|``, ``~``. Notes for type implementors diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 0dd520e..4cb14e7 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1517,21 +1517,15 @@ The constructors for both classes work the same: Return a new set with elements from both sets. - .. versionchanged:: 2.6 - Accepts multiple input iterables. - .. method:: intersection(other, ...) set & other & ... Return a new set with elements common to both sets. - .. versionchanged:: 2.6 - Accepts multiple input iterables. + .. method:: difference(other, ...) + set - other - ... - .. method:: difference(other) - set - other - - Return a new set with elements in the set that are not in *other*. + Return a new set with elements in the set that are not in the others. .. method:: symmetric_difference(other) set ^ other @@ -1595,10 +1589,13 @@ The constructors for both classes work the same: .. versionchanged:: 2.6 Accepts multiple input iterables. - .. method:: difference_update(other) - set -= other + .. method:: difference_update(other, ...) + set -= other | ... + + Update the set, removing elements found in others. - Update the set, removing elements found in *other*. + .. versionchanged:: 2.6 + Accepts multiple input iterables. .. method:: symmetric_difference_update(other) set ^= other diff --git a/Include/Python.h b/Include/Python.h index 539de51..61f8865 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -35,7 +35,7 @@ #include <unistd.h> #endif -/* For uintptr_t, intptr_t */ +/* For size_t? */ #ifdef HAVE_STDDEF_H #include <stddef.h> #endif diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py index e41abf0..1c7c97f 100644 --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -197,6 +197,27 @@ class TestHeapPython(TestHeap): class TestHeapC(TestHeap): module = c_heapq + def test_comparison_operator(self): + # Issue 3501: Make sure heapq works with both __lt__ and __le__ + def hsort(data, comp): + data = [comp(x) for x in data] + self.module.heapify(data) + return [self.module.heappop(data).x for i in range(len(data))] + class LT: + def __init__(self, x): + self.x = x + def __lt__(self, other): + return self.x > other.x + class LE: + def __init__(self, x): + self.x = x + def __lt__(self, other): + return self.x >= other.x + data = [random.random() for i in range(100)] + target = sorted(data, reverse=True) + self.assertEqual(hsort(data, LT), target) + self.assertEqual(hsort(data, LE), target) + #============================================================================== diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index ed2db18..f263a7d 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -105,6 +105,12 @@ class TestJointOps(unittest.TestCase): self.assertEqual(self.thetype('abcba').intersection(C('ccb')), set('bc')) self.assertEqual(self.thetype('abcba').intersection(C('ef')), set('')) self.assertEqual(self.thetype('abcba').intersection(C('cbcf'), C('bag')), set('b')) + s = self.thetype('abcba') + z = s.intersection() + if self.thetype == frozenset(): + self.assertEqual(id(s), id(z)) + else: + self.assertNotEqual(id(s), id(z)) def test_isdisjoint(self): def f(s1, s2): @@ -144,6 +150,8 @@ class TestJointOps(unittest.TestCase): self.assertEqual(self.thetype('abcba').difference(C('efgfe')), set('abc')) self.assertEqual(self.thetype('abcba').difference(C('ccb')), set('a')) self.assertEqual(self.thetype('abcba').difference(C('ef')), set('abc')) + self.assertEqual(self.thetype('abcba').difference(), set('abc')) + self.assertEqual(self.thetype('abcba').difference(C('a'), C('b')), set('c')) def test_sub(self): i = self.s.difference(self.otherword) @@ -470,6 +478,18 @@ class TestSet(TestJointOps): self.assertEqual(s.difference_update(C(p)), None) self.assertEqual(s, set(q)) + s = self.thetype('abcdefghih') + s.difference_update() + self.assertEqual(s, self.thetype('abcdefghih')) + + s = self.thetype('abcdefghih') + s.difference_update(C('aba')) + self.assertEqual(s, self.thetype('cdefghih')) + + s = self.thetype('abcdefghih') + s.difference_update(C('cdc'), C('aba')) + self.assertEqual(s, self.thetype('efghih')) + def test_isub(self): self.s -= set(self.otherword) for c in (self.word + self.otherword): diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 05ac760..917f626 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -1,14 +1,14 @@ -from test.support import TestFailed, verbose, verify, vereq -import test.support -import struct import array +import unittest +import struct import warnings +from functools import wraps +from test.support import TestFailed, verbose, run_unittest, catch_warning + import sys ISBIGENDIAN = sys.byteorder == "big" del sys -verify((struct.pack('=i', 1)[0] == 0) == ISBIGENDIAN, - "bigendian determination appears wrong") try: import _struct @@ -30,39 +30,21 @@ def bigendian_to_native(value): else: return string_reverse(value) -def simple_err(func, *args): - try: - func(*args) - except struct.error: - pass - else: - raise TestFailed("%s%s did not raise struct.error" % ( - func.__name__, args)) - -def any_err(func, *args): - try: - func(*args) - except (struct.error, TypeError): - pass - else: - raise TestFailed("%s%s did not raise error" % ( - func.__name__, args)) - def with_warning_restore(func): - def _with_warning_restore(*args, **kw): - with test.support.catch_warning(): + @wraps(func) + def decorator(*args, **kw): + with catch_warning(): # Grrr, we need this function to warn every time. Without removing # the warningregistry, running test_tarfile then test_struct would fail # on 64-bit platforms. globals = func.__globals__ if '__warningregistry__' in globals: del globals['__warningregistry__'] - warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning) - warnings.filterwarnings("error", r""".*format requires.*""", - DeprecationWarning) + warnings.filterwarnings("error", category=DeprecationWarning) return func(*args, **kw) - return _with_warning_restore + return decorator +@with_warning_restore def deprecated_err(func, *args): try: func(*args) @@ -75,615 +57,531 @@ def deprecated_err(func, *args): else: raise TestFailed("%s%s did not raise error" % ( func.__name__, args)) -deprecated_err = with_warning_restore(deprecated_err) - - -simple_err(struct.calcsize, 'Z') - -sz = struct.calcsize('i') -if sz * 3 != struct.calcsize('iii'): - raise TestFailed('inconsistent sizes') - -fmt = 'cbxxxxxxhhhhiillffd?' -fmt3 = '3c3b18x12h6i6l6f3d3?' -sz = struct.calcsize(fmt) -sz3 = struct.calcsize(fmt3) -if sz * 3 != sz3: - raise TestFailed('inconsistent sizes (3*%r -> 3*%d = %d, %r -> %d)' % ( - fmt, sz, 3*sz, fmt3, sz3)) - -simple_err(struct.pack, 'iii', 3) -simple_err(struct.pack, 'i', 3, 3, 3) -simple_err(struct.pack, 'i', 'foo') -simple_err(struct.pack, 'P', 'foo') -simple_err(struct.unpack, 'd', b'flap') -s = struct.pack('ii', 1, 2) -simple_err(struct.unpack, 'iii', s) -simple_err(struct.unpack, 'i', s) - -c = b'a' -b = 1 -h = 255 -i = 65535 -l = 65536 -f = 3.1415 -d = 3.1415 -t = True - -for prefix in ('', '@', '<', '>', '=', '!'): - for format in ('xcbhilfd?', 'xcBHILfd?'): - format = prefix + format - if verbose: - print("trying:", format) - s = struct.pack(format, c, b, h, i, l, f, d, t) - cp, bp, hp, ip, lp, fp, dp, tp = struct.unpack(format, s) - if (cp != c or bp != b or hp != h or ip != i or lp != l or - int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d) or - tp != t): - # ^^^ calculate only to two decimal places - raise TestFailed("unpack/pack not transitive (%s, %s)" % ( - str(format), str((cp, bp, hp, ip, lp, fp, dp, tp)))) - -# 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', 70000000, '\004,\035\200', '\200\035,\004', 0), - ('I', 0x100000000-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', 70000000, '\004,\035\200', '\200\035,\004', 0), - ('L', 0x100000000-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), -] - -for fmt, arg, big, lil, asy in tests: - big = bytes(big, "latin-1") - lil = bytes(lil, "latin-1") - if verbose: - print("%r %r %r %r" % (fmt, arg, big, lil)) - for (xfmt, exp) in [('>'+fmt, big), ('!'+fmt, big), ('<'+fmt, lil), - ('='+fmt, ISBIGENDIAN and big or lil)]: - res = struct.pack(xfmt, arg) - if res != exp: - raise TestFailed("pack(%r, %r) -> %r # expected %r" % ( - fmt, arg, res, exp)) - n = struct.calcsize(xfmt) - if n != len(res): - raise TestFailed("calcsize(%r) -> %d # expected %d" % ( - xfmt, n, len(res))) - rev = struct.unpack(xfmt, res)[0] - if isinstance(arg, str): - # Strings are returned as bytes since you can't know the encoding of - # the string when packed. - arg = bytes(arg, 'latin1') - if rev != arg and not asy: - raise TestFailed("unpack(%r, %r) -> (%r,) # expected (%r,)" % ( - fmt, res, rev, arg)) - -########################################################################### -# Simple native q/Q tests. - -has_native_qQ = 1 -try: - struct.pack("q", 5) -except struct.error: - has_native_qQ = 0 - -if verbose: - print("Platform has native q/Q?", has_native_qQ and "Yes." or "No.") - -any_err(struct.pack, "Q", -1) # can't pack -1 as unsigned regardless -simple_err(struct.pack, "q", "a") # can't pack string as 'q' regardless -simple_err(struct.pack, "Q", "a") # ditto, but 'Q' - -def test_native_qQ(): - nbytes = struct.calcsize('q') - # The expected values here are in big-endian format, primarily because - # I'm on a little-endian machine and so this is the clearest way (for - # me) to force the code to get exercised. - for format, input, expected in ( - ('q', -1, '\xff' * nbytes), - ('q', 0, '\x00' * nbytes), - ('Q', 0, '\x00' * nbytes), - ('q', 1, '\x00' * (nbytes-1) + '\x01'), - ('Q', (1 << (8*nbytes))-1, '\xff' * nbytes), - ('q', (1 << (8*nbytes-1))-1, '\x7f' + '\xff' * (nbytes - 1))): - expected = bytes(expected, "latin-1") - got = struct.pack(format, input) - native_expected = bigendian_to_native(expected) - verify(got == native_expected, - "%r-pack of %r gave %r, not %r" % - (format, input, got, native_expected)) - retrieved = struct.unpack(format, got)[0] - verify(retrieved == input, - "%r-unpack of %r gave %r, not %r" % - (format, got, retrieved, input)) - -if has_native_qQ: - test_native_qQ() - -########################################################################### -# Standard integer tests (bBhHiIlLqQ). - -import binascii - -class IntTester: - - # XXX Most std integer modes fail to test for out-of-range. - # The "i" and "l" codes appear to range-check OK on 32-bit boxes, but - # fail to check correctly on some 64-bit ones (Tru64 Unix + Compaq C - # reported by Mark Favas). - BUGGY_RANGE_CHECK = "bBhHiIlL" - - def __init__(self, formatpair, bytesize): - assert len(formatpair) == 2 - self.formatpair = formatpair - for direction in "<>!=": - for code in formatpair: - format = direction + code - verify(struct.calcsize(format) == bytesize) - self.bytesize = bytesize - self.bitsize = bytesize * 8 - self.signed_code, self.unsigned_code = formatpair - self.unsigned_min = 0 - self.unsigned_max = 2**self.bitsize - 1 - self.signed_min = -(2**(self.bitsize-1)) - self.signed_max = 2**(self.bitsize-1) - 1 - - def test_one(self, x, pack=struct.pack, - unpack=struct.unpack, - unhexlify=binascii.unhexlify): - if verbose: - print("trying std", self.formatpair, "on", x, "==", hex(x)) - - # Try signed. - code = self.signed_code - if self.signed_min <= x <= self.signed_max: - # Try big-endian. - expected = int(x) - if x < 0: - expected += 1 << self.bitsize - assert expected > 0 - expected = hex(expected)[2:] # chop "0x" - if len(expected) & 1: - expected = "0" + expected - expected = unhexlify(expected) - expected = b"\x00" * (self.bytesize - len(expected)) + expected - - # Pack work? - format = ">" + code - got = pack(format, x) - verify(got == expected, - "'%s'-pack of %r gave %r, not %r" % - (format, x, got, expected)) - - # Unpack work? - retrieved = unpack(format, got)[0] - verify(x == retrieved, - "'%s'-unpack of %r gave %r, not %r" % - (format, got, retrieved, x)) - - # Adding any byte should cause a "too big" error. - any_err(unpack, format, b'\x01' + got) - - # Try little-endian. - format = "<" + code - expected = string_reverse(expected) - - # Pack work? - got = pack(format, x) - verify(got == expected, - "'%s'-pack of %r gave %r, not %r" % - (format, x, got, expected)) - - # Unpack work? - retrieved = unpack(format, got)[0] - verify(x == retrieved, - "'%s'-unpack of %r gave %r, not %r" % - (format, got, retrieved, x)) - - # Adding any byte should cause a "too big" error. - any_err(unpack, format, b'\x01' + got) - else: - # x is out of range -- verify pack realizes that. - if not PY_STRUCT_RANGE_CHECKING and code in self.BUGGY_RANGE_CHECK: - if verbose: - print("Skipping buggy range check for code", code) - else: - deprecated_err(pack, ">" + code, x) - deprecated_err(pack, "<" + code, x) - - # Much the same for unsigned. - code = self.unsigned_code - if self.unsigned_min <= x <= self.unsigned_max: - # Try big-endian. - format = ">" + code - expected = int(x) - expected = hex(expected)[2:] # chop "0x" - if len(expected) & 1: - expected = "0" + expected - expected = unhexlify(expected) - expected = b"\x00" * (self.bytesize - len(expected)) + expected - - # Pack work? - got = pack(format, x) - verify(got == expected, - "'%s'-pack of %r gave %r, not %r" % - (format, x, got, expected)) - - # Unpack work? - retrieved = unpack(format, got)[0] - verify(x == retrieved, - "'%s'-unpack of %r gave %r, not %r" % - (format, got, retrieved, x)) - - # Adding any byte should cause a "too big" error. - any_err(unpack, format, b'\x01' + got) - - # Try little-endian. - format = "<" + code - expected = string_reverse(expected) - - # Pack work? - got = pack(format, x) - verify(got == expected, - "'%s'-pack of %r gave %r, not %r" % - (format, x, got, expected)) - - # Unpack work? - retrieved = unpack(format, got)[0] - verify(x == retrieved, - "'%s'-unpack of %r gave %r, not %r" % - (format, got, retrieved, x)) - - # Adding any byte should cause a "too big" error. - any_err(unpack, format, b'\x01' + got) +class StructTest(unittest.TestCase): + + @with_warning_restore + def check_float_coerce(self, format, number): + # SF bug 1530559. struct.pack raises TypeError where it used to convert. + if PY_STRUCT_FLOAT_COERCE == 2: + # Test for pre-2.5 struct module + packed = struct.pack(format, number) + floored = struct.unpack(format, packed)[0] + self.assertEqual(floored, int(number), + "did not correcly coerce float to int") + return + try: + struct.pack(format, number) + except (struct.error, TypeError): + if PY_STRUCT_FLOAT_COERCE: + self.fail("expected DeprecationWarning for float coerce") + except DeprecationWarning: + if not PY_STRUCT_FLOAT_COERCE: + self.fail("expected to raise struct.error for float coerce") else: - # x is out of range -- verify pack realizes that. - if not PY_STRUCT_RANGE_CHECKING and code in self.BUGGY_RANGE_CHECK: - if verbose: - print("Skipping buggy range check for code", code) - else: - deprecated_err(pack, ">" + code, x) - deprecated_err(pack, "<" + code, x) - - def run(self): - from random import randrange - - # Create all interesting powers of 2. - values = [] - for exp in range(self.bitsize + 3): - values.append(1 << exp) - - # Add some random values. - for i in range(self.bitsize): - val = 0 - for j in range(self.bytesize): - val = (val << 8) | randrange(256) - values.append(val) - - # Try all those, and their negations, and +-1 from them. Note - # that this tests all power-of-2 boundaries in range, and a few out - # of range, plus +-(2**n +- 1). - for base in values: - for val in -base, base: - for incr in -1, 0, 1: - x = val + incr - try: - x = int(x) - except OverflowError: - pass - self.test_one(x) - - # Some error cases. - for direction in "<>": - for code in self.formatpair: - for badobject in "a string", 3+42j, randrange: - any_err(struct.pack, direction + code, badobject) - -for args in [("bB", 1), - ("hH", 2), - ("iI", 4), - ("lL", 4), - ("qQ", 8)]: - t = IntTester(*args) - t.run() - - -########################################################################### -# The p ("Pascal string") code. - -def test_p_code(): - for code, input, expected, expectedback in [ - ('p','abc', '\x00', b''), - ('1p', 'abc', '\x00', b''), - ('2p', 'abc', '\x01a', b'a'), - ('3p', 'abc', '\x02ab', b'ab'), - ('4p', 'abc', '\x03abc', b'abc'), - ('5p', 'abc', '\x03abc\x00', b'abc'), - ('6p', 'abc', '\x03abc\x00\x00', b'abc'), - ('1000p', 'x'*1000, '\xff' + 'x'*999, b'x'*255)]: - expected = bytes(expected, "latin-1") - got = struct.pack(code, input) - if got != expected: - raise TestFailed("pack(%r, %r) == %r but expected %r" % - (code, input, got, expected)) - (got,) = struct.unpack(code, got) - if got != expectedback: - raise TestFailed("unpack(%r, %r) == %r but expected %r" % - (code, input, got, expectedback)) - -test_p_code() - - -########################################################################### -# SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry -# from the low-order discarded bits could propagate into the exponent -# field, causing the result to be wrong by a factor of 2. - -def test_705836(): - import math - - for base in range(1, 33): - # smaller <- largest representable float less than base. - delta = 0.5 - while base - delta / 2.0 != base: - delta /= 2.0 - smaller = base - delta - # Packing this rounds away a solid string of trailing 1 bits. - packed = struct.pack("<f", smaller) - unpacked = struct.unpack("<f", packed)[0] - # This failed at base = 2, 4, and 32, with unpacked = 1, 2, and - # 16, respectively. - verify(base == unpacked) - bigpacked = struct.pack(">f", smaller) - verify(bigpacked == string_reverse(packed), - ">f pack should be byte-reversal of <f pack") - unpacked = struct.unpack(">f", bigpacked)[0] - verify(base == unpacked) - - # Largest finite IEEE single. - big = (1 << 24) - 1 - big = math.ldexp(big, 127 - 23) - packed = struct.pack(">f", big) - unpacked = struct.unpack(">f", packed)[0] - verify(big == unpacked) - - # The same, but tack on a 1 bit so it rounds up to infinity. - big = (1 << 25) - 1 - big = math.ldexp(big, 127 - 24) - try: - packed = struct.pack(">f", big) - except OverflowError: - pass - else: - raise TestFailed("expected OverflowError") - -test_705836() - -########################################################################### -# SF bug 1229380. No struct.pack exception for some out of range integers - -def test_1229380(): - import sys - for endian in ('', '>', '<'): - for fmt in ('B', 'H', 'I', 'L'): - deprecated_err(struct.pack, endian + fmt, -1) - - deprecated_err(struct.pack, endian + 'B', 300) - deprecated_err(struct.pack, endian + 'H', 70000) - - deprecated_err(struct.pack, endian + 'I', sys.maxsize * 4) - deprecated_err(struct.pack, endian + 'L', sys.maxsize * 4) - -if PY_STRUCT_RANGE_CHECKING: - test_1229380() - -########################################################################### -# SF bug 1530559. struct.pack raises TypeError where it used to convert. - -def check_float_coerce(format, number): - if PY_STRUCT_FLOAT_COERCE == 2: - # Test for pre-2.5 struct module - packed = struct.pack(format, number) - floored = struct.unpack(format, packed)[0] - if floored != int(number): - raise TestFailed("did not correcly coerce float to int") - return - try: - func(*args) - except (struct.error, TypeError): - if PY_STRUCT_FLOAT_COERCE: - raise TestFailed("expected DeprecationWarning for float coerce") - except DeprecationWarning: - if not PY_STRUCT_FLOAT_COERCE: - raise TestFailed("expected to raise struct.error for float coerce") - else: - raise TestFailed("did not raise error for float coerce") - -check_float_coerce = with_warning_restore(deprecated_err) - -def test_1530559(): - for endian in ('', '>', '<'): - for fmt in ('B', 'H', 'I', 'L', 'b', 'h', 'i', 'l'): - check_float_coerce(endian + fmt, 1.0) - check_float_coerce(endian + fmt, 1.5) - -test_1530559() - -########################################################################### -# Packing and unpacking to/from memory views. - -# Copied and modified from unittest. -def assertRaises(excClass, callableObj, *args, **kwargs): - try: - callableObj(*args, **kwargs) - except excClass: - return - else: - raise TestFailed("%s not raised." % excClass) - -def test_unpack_from(): - test_string = b'abcd01234' - fmt = '4s' - s = struct.Struct(fmt) - for cls in (bytes, bytearray): - if verbose: - print("test_unpack_from using", cls.__name__) - data = cls(test_string) - if not isinstance(data, (bytes, bytearray)): - bytes_data = bytes(data, 'latin1') + self.fail("did not raise error for float coerce") + + def test_isbigendian(self): + self.assertEqual((struct.pack('=i', 1)[0] == chr(0)), ISBIGENDIAN) + + def test_consistence(self): + self.assertRaises(struct.error, struct.calcsize, 'Z') + + sz = struct.calcsize('i') + self.assertEqual(sz * 3, struct.calcsize('iii')) + + fmt = 'cbxxxxxxhhhhiillffd?' + fmt3 = '3c3b18x12h6i6l6f3d3?' + sz = struct.calcsize(fmt) + sz3 = struct.calcsize(fmt3) + self.assertEqual(sz * 3, sz3) + + self.assertRaises(struct.error, struct.pack, 'iii', 3) + self.assertRaises(struct.error, struct.pack, 'i', 3, 3, 3) + self.assertRaises(struct.error, struct.pack, 'i', 'foo') + self.assertRaises(struct.error, struct.pack, 'P', 'foo') + 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 = b'a' + b = 1 + h = 255 + i = 65535 + l = 65536 + f = 3.1415 + d = 3.1415 + t = True + + for prefix in ('', '@', '<', '>', '=', '!'): + for format in ('xcbhilfd?', 'xcBHILfd?'): + format = prefix + format + s = struct.pack(format, c, b, h, i, l, f, d, t) + cp, bp, hp, ip, lp, fp, dp, tp = struct.unpack(format, s) + self.assertEqual(cp, c) + self.assertEqual(bp, b) + self.assertEqual(hp, h) + self.assertEqual(ip, i) + self.assertEqual(lp, l) + self.assertEqual(int(100 * fp), int(100 * f)) + self.assertEqual(int(100 * dp), int(100 * d)) + self.assertEqual(tp, t) + + def test_new_features(self): + # 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', 70000000, '\004,\035\200', '\200\035,\004', 0), + ('I', 0x100000000-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', 70000000, '\004,\035\200', '\200\035,\004', 0), + ('L', 0x100000000-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), + ] + + for fmt, arg, big, lil, asy in tests: + big = bytes(big, "latin-1") + lil = bytes(lil, "latin-1") + for (xfmt, exp) in [('>'+fmt, big), ('!'+fmt, big), ('<'+fmt, lil), + ('='+fmt, ISBIGENDIAN and big or lil)]: + res = struct.pack(xfmt, arg) + self.assertEqual(res, exp) + self.assertEqual(struct.calcsize(xfmt), len(res)) + rev = struct.unpack(xfmt, res)[0] + if isinstance(arg, str): + # Strings are returned as bytes since you can't know the + # encoding of the string when packed. + arg = bytes(arg, 'latin1') + if rev != arg: + self.assert_(asy) + + def test_native_qQ(self): + # can't pack -1 as unsigned regardless + self.assertRaises((struct.error, TypeError), struct.pack, "Q", -1) + # can't pack string as 'q' regardless + self.assertRaises(struct.error, struct.pack, "q", "a") + # ditto, but 'Q' + self.assertRaises(struct.error, struct.pack, "Q", "a") + + try: + struct.pack("q", 5) + except struct.error: + # does not have native q/Q + pass else: - bytes_data = data - vereq(s.unpack_from(data), (b'abcd',)) - vereq(s.unpack_from(data, 2), (b'cd01',)) - vereq(s.unpack_from(data, 4), (b'0123',)) - for i in range(6): - vereq(s.unpack_from(data, i), (bytes_data[i:i+4],)) - for i in range(6, len(test_string) + 1): - simple_err(s.unpack_from, data, i) - for cls in (bytes, bytearray): - data = cls(test_string) - vereq(struct.unpack_from(fmt, data), (b'abcd',)) - vereq(struct.unpack_from(fmt, data, 2), (b'cd01',)) - vereq(struct.unpack_from(fmt, data, 4), (b'0123',)) - for i in range(6): - vereq(struct.unpack_from(fmt, data, i), (bytes_data[i:i+4],)) - for i in range(6, len(test_string) + 1): - simple_err(struct.unpack_from, fmt, data, i) - -def test_pack_into(): - 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)] - vereq(from_buf, test_string) - - # Test with offset. - s.pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] - vereq(from_buf, test_string[:10] + test_string) - - # Go beyond boundaries. - small_buf = array.array('b', b' '*10) - assertRaises(struct.error, s.pack_into, small_buf, 0, test_string) - assertRaises(struct.error, s.pack_into, small_buf, 2, test_string) - -def test_pack_into_fn(): - 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)] - vereq(from_buf, test_string) - - # Test with offset. - pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] - vereq(from_buf, test_string[:10] + test_string) - - # Go beyond boundaries. - small_buf = array.array('b', b' '*10) - assertRaises(struct.error, pack_into, small_buf, 0, test_string) - assertRaises(struct.error, pack_into, small_buf, 2, test_string) - -def test_unpack_with_memoryview(): - # SF bug 1563759: struct.unpack doens'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) - vereq(value, 0x12345678) - -# Test methods to pack and unpack from memoryviews rather than strings. -test_unpack_from() -test_pack_into() -test_pack_into_fn() -test_unpack_with_memoryview() - -def test_bool(): - for prefix in tuple("<>!=")+('',): - false = (), [], [], '', 0 - true = [1], 'test', 5, -1, 0xffffffff+1, 0xffffffff/2 - - falseFormat = prefix + '?' * len(false) - if verbose: - print('trying bool pack/unpack on', false, 'using format', falseFormat) - packedFalse = struct.pack(falseFormat, *false) - unpackedFalse = struct.unpack(falseFormat, packedFalse) - - trueFormat = prefix + '?' * len(true) - if verbose: - print('trying bool pack/unpack on', true, 'using format', trueFormat) - packedTrue = struct.pack(trueFormat, *true) - unpackedTrue = struct.unpack(trueFormat, packedTrue) - - if len(true) != len(unpackedTrue): - raise TestFailed('unpacked true array is not of same size as input') - if len(false) != len(unpackedFalse): - raise TestFailed('unpacked false array is not of same size as input') - - for t in unpackedFalse: - if t is not False: - raise TestFailed('%r did not unpack as False' % t) - for t in unpackedTrue: - if t is not True: - raise TestFailed('%r did not unpack as false' % t) - - if prefix and verbose: - print('trying size of bool with format %r' % (prefix+'?')) - packed = struct.pack(prefix+'?', 1) - - if len(packed) != struct.calcsize(prefix+'?'): - raise TestFailed('packed length is not equal to calculated size') - - if len(packed) != 1 and prefix: - raise TestFailed('encoded bool is not one byte: %r' % packed) - elif not prefix and verbose: - print('size of bool in native format is %i' % (len(packed))) - - for c in [b'\x01', b'\x7f', b'\xff', b'\x0f', b'\xf0']: - if struct.unpack('>?', c)[0] is not True: - raise TestFailed('%c did not unpack as True' % c) - -test_bool() + nbytes = struct.calcsize('q') + # The expected values here are in big-endian format, primarily + # because I'm on a little-endian machine and so this is the + # clearest way (for me) to force the code to get exercised. + for format, input, expected in ( + ('q', -1, '\xff' * nbytes), + ('q', 0, '\x00' * nbytes), + ('Q', 0, '\x00' * nbytes), + ('q', 1, '\x00' * (nbytes-1) + '\x01'), + ('Q', (1 << (8*nbytes))-1, '\xff' * nbytes), + ('q', (1 << (8*nbytes-1))-1, '\x7f' + '\xff' * (nbytes - 1))): + expected = bytes(expected, "latin-1") + got = struct.pack(format, input) + native_expected = bigendian_to_native(expected) + self.assertEqual(got, native_expected) + retrieved = struct.unpack(format, got)[0] + self.assertEqual(retrieved, input) + + def test_standard_integers(self): + # Standard integer tests (bBhHiIlLqQ). + import binascii + + class IntTester(unittest.TestCase): + + # XXX Most std integer modes fail to test for out-of-range. + # The "i" and "l" codes appear to range-check OK on 32-bit boxes, but + # fail to check correctly on some 64-bit ones (Tru64 Unix + Compaq C + # reported by Mark Favas). + BUGGY_RANGE_CHECK = "bBhHiIlL" + + def __init__(self, formatpair, bytesize): + self.assertEqual(len(formatpair), 2) + self.formatpair = formatpair + for direction in "<>!=": + for code in formatpair: + format = direction + code + self.assertEqual(struct.calcsize(format), bytesize) + self.bytesize = bytesize + self.bitsize = bytesize * 8 + self.signed_code, self.unsigned_code = formatpair + self.unsigned_min = 0 + self.unsigned_max = 2**self.bitsize - 1 + self.signed_min = -(2**(self.bitsize-1)) + self.signed_max = 2**(self.bitsize-1) - 1 + + def test_one(self, x, pack=struct.pack, + unpack=struct.unpack, + unhexlify=binascii.unhexlify): + # Try signed. + code = self.signed_code + if self.signed_min <= x <= self.signed_max: + # Try big-endian. + expected = x + if x < 0: + expected += 1 << self.bitsize + self.assert_(expected > 0) + expected = hex(expected)[2:] # chop "0x" + if len(expected) & 1: + expected = "0" + expected + expected = unhexlify(expected) + expected = b"\x00" * (self.bytesize - len(expected)) + expected + + # Pack work? + format = ">" + code + got = pack(format, x) + self.assertEqual(got, expected) + + # Unpack work? + retrieved = unpack(format, got)[0] + self.assertEqual(x, retrieved) + + # Adding any byte should cause a "too big" error. + self.assertRaises((struct.error, TypeError), + unpack, format, b'\x01' + got) + + # Try little-endian. + format = "<" + code + expected = string_reverse(expected) + + # Pack work? + got = pack(format, x) + self.assertEqual(got, expected) + + # Unpack work? + retrieved = unpack(format, got)[0] + self.assertEqual(x, retrieved) + + # Adding any byte should cause a "too big" error. + self.assertRaises((struct.error, TypeError), + unpack, format, b'\x01' + got) + + else: + # x is out of range -- verify pack realizes that. + if not PY_STRUCT_RANGE_CHECKING and code in self.BUGGY_RANGE_CHECK: + if verbose: + print("Skipping buggy range check for code", code) + else: + deprecated_err(pack, ">" + code, x) + deprecated_err(pack, "<" + code, x) + + # Much the same for unsigned. + code = self.unsigned_code + if self.unsigned_min <= x <= self.unsigned_max: + # Try big-endian. + format = ">" + code + expected = x + expected = hex(expected)[2:] # chop "0x" + if len(expected) & 1: + expected = "0" + expected + expected = unhexlify(expected) + expected = b"\x00" * (self.bytesize - len(expected)) + expected + + # Pack work? + got = pack(format, x) + self.assertEqual(got, expected) + + # Unpack work? + retrieved = unpack(format, got)[0] + self.assertEqual(x, retrieved) + + # Adding any byte should cause a "too big" error. + self.assertRaises((struct.error, TypeError), + unpack, format, b'\x01' + got) + + # Try little-endian. + format = "<" + code + expected = string_reverse(expected) + + # Pack work? + got = pack(format, x) + self.assertEqual(got, expected) + + # Unpack work? + retrieved = unpack(format, got)[0] + self.assertEqual(x, retrieved) + + # Adding any byte should cause a "too big" error. + self.assertRaises((struct.error, TypeError), + unpack, format, b'\x01' + got) + + else: + # x is out of range -- verify pack realizes that. + if not PY_STRUCT_RANGE_CHECKING and code in self.BUGGY_RANGE_CHECK: + if verbose: + print("Skipping buggy range check for code", code) + else: + deprecated_err(pack, ">" + code, x) + deprecated_err(pack, "<" + code, x) + + def run(self): + from random import randrange + + # Create all interesting powers of 2. + values = [] + for exp in range(self.bitsize + 3): + values.append(1 << exp) + + # Add some random values. + for i in range(self.bitsize): + val = 0 + for j in range(self.bytesize): + val = (val << 8) | randrange(256) + values.append(val) + + # Try all those, and their negations, and +-1 from them. Note + # that this tests all power-of-2 boundaries in range, and a few out + # of range, plus +-(2**n +- 1). + for base in values: + for val in -base, base: + for incr in -1, 0, 1: + x = val + incr + try: + x = int(x) + except OverflowError: + pass + self.test_one(x) + + # Some error cases. + for direction in "<>": + for code in self.formatpair: + for badobject in "a string", 3+42j, randrange: + self.assertRaises((struct.error, TypeError), + struct.pack, direction + code, + badobject) + + for args in [("bB", 1), + ("hH", 2), + ("iI", 4), + ("lL", 4), + ("qQ", 8)]: + t = IntTester(*args) + t.run() + + def test_p_code(self): + # Test p ("Pascal string") code. + for code, input, expected, expectedback in [ + ('p','abc', '\x00', b''), + ('1p', 'abc', '\x00', b''), + ('2p', 'abc', '\x01a', b'a'), + ('3p', 'abc', '\x02ab', b'ab'), + ('4p', 'abc', '\x03abc', b'abc'), + ('5p', 'abc', '\x03abc\x00', b'abc'), + ('6p', 'abc', '\x03abc\x00\x00', b'abc'), + ('1000p', 'x'*1000, '\xff' + 'x'*999, b'x'*255)]: + expected = bytes(expected, "latin-1") + got = struct.pack(code, input) + self.assertEqual(got, expected) + (got,) = struct.unpack(code, got) + self.assertEqual(got, expectedback) + + def test_705836(self): + # SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry + # from the low-order discarded bits could propagate into the exponent + # field, causing the result to be wrong by a factor of 2. + import math + + for base in range(1, 33): + # smaller <- largest representable float less than base. + delta = 0.5 + while base - delta / 2.0 != base: + delta /= 2.0 + smaller = base - delta + # Packing this rounds away a solid string of trailing 1 bits. + packed = struct.pack("<f", smaller) + unpacked = struct.unpack("<f", packed)[0] + # This failed at base = 2, 4, and 32, with unpacked = 1, 2, and + # 16, respectively. + self.assertEqual(base, unpacked) + bigpacked = struct.pack(">f", smaller) + self.assertEqual(bigpacked, string_reverse(packed)) + unpacked = struct.unpack(">f", bigpacked)[0] + self.assertEqual(base, unpacked) + + # Largest finite IEEE single. + big = (1 << 24) - 1 + big = math.ldexp(big, 127 - 23) + packed = struct.pack(">f", big) + unpacked = struct.unpack(">f", packed)[0] + self.assertEqual(big, unpacked) + + # The same, but tack on a 1 bit so it rounds up to infinity. + big = (1 << 25) - 1 + big = math.ldexp(big, 127 - 24) + self.assertRaises(OverflowError, struct.pack, ">f", big) + + if PY_STRUCT_RANGE_CHECKING: + def test_1229380(self): + # SF bug 1229380. No struct.pack exception for some out of + # range integers + import sys + for endian in ('', '>', '<'): + for fmt in ('B', 'H', 'I', 'L'): + deprecated_err(struct.pack, endian + fmt, -1) + + deprecated_err(struct.pack, endian + 'B', 300) + deprecated_err(struct.pack, endian + 'H', 70000) + + deprecated_err(struct.pack, endian + 'I', sys.maxsize * 4) + deprecated_err(struct.pack, endian + 'L', sys.maxsize * 4) + + def XXXtest_1530559(self): + # XXX This is broken: see the bug report + # SF bug 1530559. struct.pack raises TypeError where it used to convert. + for endian in ('', '>', '<'): + for fmt in ('B', 'H', 'I', 'L', 'b', 'h', 'i', 'l'): + self.check_float_coerce(endian + fmt, 1.0) + self.check_float_coerce(endian + fmt, 1.5) + + def test_unpack_from(self): + test_string = b'abcd01234' + fmt = '4s' + s = struct.Struct(fmt) + for cls in (bytes, bytearray): + data = cls(test_string) + if not isinstance(data, (bytes, bytearray)): + bytes_data = bytes(data, 'latin1') + else: + bytes_data = data + 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), (bytes_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 = 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)] + 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] + self.assertEqual(from_buf, test_string[:10] + test_string) + + # Go beyond boundaries. + small_buf = array.array('b', b' '*10) + self.assertRaises(struct.error, s.pack_into, small_buf, 0, test_string) + self.assertRaises(struct.error, s.pack_into, small_buf, 2, test_string) + + def test_pack_into_fn(self): + 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)] + 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] + self.assertEqual(from_buf, test_string[:10] + test_string) + + # Go beyond boundaries. + small_buf = array.array('b', b' '*10) + self.assertRaises(struct.error, pack_into, small_buf, 0, test_string) + self.assertRaises(struct.error, pack_into, small_buf, 2, test_string) + + def test_unpack_with_buffer(self): + # SF bug 1563759: struct.unpack doens'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): + for prefix in tuple("<>!=")+('',): + false = (), [], [], '', 0 + true = [1], 'test', 5, -1, 0xffffffff+1, 0xffffffff/2 + + falseFormat = prefix + '?' * len(false) + packedFalse = struct.pack(falseFormat, *false) + unpackedFalse = struct.unpack(falseFormat, packedFalse) + + trueFormat = prefix + '?' * len(true) + packedTrue = struct.pack(trueFormat, *true) + unpackedTrue = struct.unpack(trueFormat, packedTrue) + + self.assertEqual(len(true), len(unpackedTrue)) + self.assertEqual(len(false), len(unpackedFalse)) + + for t in unpackedFalse: + self.assertFalse(t) + for t in unpackedTrue: + self.assertTrue(t) + + packed = struct.pack(prefix+'?', 1) + + self.assertEqual(len(packed), struct.calcsize(prefix+'?')) + + if len(packed) != 1: + self.assertFalse(prefix, msg='encoded bool is not one byte: %r' + %packed) + + for c in [b'\x01', b'\x7f', b'\xff', b'\x0f', b'\xf0']: + self.assertTrue(struct.unpack('>?', c)[0]) + +def test_main(): + run_unittest(StructTest) + +if __name__ == '__main__': + test_main() diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c index 9d2bb1a..4cf6c95 100644 --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -17,13 +17,18 @@ static int cmp_lt(PyObject *x, PyObject *y) { int cmp; - cmp = PyObject_RichCompareBool(x, y, Py_LT); - if (cmp == -1 && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - cmp = PyObject_RichCompareBool(y, x, Py_LE); - if (cmp != -1) - cmp = 1 - cmp; + static PyObject *lt = NULL; + + if (lt == NULL) { + lt = PyUnicode_FromString("__lt__"); + if (lt == NULL) + return -1; } + if (PyObject_HasAttr(x, lt)) + return PyObject_RichCompareBool(x, y, Py_LT); + cmp = PyObject_RichCompareBool(y, x, Py_LE); + if (cmp != -1) + cmp = 1 - cmp; return cmp; } diff --git a/Objects/setobject.c b/Objects/setobject.c index e5c89e5..461fee6 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1304,6 +1304,9 @@ set_intersection_multi(PySetObject *so, PyObject *args) Py_ssize_t i; PyObject *result = (PyObject *)so; + if (PyTuple_GET_SIZE(args) == 0) + return set_copy(so); + Py_INCREF(so); for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) { PyObject *other = PyTuple_GET_ITEM(args, i); @@ -1484,11 +1487,16 @@ set_difference_update_internal(PySetObject *so, PyObject *other) } static PyObject * -set_difference_update(PySetObject *so, PyObject *other) +set_difference_update(PySetObject *so, PyObject *args) { - if (set_difference_update_internal(so, other) != -1) - Py_RETURN_NONE; - return NULL; + Py_ssize_t i; + + for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) { + PyObject *other = PyTuple_GET_ITEM(args, i); + if (set_difference_update_internal(so, other) == -1) + return NULL; + } + Py_RETURN_NONE; } PyDoc_STRVAR(difference_update_doc, @@ -1546,10 +1554,34 @@ set_difference(PySetObject *so, PyObject *other) return result; } +static PyObject * +set_difference_multi(PySetObject *so, PyObject *args) +{ + Py_ssize_t i; + PyObject *result, *other; + + if (PyTuple_GET_SIZE(args) == 0) + return set_copy(so); + + other = PyTuple_GET_ITEM(args, 0); + result = set_difference(so, other); + if (result == NULL) + return NULL; + + for (i=1 ; i<PyTuple_GET_SIZE(args) ; i++) { + other = PyTuple_GET_ITEM(args, i); + if (set_difference_update_internal((PySetObject *)result, other) == -1) { + Py_DECREF(result); + return NULL; + } + } + return result; +} + PyDoc_STRVAR(difference_doc, -"Return the difference of two sets as a new set.\n\ +"Return the difference of two or more sets as a new set.\n\ \n\ -(i.e. all elements that are in this set but not the other.)"); +(i.e. all elements that are in this set but not the others.)"); static PyObject * set_sub(PySetObject *so, PyObject *other) { @@ -1563,16 +1595,12 @@ set_sub(PySetObject *so, PyObject *other) static PyObject * set_isub(PySetObject *so, PyObject *other) { - PyObject *result; - if (!PyAnySet_Check(other)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } - result = set_difference_update(so, other); - if (result == NULL) + if (set_difference_update_internal(so, other) == -1) return NULL; - Py_DECREF(result); Py_INCREF(so); return (PyObject *)so; } @@ -1963,9 +1991,9 @@ static PyMethodDef set_methods[] = { copy_doc}, {"discard", (PyCFunction)set_discard, METH_O, discard_doc}, - {"difference", (PyCFunction)set_difference, METH_O, + {"difference", (PyCFunction)set_difference_multi, METH_VARARGS, difference_doc}, - {"difference_update", (PyCFunction)set_difference_update, METH_O, + {"difference_update", (PyCFunction)set_difference_update, METH_VARARGS, difference_update_doc}, {"intersection",(PyCFunction)set_intersection_multi, METH_VARARGS, intersection_doc}, @@ -2087,7 +2115,7 @@ static PyMethodDef frozenset_methods[] = { contains_doc}, {"copy", (PyCFunction)frozenset_copy, METH_NOARGS, copy_doc}, - {"difference", (PyCFunction)set_difference, METH_O, + {"difference", (PyCFunction)set_difference_multi, METH_VARARGS, difference_doc}, {"intersection",(PyCFunction)set_intersection_multi, METH_VARARGS, intersection_doc}, |