summaryrefslogtreecommitdiffstats
path: root/Lib/plistlib.py
diff options
context:
space:
mode:
authorJon Janzen <jjjonjanzen@gmail.com>2019-05-15 20:14:38 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2019-05-15 20:14:38 (GMT)
commitc981ad16b0f9740bd3381c96b4227a1faa1a88d9 (patch)
tree11ae5ac3f709d5ddd54a27334207441f7debe025 /Lib/plistlib.py
parente307e5cd06f60e11e4e4f126fae412b9ec66a005 (diff)
downloadcpython-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.py49
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)