diff options
author | Jon Janzen <jjjonjanzen@gmail.com> | 2019-05-15 20:14:38 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2019-05-15 20:14:38 (GMT) |
commit | c981ad16b0f9740bd3381c96b4227a1faa1a88d9 (patch) | |
tree | 11ae5ac3f709d5ddd54a27334207441f7debe025 /Lib/plistlib.py | |
parent | e307e5cd06f60e11e4e4f126fae412b9ec66a005 (diff) | |
download | cpython-c981ad16b0f9740bd3381c96b4227a1faa1a88d9.zip cpython-c981ad16b0f9740bd3381c96b4227a1faa1a88d9.tar.gz cpython-c981ad16b0f9740bd3381c96b4227a1faa1a88d9.tar.bz2 |
bpo-26707: Enable plistlib to read UID keys. (GH-12153)
Plistlib currently throws an exception when asked to decode a valid
.plist file that was generated by Apple's NSKeyedArchiver. Specifically,
this is caused by a byte 0x80 (signifying a UID) not being understood.
This fixes the problem by enabling the binary plist reader and writer
to read and write plistlib.UID objects.
Diffstat (limited to 'Lib/plistlib.py')
-rw-r--r-- | Lib/plistlib.py | 49 |
1 files changed, 46 insertions, 3 deletions
diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 248f514..0133c89 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -48,7 +48,7 @@ Parse Plist example: __all__ = [ "readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", "Data", "InvalidFileException", "FMT_XML", "FMT_BINARY", - "load", "dump", "loads", "dumps" + "load", "dump", "loads", "dumps", "UID" ] import binascii @@ -175,6 +175,34 @@ class Data: # +class UID: + def __init__(self, data): + if not isinstance(data, int): + raise TypeError("data must be an int") + if data >= 1 << 64: + raise ValueError("UIDs cannot be >= 2**64") + if data < 0: + raise ValueError("UIDs must be positive") + self.data = data + + def __index__(self): + return self.data + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self.data)) + + def __reduce__(self): + return self.__class__, (self.data,) + + def __eq__(self, other): + if not isinstance(other, UID): + return NotImplemented + return self.data == other.data + + def __hash__(self): + return hash(self.data) + + # # XML support # @@ -649,8 +677,9 @@ class _BinaryPlistParser: s = self._get_size(tokenL) result = self._fp.read(s * 2).decode('utf-16be') - # tokenH == 0x80 is documented as 'UID' and appears to be used for - # keyed-archiving, not in plists. + elif tokenH == 0x80: # UID + # used by Key-Archiver plist files + result = UID(int.from_bytes(self._fp.read(1 + tokenL), 'big')) elif tokenH == 0xA0: # array s = self._get_size(tokenL) @@ -874,6 +903,20 @@ class _BinaryPlistWriter (object): self._fp.write(t) + elif isinstance(value, UID): + if value.data < 0: + raise ValueError("UIDs must be positive") + elif value.data < 1 << 8: + self._fp.write(struct.pack('>BB', 0x80, value)) + elif value.data < 1 << 16: + self._fp.write(struct.pack('>BH', 0x81, value)) + elif value.data < 1 << 32: + self._fp.write(struct.pack('>BL', 0x83, value)) + elif value.data < 1 << 64: + self._fp.write(struct.pack('>BQ', 0x87, value)) + else: + raise OverflowError(value) + elif isinstance(value, (list, tuple)): refs = [self._getrefnum(o) for o in value] s = len(refs) |