mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Keep the existing conversion and initialization path while binding format as positional-or-keyword, matching Python 3.14. Assisted-by: OpenAI Codex:model identifier unavailable
205 lines
5.2 KiB
Python
205 lines
5.2 KiB
Python
import struct
|
|
|
|
from testutils import assert_raises
|
|
|
|
data = struct.pack("IH", 14, 12)
|
|
assert data == bytes([14, 0, 0, 0, 12, 0])
|
|
|
|
v1, v2 = struct.unpack("IH", data)
|
|
assert v1 == 14
|
|
assert v2 == 12
|
|
|
|
data = struct.pack("<IH", 14, 12)
|
|
assert data == bytes([14, 0, 0, 0, 12, 0])
|
|
|
|
v1, v2 = struct.unpack("<IH", data)
|
|
assert v1 == 14
|
|
assert v2 == 12
|
|
|
|
data = struct.pack(">IH", 14, 12)
|
|
assert data == bytes([0, 0, 0, 14, 0, 12])
|
|
|
|
v1, v2 = struct.unpack(">IH", data)
|
|
assert v1 == 14
|
|
assert v2 == 12
|
|
|
|
data = struct.pack("3B", 65, 66, 67)
|
|
assert data == bytes([65, 66, 67])
|
|
|
|
v1, v2, v3 = struct.unpack("3B", data)
|
|
assert v1 == 65
|
|
assert v2 == 66
|
|
assert v3 == 67
|
|
|
|
with assert_raises(Exception):
|
|
data = struct.pack("B0B", 65, 66)
|
|
|
|
with assert_raises(Exception):
|
|
data = struct.pack("B2B", 65, 66)
|
|
|
|
data = struct.pack("B1B", 65, 66)
|
|
|
|
with assert_raises(Exception):
|
|
struct.pack("<IH", "14", 12)
|
|
|
|
assert struct.calcsize("B") == 1
|
|
# assert struct.calcsize("<L4B") == 12
|
|
|
|
assert struct.Struct("3B").pack(65, 66, 67) == bytes([65, 66, 67])
|
|
|
|
|
|
class Indexable(object):
|
|
def __init__(self, value):
|
|
self._value = value
|
|
|
|
def __index__(self):
|
|
return self._value
|
|
|
|
|
|
data = struct.pack("B", Indexable(65))
|
|
assert data == bytes([65])
|
|
|
|
data = struct.pack("5s", b"test1")
|
|
assert data == b"test1"
|
|
|
|
data = struct.pack("3s", b"test2")
|
|
assert data == b"tes"
|
|
|
|
data = struct.pack("7s", b"test3")
|
|
assert data == b"test3\0\0"
|
|
|
|
data = struct.pack("?", True)
|
|
assert data == b"\1"
|
|
|
|
data = struct.pack("?", [])
|
|
assert data == b"\0"
|
|
|
|
assert struct.error.__module__ == "struct"
|
|
assert struct.error.__name__ == "error"
|
|
|
|
# Non-ASCII format string: error type matches CPython.
|
|
# str → UnicodeEncodeError (encoding='ascii')
|
|
# bytes → struct.error
|
|
try:
|
|
struct.Struct("\udc00")
|
|
except UnicodeEncodeError as e:
|
|
assert e.encoding == "ascii"
|
|
else:
|
|
raise AssertionError("expected UnicodeEncodeError")
|
|
|
|
with assert_raises(UnicodeEncodeError):
|
|
struct.Struct("한")
|
|
|
|
with assert_raises(struct.error):
|
|
struct.Struct(b"\xff")
|
|
|
|
|
|
# A value the format has no room for names the format and the range it holds.
|
|
for fmt, message in (
|
|
("B", "'B' format requires 0 <= number <= 255"),
|
|
("b", "'b' format requires -128 <= number <= 127"),
|
|
(">H", "'H' format requires 0 <= number <= 65535"),
|
|
(">i", "'i' format requires -2147483648 <= number <= 2147483647"),
|
|
("N", "'N' format requires 0 <= number <= 18446744073709551615"),
|
|
("P", "int too large to convert"),
|
|
):
|
|
try:
|
|
struct.pack(fmt, 10**30)
|
|
except struct.error as e:
|
|
assert str(e) == message, (fmt, str(e))
|
|
else:
|
|
raise AssertionError(f"expected struct.error for {fmt!r}")
|
|
|
|
try:
|
|
struct.pack("B", "x")
|
|
except struct.error as e:
|
|
assert str(e) == "required argument is not an integer", e
|
|
else:
|
|
raise AssertionError("expected struct.error")
|
|
|
|
|
|
# __init__ reads a new format into a Struct that already holds one.
|
|
s = struct.Struct(">h")
|
|
s.__init__(">hh")
|
|
assert s.format == ">hh"
|
|
assert s.size == 4
|
|
assert s.pack(1, 2) == b"\x00\x01\x00\x02"
|
|
assert s.unpack(b"\x00\x01\x00\x02") == (1, 2)
|
|
|
|
# A format that cannot be read leaves the Struct as it was.
|
|
for bad in ("\udc00", "$"):
|
|
with assert_raises((UnicodeEncodeError, struct.error)):
|
|
s.__init__(bad)
|
|
assert s.format == ">hh"
|
|
assert s.pack(1, 2) == b"\x00\x01\x00\x02"
|
|
|
|
|
|
# A subclass may do its own __init__ and pass the format up.
|
|
class BigShort(struct.Struct):
|
|
def __init__(self):
|
|
super().__init__(">h")
|
|
|
|
|
|
assert BigShort().pack(12345) == b"\x30\x39"
|
|
|
|
# Until __init__ runs there is no format to answer with.
|
|
blank = struct.Struct.__new__(struct.Struct)
|
|
assert blank.size == -1
|
|
for call in (
|
|
lambda: blank.format,
|
|
lambda: blank.pack(1),
|
|
lambda: blank.unpack(b"aa"),
|
|
lambda: blank.unpack_from(b"aaaa"),
|
|
lambda: blank.pack_into(bytearray(4), 0, 1),
|
|
lambda: blank.iter_unpack(b"aa"),
|
|
lambda: repr(blank),
|
|
):
|
|
with assert_raises(RuntimeError):
|
|
call()
|
|
|
|
|
|
# The buffer a format asks for is sized by the format: one too large to
|
|
# allocate must raise instead of aborting.
|
|
with assert_raises(MemoryError):
|
|
struct.pack("%dx" % (2**60))
|
|
|
|
|
|
# Clearing or evicting cached formats must not invalidate a live iterator.
|
|
iterator = struct.iter_unpack("<H", b"\x01\x00\x02\x00")
|
|
struct._clearcache()
|
|
for padding in range(110):
|
|
assert struct.calcsize(f"{padding}x") == padding
|
|
assert list(iterator) == [(1,), (2,)]
|
|
|
|
|
|
class ClearFormats:
|
|
def __index__(self):
|
|
struct._clearcache()
|
|
assert struct.pack("<H", 7) == b"\x07\x00"
|
|
return 42
|
|
|
|
|
|
# Argument conversion may clear and repopulate the cache during an active pack.
|
|
assert struct.pack("<H", ClearFormats()) == b"\x2a\x00"
|
|
buffer = bytearray(2)
|
|
struct.pack_into("<H", buffer, 0, ClearFormats())
|
|
assert buffer == b"\x2a\x00"
|
|
|
|
|
|
# Struct's format is positional-or-keyword, including inherited initialization.
|
|
class KeywordStruct(struct.Struct):
|
|
pass
|
|
|
|
|
|
for cls in (struct.Struct, KeywordStruct):
|
|
for fmt in (">h", b">h"):
|
|
s = cls(format=fmt)
|
|
assert s.format == ">h"
|
|
assert s.pack(7) == b"\x00\x07"
|
|
s.__init__(format=">hh")
|
|
assert s.unpack(b"\x00\x01\x00\x02") == (1, 2)
|
|
with assert_raises(struct.error):
|
|
s.__init__(format="$")
|
|
assert s.format == ">hh"
|
|
assert s.pack(1, 2) == b"\x00\x01\x00\x02"
|