mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
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.
This commit is contained in:
parent
e307e5cd06
commit
c981ad16b0
7 changed files with 169 additions and 5 deletions
|
@ -1,5 +1,7 @@
|
|||
# Copyright (C) 2003-2013 Python Software Foundation
|
||||
|
||||
import copy
|
||||
import operator
|
||||
import pickle
|
||||
import unittest
|
||||
import plistlib
|
||||
import os
|
||||
|
@ -10,6 +12,8 @@ import collections
|
|||
from test import support
|
||||
from io import BytesIO
|
||||
|
||||
from plistlib import UID
|
||||
|
||||
ALL_FORMATS=(plistlib.FMT_XML, plistlib.FMT_BINARY)
|
||||
|
||||
# The testdata is generated using Mac/Tools/plistlib_generate_testdata.py
|
||||
|
@ -88,6 +92,17 @@ TESTDATA={
|
|||
ZwB0AHwAiACUAJoApQCuALsAygDTAOQA7QD4AQQBDwEdASsBNgE3ATgBTwFn
|
||||
AW4BcAFyAXQBdgF/AYMBhQGHAYwBlQGbAZ0BnwGhAaUBpwGwAbkBwAHBAcIB
|
||||
xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''),
|
||||
'KEYED_ARCHIVE': binascii.a2b_base64(b'''
|
||||
YnBsaXN0MDDUAQIDBAUGHB1YJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVy
|
||||
VCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVnB5dHlwZVYkY2xhc3NZTlMu
|
||||
c3RyaW5nEAGAAl8QE0tleUFyY2hpdmUgVUlEIFRlc3TTEBESExQZWiRjbGFz
|
||||
c25hbWVYJGNsYXNzZXNbJGNsYXNzaGludHNfEBdPQ19CdWlsdGluUHl0aG9u
|
||||
VW5pY29kZaQVFhcYXxAXT0NfQnVpbHRpblB5dGhvblVuaWNvZGVfEBBPQ19Q
|
||||
eXRob25Vbmljb2RlWE5TU3RyaW5nWE5TT2JqZWN0ohobXxAPT0NfUHl0aG9u
|
||||
U3RyaW5nWE5TU3RyaW5nXxAPTlNLZXllZEFyY2hpdmVy0R4fVHJvb3SAAQAI
|
||||
ABEAGgAjAC0AMgA3ADsAQQBIAE8AVgBgAGIAZAB6AIEAjACVAKEAuwDAANoA
|
||||
7QD2AP8BAgEUAR0BLwEyATcAAAAAAAACAQAAAAAAAAAgAAAAAAAAAAAAAAAA
|
||||
AAABOQ=='''),
|
||||
}
|
||||
|
||||
|
||||
|
@ -151,6 +166,14 @@ class TestPlistlib(unittest.TestCase):
|
|||
with self.subTest(fmt=fmt):
|
||||
self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt)
|
||||
|
||||
def test_invalid_uid(self):
|
||||
with self.assertRaises(TypeError):
|
||||
UID("not an int")
|
||||
with self.assertRaises(ValueError):
|
||||
UID(2 ** 64)
|
||||
with self.assertRaises(ValueError):
|
||||
UID(-19)
|
||||
|
||||
def test_int(self):
|
||||
for pl in [0, 2**8-1, 2**8, 2**16-1, 2**16, 2**32-1, 2**32,
|
||||
2**63-1, 2**64-1, 1, -2**63]:
|
||||
|
@ -200,6 +223,45 @@ class TestPlistlib(unittest.TestCase):
|
|||
data = {'1': {'2': [{'3': [[[[[{'test': b'aaaaaa'}]]]]]}]}}
|
||||
self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
|
||||
|
||||
def test_uid(self):
|
||||
data = UID(1)
|
||||
self.assertEqual(plistlib.loads(plistlib.dumps(data, fmt=plistlib.FMT_BINARY)), data)
|
||||
dict_data = {
|
||||
'uid0': UID(0),
|
||||
'uid2': UID(2),
|
||||
'uid8': UID(2 ** 8),
|
||||
'uid16': UID(2 ** 16),
|
||||
'uid32': UID(2 ** 32),
|
||||
'uid63': UID(2 ** 63)
|
||||
}
|
||||
self.assertEqual(plistlib.loads(plistlib.dumps(dict_data, fmt=plistlib.FMT_BINARY)), dict_data)
|
||||
|
||||
def test_uid_data(self):
|
||||
uid = UID(1)
|
||||
self.assertEqual(uid.data, 1)
|
||||
|
||||
def test_uid_eq(self):
|
||||
self.assertEqual(UID(1), UID(1))
|
||||
self.assertNotEqual(UID(1), UID(2))
|
||||
self.assertNotEqual(UID(1), "not uid")
|
||||
|
||||
def test_uid_hash(self):
|
||||
self.assertEqual(hash(UID(1)), hash(UID(1)))
|
||||
|
||||
def test_uid_repr(self):
|
||||
self.assertEqual(repr(UID(1)), "UID(1)")
|
||||
|
||||
def test_uid_index(self):
|
||||
self.assertEqual(operator.index(UID(1)), 1)
|
||||
|
||||
def test_uid_pickle(self):
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
self.assertEqual(pickle.loads(pickle.dumps(UID(19), protocol=proto)), UID(19))
|
||||
|
||||
def test_uid_copy(self):
|
||||
self.assertEqual(copy.copy(UID(1)), UID(1))
|
||||
self.assertEqual(copy.deepcopy(UID(1)), UID(1))
|
||||
|
||||
def test_appleformatting(self):
|
||||
for use_builtin_types in (True, False):
|
||||
for fmt in ALL_FORMATS:
|
||||
|
@ -648,6 +710,38 @@ class TestPlistlibDeprecated(unittest.TestCase):
|
|||
self.assertEqual(cur, in_data)
|
||||
|
||||
|
||||
class TestKeyedArchive(unittest.TestCase):
|
||||
def test_keyed_archive_data(self):
|
||||
# This is the structure of a NSKeyedArchive packed plist
|
||||
data = {
|
||||
'$version': 100000,
|
||||
'$objects': [
|
||||
'$null', {
|
||||
'pytype': 1,
|
||||
'$class': UID(2),
|
||||
'NS.string': 'KeyArchive UID Test'
|
||||
},
|
||||
{
|
||||
'$classname': 'OC_BuiltinPythonUnicode',
|
||||
'$classes': [
|
||||
'OC_BuiltinPythonUnicode',
|
||||
'OC_PythonUnicode',
|
||||
'NSString',
|
||||
'NSObject'
|
||||
],
|
||||
'$classhints': [
|
||||
'OC_PythonString', 'NSString'
|
||||
]
|
||||
}
|
||||
],
|
||||
'$archiver': 'NSKeyedArchiver',
|
||||
'$top': {
|
||||
'root': UID(1)
|
||||
}
|
||||
}
|
||||
self.assertEqual(plistlib.loads(TESTDATA["KEYED_ARCHIVE"]), data)
|
||||
|
||||
|
||||
class MiscTestCase(unittest.TestCase):
|
||||
def test__all__(self):
|
||||
blacklist = {"PlistFormat", "PLISTHEADER"}
|
||||
|
@ -655,7 +749,7 @@ class MiscTestCase(unittest.TestCase):
|
|||
|
||||
|
||||
def test_main():
|
||||
support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase)
|
||||
support.run_unittest(TestPlistlib, TestPlistlibDeprecated, TestKeyedArchive, MiscTestCase)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue