Issue #14455: plistlib now supports binary plists and has an updated API.
This patch adds support for binary plists on OSX to plistlib (based on a patch by 'dpounces'). The patch also cleans up the API for the plistlib module.
This commit is contained in:
parent
8455723cfb
commit
c5cf797342
|
@ -16,26 +16,21 @@
|
|||
--------------
|
||||
|
||||
This module provides an interface for reading and writing the "property list"
|
||||
XML files used mainly by Mac OS X.
|
||||
files used mainly by Mac OS X and supports both binary and XML plist files.
|
||||
|
||||
The property list (``.plist``) file format is a simple XML pickle supporting
|
||||
The property list (``.plist``) file format is a simple serialization supporting
|
||||
basic object types, like dictionaries, lists, numbers and strings. Usually the
|
||||
top level object is a dictionary.
|
||||
|
||||
To write out and to parse a plist file, use the :func:`writePlist` and
|
||||
:func:`readPlist` functions.
|
||||
To write out and to parse a plist file, use the :func:`dump` and
|
||||
:func:`load` functions.
|
||||
|
||||
To work with plist data in bytes objects, use :func:`writePlistToBytes`
|
||||
and :func:`readPlistFromBytes`.
|
||||
To work with plist data in bytes objects, use :func:`dumps`
|
||||
and :func:`loads`.
|
||||
|
||||
Values can be strings, integers, floats, booleans, tuples, lists, dictionaries
|
||||
(but only with string keys), :class:`Data` or :class:`datetime.datetime`
|
||||
objects. String values (including dictionary keys) have to be unicode strings --
|
||||
they will be written out as UTF-8.
|
||||
|
||||
The ``<data>`` plist type is supported through the :class:`Data` class. This is
|
||||
a thin wrapper around a Python bytes object. Use :class:`Data` if your strings
|
||||
contain control characters.
|
||||
(but only with string keys), :class:`Data`, :class:`bytes`, :class:`bytesarray`
|
||||
or :class:`datetime.datetime` objects.
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
@ -45,37 +40,145 @@ contain control characters.
|
|||
|
||||
This module defines the following functions:
|
||||
|
||||
.. function:: readPlist(pathOrFile)
|
||||
.. function:: load(fp, \*, fmt=None, use_builtin_types=True, dict_type=dict)
|
||||
|
||||
Read a plist file. *pathOrFile* may either be a file name or a (readable and
|
||||
binary) file object. Return the unpacked root object (which usually is a
|
||||
Read a plist file. *fp* should be a readable and binary file object.
|
||||
Return the unpacked root object (which usually is a
|
||||
dictionary).
|
||||
|
||||
The XML data is parsed using the Expat parser from :mod:`xml.parsers.expat`
|
||||
-- see its documentation for possible exceptions on ill-formed XML.
|
||||
Unknown elements will simply be ignored by the plist parser.
|
||||
The *fmt* is the format of the file and the following values are valid:
|
||||
|
||||
* :data:`None`: Autodetect the file format
|
||||
|
||||
* :data:`FMT_XML`: XML file format
|
||||
|
||||
* :data:`FMT_BINARY`: Binary plist format
|
||||
|
||||
If *use_builtin_types* is True (the default) binary data will be returned
|
||||
as instances of :class:`bytes`, otherwise it is returned as instances of
|
||||
:class:`Data`.
|
||||
|
||||
The *dict_type* is the type used for dictionaries that are read from the
|
||||
plist file. The exact structure of the plist can be recovered by using
|
||||
:class:`collections.OrderedDict` (although the order of keys shouldn't be
|
||||
important in plist files).
|
||||
|
||||
XML data for the :data:`FMT_XML` format is parsed using the Expat parser
|
||||
from :mod:`xml.parsers.expat` -- see its documentation for possible
|
||||
exceptions on ill-formed XML. Unknown elements will simply be ignored
|
||||
by the plist parser.
|
||||
|
||||
The parser for the binary format raises :exc:`InvalidFileException`
|
||||
when the file cannot be parsed.
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
|
||||
.. function:: loads(data, \*, fmt=None, use_builtin_types=True, dict_type=dict)
|
||||
|
||||
Load a plist from a bytes object. See :func:`load` for an explanation of
|
||||
the keyword arguments.
|
||||
|
||||
|
||||
.. function:: dump(value, fp, \*, fmt=FMT_XML, sort_keys=True, skipkeys=False)
|
||||
|
||||
Write *value* to a plist file. *Fp* should be a writable, binary
|
||||
file object.
|
||||
|
||||
The *fmt* argument specifies the format of the plist file and can be
|
||||
one of the following values:
|
||||
|
||||
* :data:`FMT_XML`: XML formatted plist file
|
||||
|
||||
* :data:`FMT_BINARY`: Binary formatted plist file
|
||||
|
||||
When *sort_keys* is true (the default) the keys for dictionaries will be
|
||||
written to the plist in sorted order, otherwise they will be written in
|
||||
the iteration order of the dictionary.
|
||||
|
||||
When *skipkeys* is false (the default) the function raises :exc:`TypeError`
|
||||
when a key of a dictionary is not a string, otherwise such keys are skipped.
|
||||
|
||||
A :exc:`TypeError` will be raised if the object is of an unsupported type or
|
||||
a container that contains objects of unsupported types.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Added the *fmt*, *sort_keys* and *skipkeys* arguments.
|
||||
|
||||
|
||||
.. function:: dumps(value, \*, fmt=FMT_XML, sort_keys=True, skipkeys=False)
|
||||
|
||||
Return *value* as a plist-formatted bytes object. See
|
||||
the documentation for :func:`dump` for an explanation of the keyword
|
||||
arguments of this function.
|
||||
|
||||
|
||||
The following functions are deprecated:
|
||||
|
||||
.. function:: readPlist(pathOrFile)
|
||||
|
||||
Read a plist file. *pathOrFile* may be either a file name or a (readable
|
||||
and binary) file object. Returns the unpacked root object (which usually
|
||||
is a dictionary).
|
||||
|
||||
This function calls :func:`load` to do the actual work, the the documentation
|
||||
of :func:`that function <load>` for an explanation of the keyword arguments.
|
||||
|
||||
.. note::
|
||||
|
||||
Dict values in the result have a ``__getattr__`` method that defers
|
||||
to ``__getitem_``. This means that you can use attribute access to
|
||||
access items of these dictionaries.
|
||||
|
||||
.. deprecated: 3.4 Use :func:`load` instead.
|
||||
|
||||
|
||||
.. function:: writePlist(rootObject, pathOrFile)
|
||||
|
||||
Write *rootObject* to a plist file. *pathOrFile* may either be a file name
|
||||
or a (writable and binary) file object.
|
||||
Write *rootObject* to an XML plist file. *pathOrFile* may be either a file name
|
||||
or a (writable and binary) file object
|
||||
|
||||
A :exc:`TypeError` will be raised if the object is of an unsupported type or
|
||||
a container that contains objects of unsupported types.
|
||||
.. deprecated: 3.4 Use :func:`dump` instead.
|
||||
|
||||
|
||||
.. function:: readPlistFromBytes(data)
|
||||
|
||||
Read a plist data from a bytes object. Return the root object.
|
||||
|
||||
See :func:`load` for a description of the keyword arguments.
|
||||
|
||||
.. note::
|
||||
|
||||
Dict values in the result have a ``__getattr__`` method that defers
|
||||
to ``__getitem_``. This means that you can use attribute access to
|
||||
access items of these dictionaries.
|
||||
|
||||
.. deprecated:: 3.4 Use :func:`loads` instead.
|
||||
|
||||
|
||||
.. function:: writePlistToBytes(rootObject)
|
||||
|
||||
Return *rootObject* as a plist-formatted bytes object.
|
||||
Return *rootObject* as an XML plist-formatted bytes object.
|
||||
|
||||
.. deprecated:: 3.4 Use :func:`dumps` instead.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Added the *fmt*, *sort_keys* and *skipkeys* arguments.
|
||||
|
||||
|
||||
The following class is available:
|
||||
The following classes are available:
|
||||
|
||||
.. class:: Dict([dict]):
|
||||
|
||||
Return an extended mapping object with the same value as dictionary
|
||||
*dict*.
|
||||
|
||||
This class is a subclass of :class:`dict` where attribute access can
|
||||
be used to access items. That is, ``aDict.key`` is the same as
|
||||
``aDict['key']`` for getting, setting and deleting items in the mapping.
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
|
||||
.. class:: Data(data)
|
||||
|
||||
|
@ -86,6 +189,24 @@ The following class is available:
|
|||
It has one attribute, :attr:`data`, that can be used to retrieve the Python
|
||||
bytes object stored in it.
|
||||
|
||||
.. deprecated:: 3.4 Use a :class:`bytes` object instead
|
||||
|
||||
|
||||
The following constants are avaiable:
|
||||
|
||||
.. data:: FMT_XML
|
||||
|
||||
The XML format for plist files.
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
|
||||
.. data:: FMT_BINARY
|
||||
|
||||
The binary format for plist files
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
@ -103,13 +224,15 @@ Generating a plist::
|
|||
aTrueValue = True,
|
||||
aFalseValue = False,
|
||||
),
|
||||
someData = Data(b"<binary gunk>"),
|
||||
someMoreData = Data(b"<lots of binary gunk>" * 10),
|
||||
someData = b"<binary gunk>",
|
||||
someMoreData = b"<lots of binary gunk>" * 10,
|
||||
aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
|
||||
)
|
||||
writePlist(pl, fileName)
|
||||
with open(fileName, 'wb') as fp:
|
||||
dump(pl, fp)
|
||||
|
||||
Parsing a plist::
|
||||
|
||||
pl = readPlist(pathOrFile)
|
||||
with open(fileName, 'rb') as fp:
|
||||
pl = load(fp)
|
||||
print(pl["aKey"])
|
||||
|
|
1098
Lib/plistlib.py
1098
Lib/plistlib.py
File diff suppressed because it is too large
Load Diff
|
@ -1,94 +1,87 @@
|
|||
# Copyright (C) 2003 Python Software Foundation
|
||||
# Copyright (C) 2003-2013 Python Software Foundation
|
||||
|
||||
import unittest
|
||||
import plistlib
|
||||
import os
|
||||
import datetime
|
||||
import codecs
|
||||
import binascii
|
||||
import collections
|
||||
from test import support
|
||||
from io import BytesIO
|
||||
|
||||
ALL_FORMATS=(plistlib.FMT_XML, plistlib.FMT_BINARY)
|
||||
|
||||
# This test data was generated through Cocoa's NSDictionary class
|
||||
TESTDATA = b"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aDate</key>
|
||||
<date>2004-10-26T10:33:33Z</date>
|
||||
<key>aDict</key>
|
||||
<dict>
|
||||
<key>aFalseValue</key>
|
||||
<false/>
|
||||
<key>aTrueValue</key>
|
||||
<true/>
|
||||
<key>aUnicodeValue</key>
|
||||
<string>M\xc3\xa4ssig, Ma\xc3\x9f</string>
|
||||
<key>anotherString</key>
|
||||
<string><hello & 'hi' there!></string>
|
||||
<key>deeperDict</key>
|
||||
<dict>
|
||||
<key>a</key>
|
||||
<integer>17</integer>
|
||||
<key>b</key>
|
||||
<real>32.5</real>
|
||||
<key>c</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
<string>text</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>aFloat</key>
|
||||
<real>0.5</real>
|
||||
<key>aList</key>
|
||||
<array>
|
||||
<string>A</string>
|
||||
<string>B</string>
|
||||
<integer>12</integer>
|
||||
<real>32.5</real>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
<integer>3</integer>
|
||||
</array>
|
||||
</array>
|
||||
<key>aString</key>
|
||||
<string>Doodah</string>
|
||||
<key>anEmptyDict</key>
|
||||
<dict/>
|
||||
<key>anEmptyList</key>
|
||||
<array/>
|
||||
<key>anInt</key>
|
||||
<integer>728</integer>
|
||||
<key>nestedData</key>
|
||||
<array>
|
||||
<data>
|
||||
PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5r
|
||||
PgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5
|
||||
IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBi
|
||||
aW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3Rz
|
||||
IG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQID
|
||||
PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAw==
|
||||
</data>
|
||||
</array>
|
||||
<key>someData</key>
|
||||
<data>
|
||||
PGJpbmFyeSBndW5rPg==
|
||||
</data>
|
||||
<key>someMoreData</key>
|
||||
<data>
|
||||
PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8
|
||||
bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs
|
||||
b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxv
|
||||
dHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90
|
||||
cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAw==
|
||||
</data>
|
||||
<key>\xc3\x85benraa</key>
|
||||
<string>That was a unicode key.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
""".replace(b" " * 8, b"\t") # Apple as well as plistlib.py output hard tabs
|
||||
# The testdata is generated using Mac/Tools/plistlib_generate_testdata.py
|
||||
# (which using PyObjC to control the Cocoa classes for generating plists)
|
||||
TESTDATA={
|
||||
plistlib.FMT_XML: binascii.a2b_base64(b'''
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU
|
||||
WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO
|
||||
IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w
|
||||
LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+YURh
|
||||
dGU8L2tleT4KCTxkYXRlPjIwMDQtMTAtMjZUMTA6MzM6MzNaPC9kYXRlPgoJ
|
||||
PGtleT5hRGljdDwva2V5PgoJPGRpY3Q+CgkJPGtleT5hRmFsc2VWYWx1ZTwv
|
||||
a2V5PgoJCTxmYWxzZS8+CgkJPGtleT5hVHJ1ZVZhbHVlPC9rZXk+CgkJPHRy
|
||||
dWUvPgoJCTxrZXk+YVVuaWNvZGVWYWx1ZTwva2V5PgoJCTxzdHJpbmc+TcOk
|
||||
c3NpZywgTWHDnzwvc3RyaW5nPgoJCTxrZXk+YW5vdGhlclN0cmluZzwva2V5
|
||||
PgoJCTxzdHJpbmc+Jmx0O2hlbGxvICZhbXA7ICdoaScgdGhlcmUhJmd0Ozwv
|
||||
c3RyaW5nPgoJCTxrZXk+ZGVlcGVyRGljdDwva2V5PgoJCTxkaWN0PgoJCQk8
|
||||
a2V5PmE8L2tleT4KCQkJPGludGVnZXI+MTc8L2ludGVnZXI+CgkJCTxrZXk+
|
||||
Yjwva2V5PgoJCQk8cmVhbD4zMi41PC9yZWFsPgoJCQk8a2V5PmM8L2tleT4K
|
||||
CQkJPGFycmF5PgoJCQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJCTxpbnRl
|
||||
Z2VyPjI8L2ludGVnZXI+CgkJCQk8c3RyaW5nPnRleHQ8L3N0cmluZz4KCQkJ
|
||||
PC9hcnJheT4KCQk8L2RpY3Q+Cgk8L2RpY3Q+Cgk8a2V5PmFGbG9hdDwva2V5
|
||||
PgoJPHJlYWw+MC41PC9yZWFsPgoJPGtleT5hTGlzdDwva2V5PgoJPGFycmF5
|
||||
PgoJCTxzdHJpbmc+QTwvc3RyaW5nPgoJCTxzdHJpbmc+Qjwvc3RyaW5nPgoJ
|
||||
CTxpbnRlZ2VyPjEyPC9pbnRlZ2VyPgoJCTxyZWFsPjMyLjU8L3JlYWw+CgkJ
|
||||
PGFycmF5PgoJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQk8aW50ZWdlcj4y
|
||||
PC9pbnRlZ2VyPgoJCQk8aW50ZWdlcj4zPC9pbnRlZ2VyPgoJCTwvYXJyYXk+
|
||||
Cgk8L2FycmF5PgoJPGtleT5hU3RyaW5nPC9rZXk+Cgk8c3RyaW5nPkRvb2Rh
|
||||
aDwvc3RyaW5nPgoJPGtleT5hbkVtcHR5RGljdDwva2V5PgoJPGRpY3QvPgoJ
|
||||
PGtleT5hbkVtcHR5TGlzdDwva2V5PgoJPGFycmF5Lz4KCTxrZXk+YW5JbnQ8
|
||||
L2tleT4KCTxpbnRlZ2VyPjcyODwvaW50ZWdlcj4KCTxrZXk+bmVzdGVkRGF0
|
||||
YTwva2V5PgoJPGFycmF5PgoJCTxkYXRhPgoJCVBHeHZkSE1nYjJZZ1ltbHVZ
|
||||
WEo1SUdkMWJtcytBQUVDQXp4c2IzUnpJRzltSUdKcGJtRnllU0JuZFc1cgoJ
|
||||
CVBnQUJBZ004Ykc5MGN5QnZaaUJpYVc1aGNua2daM1Z1YXo0QUFRSURQR3h2
|
||||
ZEhNZ2IyWWdZbWx1WVhKNQoJCUlHZDFibXMrQUFFQ0F6eHNiM1J6SUc5bUlH
|
||||
SnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJHOTBjeUJ2WmlCaQoJCWFXNWhjbmtn
|
||||
WjNWdWF6NEFBUUlEUEd4dmRITWdiMllnWW1sdVlYSjVJR2QxYm1zK0FBRUNB
|
||||
enhzYjNSegoJCUlHOW1JR0pwYm1GeWVTQm5kVzVyUGdBQkFnTThiRzkwY3lC
|
||||
dlppQmlhVzVoY25rZ1ozVnVhejRBQVFJRAoJCVBHeHZkSE1nYjJZZ1ltbHVZ
|
||||
WEo1SUdkMWJtcytBQUVDQXc9PQoJCTwvZGF0YT4KCTwvYXJyYXk+Cgk8a2V5
|
||||
PnNvbWVEYXRhPC9rZXk+Cgk8ZGF0YT4KCVBHSnBibUZ5ZVNCbmRXNXJQZz09
|
||||
Cgk8L2RhdGE+Cgk8a2V5PnNvbWVNb3JlRGF0YTwva2V5PgoJPGRhdGE+CglQ
|
||||
R3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMrQUFFQ0F6eHNiM1J6SUc5bUlH
|
||||
SnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOAoJYkc5MGN5QnZaaUJpYVc1aGNua2da
|
||||
M1Z1YXo0QUFRSURQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMrQUFFQ0F6
|
||||
eHMKCWIzUnpJRzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004Ykc5MGN5QnZa
|
||||
aUJpYVc1aGNua2daM1Z1YXo0QUFRSURQR3h2CglkSE1nYjJZZ1ltbHVZWEo1
|
||||
SUdkMWJtcytBQUVDQXp4c2IzUnpJRzltSUdKcGJtRnllU0JuZFc1clBnQUJB
|
||||
Z004Ykc5MAoJY3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJRFBHeHZkSE1n
|
||||
YjJZZ1ltbHVZWEo1SUdkMWJtcytBQUVDQXc9PQoJPC9kYXRhPgoJPGtleT7D
|
||||
hWJlbnJhYTwva2V5PgoJPHN0cmluZz5UaGF0IHdhcyBhIHVuaWNvZGUga2V5
|
||||
Ljwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+Cg=='''),
|
||||
plistlib.FMT_BINARY: binascii.a2b_base64(b'''
|
||||
YnBsaXN0MDDcAQIDBAUGBwgJCgsMDQ4iIykqKywtLy4wVWFEYXRlVWFEaWN0
|
||||
VmFGbG9hdFVhTGlzdFdhU3RyaW5nW2FuRW1wdHlEaWN0W2FuRW1wdHlMaXN0
|
||||
VWFuSW50Wm5lc3RlZERhdGFYc29tZURhdGFcc29tZU1vcmVEYXRhZwDFAGIA
|
||||
ZQBuAHIAYQBhM0GcuX30AAAA1Q8QERITFBUWFxhbYUZhbHNlVmFsdWVaYVRy
|
||||
dWVWYWx1ZV1hVW5pY29kZVZhbHVlXWFub3RoZXJTdHJpbmdaZGVlcGVyRGlj
|
||||
dAgJawBNAOQAcwBzAGkAZwAsACAATQBhAN9fEBU8aGVsbG8gJiAnaGknIHRo
|
||||
ZXJlIT7TGRobHB0eUWFRYlFjEBEjQEBAAAAAAACjHyAhEAEQAlR0ZXh0Iz/g
|
||||
AAAAAAAApSQlJh0nUUFRQhAMox8gKBADVkRvb2RhaNCgEQLYoS5PEPo8bG90
|
||||
cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAEC
|
||||
Azxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vu
|
||||
az4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFy
|
||||
eSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2Yg
|
||||
YmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90
|
||||
cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDTTxiaW5hcnkgZ3Vuaz5fEBdUaGF0IHdh
|
||||
cyBhIHVuaWNvZGUga2V5LgAIACEAJwAtADQAOgBCAE4AWgBgAGsAdACBAJAA
|
||||
mQCkALAAuwDJANcA4gDjAOQA+wETARoBHAEeASABIgErAS8BMQEzATgBQQFH
|
||||
AUkBSwFNAVEBUwFaAVsBXAFfAWECXgJsAAAAAAAAAgEAAAAAAAAAMQAAAAAA
|
||||
AAAAAAAAAAAAAoY='''),
|
||||
}
|
||||
|
||||
|
||||
class TestPlistlib(unittest.TestCase):
|
||||
|
@ -99,7 +92,7 @@ class TestPlistlib(unittest.TestCase):
|
|||
except:
|
||||
pass
|
||||
|
||||
def _create(self):
|
||||
def _create(self, fmt=None):
|
||||
pl = dict(
|
||||
aString="Doodah",
|
||||
aList=["A", "B", 12, 32.5, [1, 2, 3]],
|
||||
|
@ -112,9 +105,9 @@ class TestPlistlib(unittest.TestCase):
|
|||
aFalseValue=False,
|
||||
deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
|
||||
),
|
||||
someData = plistlib.Data(b"<binary gunk>"),
|
||||
someMoreData = plistlib.Data(b"<lots of binary gunk>\0\1\2\3" * 10),
|
||||
nestedData = [plistlib.Data(b"<lots of binary gunk>\0\1\2\3" * 10)],
|
||||
someData = b"<binary gunk>",
|
||||
someMoreData = b"<lots of binary gunk>\0\1\2\3" * 10,
|
||||
nestedData = [b"<lots of binary gunk>\0\1\2\3" * 10],
|
||||
aDate = datetime.datetime(2004, 10, 26, 10, 33, 33),
|
||||
anEmptyDict = dict(),
|
||||
anEmptyList = list()
|
||||
|
@ -129,49 +122,191 @@ class TestPlistlib(unittest.TestCase):
|
|||
|
||||
def test_io(self):
|
||||
pl = self._create()
|
||||
plistlib.writePlist(pl, support.TESTFN)
|
||||
pl2 = plistlib.readPlist(support.TESTFN)
|
||||
with open(support.TESTFN, 'wb') as fp:
|
||||
plistlib.dump(pl, fp)
|
||||
|
||||
with open(support.TESTFN, 'rb') as fp:
|
||||
pl2 = plistlib.load(fp)
|
||||
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
|
||||
self.assertRaises(AttributeError, plistlib.dump, pl, 'filename')
|
||||
self.assertRaises(AttributeError, plistlib.load, 'filename')
|
||||
|
||||
|
||||
def test_bytes(self):
|
||||
pl = self._create()
|
||||
data = plistlib.writePlistToBytes(pl)
|
||||
pl2 = plistlib.readPlistFromBytes(data)
|
||||
data = plistlib.dumps(pl)
|
||||
pl2 = plistlib.loads(data)
|
||||
self.assertNotIsInstance(pl, plistlib._InternalDict)
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
data2 = plistlib.writePlistToBytes(pl2)
|
||||
data2 = plistlib.dumps(pl2)
|
||||
self.assertEqual(data, data2)
|
||||
|
||||
def test_indentation_array(self):
|
||||
data = [[[[[[[[{'test': plistlib.Data(b'aaaaaa')}]]]]]]]]
|
||||
self.assertEqual(plistlib.readPlistFromBytes(plistlib.writePlistToBytes(data)), data)
|
||||
data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]]
|
||||
self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
|
||||
|
||||
def test_indentation_dict(self):
|
||||
data = {'1': {'2': {'3': {'4': {'5': {'6': {'7': {'8': {'9': plistlib.Data(b'aaaaaa')}}}}}}}}}
|
||||
self.assertEqual(plistlib.readPlistFromBytes(plistlib.writePlistToBytes(data)), data)
|
||||
data = {'1': {'2': {'3': {'4': {'5': {'6': {'7': {'8': {'9': b'aaaaaa'}}}}}}}}}
|
||||
self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
|
||||
|
||||
def test_indentation_dict_mix(self):
|
||||
data = {'1': {'2': [{'3': [[[[[{'test': plistlib.Data(b'aaaaaa')}]]]]]}]}}
|
||||
self.assertEqual(plistlib.readPlistFromBytes(plistlib.writePlistToBytes(data)), data)
|
||||
data = {'1': {'2': [{'3': [[[[[{'test': b'aaaaaa'}]]]]]}]}}
|
||||
self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
|
||||
|
||||
def test_appleformatting(self):
|
||||
pl = plistlib.readPlistFromBytes(TESTDATA)
|
||||
data = plistlib.writePlistToBytes(pl)
|
||||
self.assertEqual(data, TESTDATA,
|
||||
"generated data was not identical to Apple's output")
|
||||
for use_builtin_types in (True, False):
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt, use_builtin_types=use_builtin_types):
|
||||
pl = plistlib.loads(TESTDATA[fmt],
|
||||
use_builtin_types=use_builtin_types)
|
||||
data = plistlib.dumps(pl, fmt=fmt)
|
||||
self.assertEqual(data, TESTDATA[fmt],
|
||||
"generated data was not identical to Apple's output")
|
||||
|
||||
|
||||
def test_appleformattingfromliteral(self):
|
||||
pl = self._create()
|
||||
pl2 = plistlib.readPlistFromBytes(TESTDATA)
|
||||
self.assertEqual(dict(pl), dict(pl2),
|
||||
"generated data was not identical to Apple's output")
|
||||
self.maxDiff = None
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
pl = self._create(fmt=fmt)
|
||||
pl2 = plistlib.loads(TESTDATA[fmt])
|
||||
self.assertEqual(dict(pl), dict(pl2),
|
||||
"generated data was not identical to Apple's output")
|
||||
|
||||
def test_bytesio(self):
|
||||
from io import BytesIO
|
||||
b = BytesIO()
|
||||
pl = self._create()
|
||||
plistlib.writePlist(pl, b)
|
||||
pl2 = plistlib.readPlist(BytesIO(b.getvalue()))
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
b = BytesIO()
|
||||
pl = self._create(fmt=fmt)
|
||||
plistlib.dump(pl, b, fmt=fmt)
|
||||
pl2 = plistlib.load(BytesIO(b.getvalue()))
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
|
||||
def test_keysort_bytesio(self):
|
||||
pl = collections.OrderedDict()
|
||||
pl['b'] = 1
|
||||
pl['a'] = 2
|
||||
pl['c'] = 3
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
for sort_keys in (False, True):
|
||||
with self.subTest(fmt=fmt, sort_keys=sort_keys):
|
||||
b = BytesIO()
|
||||
|
||||
plistlib.dump(pl, b, fmt=fmt, sort_keys=sort_keys)
|
||||
pl2 = plistlib.load(BytesIO(b.getvalue()),
|
||||
dict_type=collections.OrderedDict)
|
||||
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
if sort_keys:
|
||||
self.assertEqual(list(pl2.keys()), ['a', 'b', 'c'])
|
||||
else:
|
||||
self.assertEqual(list(pl2.keys()), ['b', 'a', 'c'])
|
||||
|
||||
def test_keysort(self):
|
||||
pl = collections.OrderedDict()
|
||||
pl['b'] = 1
|
||||
pl['a'] = 2
|
||||
pl['c'] = 3
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
for sort_keys in (False, True):
|
||||
with self.subTest(fmt=fmt, sort_keys=sort_keys):
|
||||
data = plistlib.dumps(pl, fmt=fmt, sort_keys=sort_keys)
|
||||
pl2 = plistlib.loads(data, dict_type=collections.OrderedDict)
|
||||
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
if sort_keys:
|
||||
self.assertEqual(list(pl2.keys()), ['a', 'b', 'c'])
|
||||
else:
|
||||
self.assertEqual(list(pl2.keys()), ['b', 'a', 'c'])
|
||||
|
||||
def test_keys_no_string(self):
|
||||
pl = { 42: 'aNumber' }
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt)
|
||||
|
||||
b = BytesIO()
|
||||
self.assertRaises(TypeError, plistlib.dump, pl, b, fmt=fmt)
|
||||
|
||||
def test_skipkeys(self):
|
||||
pl = {
|
||||
42: 'aNumber',
|
||||
'snake': 'aWord',
|
||||
}
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
data = plistlib.dumps(
|
||||
pl, fmt=fmt, skipkeys=True, sort_keys=False)
|
||||
|
||||
pl2 = plistlib.loads(data)
|
||||
self.assertEqual(pl2, {'snake': 'aWord'})
|
||||
|
||||
fp = BytesIO()
|
||||
plistlib.dump(
|
||||
pl, fp, fmt=fmt, skipkeys=True, sort_keys=False)
|
||||
data = fp.getvalue()
|
||||
pl2 = plistlib.loads(fp.getvalue())
|
||||
self.assertEqual(pl2, {'snake': 'aWord'})
|
||||
|
||||
def test_tuple_members(self):
|
||||
pl = {
|
||||
'first': (1, 2),
|
||||
'second': (1, 2),
|
||||
'third': (3, 4),
|
||||
}
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
data = plistlib.dumps(pl, fmt=fmt)
|
||||
pl2 = plistlib.loads(data)
|
||||
self.assertEqual(pl2, {
|
||||
'first': [1, 2],
|
||||
'second': [1, 2],
|
||||
'third': [3, 4],
|
||||
})
|
||||
self.assertIsNot(pl2['first'], pl2['second'])
|
||||
|
||||
def test_list_members(self):
|
||||
pl = {
|
||||
'first': [1, 2],
|
||||
'second': [1, 2],
|
||||
'third': [3, 4],
|
||||
}
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
data = plistlib.dumps(pl, fmt=fmt)
|
||||
pl2 = plistlib.loads(data)
|
||||
self.assertEqual(pl2, {
|
||||
'first': [1, 2],
|
||||
'second': [1, 2],
|
||||
'third': [3, 4],
|
||||
})
|
||||
self.assertIsNot(pl2['first'], pl2['second'])
|
||||
|
||||
def test_dict_members(self):
|
||||
pl = {
|
||||
'first': {'a': 1},
|
||||
'second': {'a': 1},
|
||||
'third': {'b': 2 },
|
||||
}
|
||||
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
data = plistlib.dumps(pl, fmt=fmt)
|
||||
pl2 = plistlib.loads(data)
|
||||
self.assertEqual(pl2, {
|
||||
'first': {'a': 1},
|
||||
'second': {'a': 1},
|
||||
'third': {'b': 2 },
|
||||
})
|
||||
self.assertIsNot(pl2['first'], pl2['second'])
|
||||
|
||||
def test_controlcharacters(self):
|
||||
for i in range(128):
|
||||
|
@ -179,25 +314,27 @@ class TestPlistlib(unittest.TestCase):
|
|||
testString = "string containing %s" % c
|
||||
if i >= 32 or c in "\r\n\t":
|
||||
# \r, \n and \t are the only legal control chars in XML
|
||||
plistlib.writePlistToBytes(testString)
|
||||
plistlib.dumps(testString, fmt=plistlib.FMT_XML)
|
||||
else:
|
||||
self.assertRaises(ValueError,
|
||||
plistlib.writePlistToBytes,
|
||||
plistlib.dumps,
|
||||
testString)
|
||||
|
||||
def test_nondictroot(self):
|
||||
test1 = "abc"
|
||||
test2 = [1, 2, 3, "abc"]
|
||||
result1 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test1))
|
||||
result2 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test2))
|
||||
self.assertEqual(test1, result1)
|
||||
self.assertEqual(test2, result2)
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
test1 = "abc"
|
||||
test2 = [1, 2, 3, "abc"]
|
||||
result1 = plistlib.loads(plistlib.dumps(test1, fmt=fmt))
|
||||
result2 = plistlib.loads(plistlib.dumps(test2, fmt=fmt))
|
||||
self.assertEqual(test1, result1)
|
||||
self.assertEqual(test2, result2)
|
||||
|
||||
def test_invalidarray(self):
|
||||
for i in ["<key>key inside an array</key>",
|
||||
"<key>key inside an array2</key><real>3</real>",
|
||||
"<true/><key>key inside an array3</key>"]:
|
||||
self.assertRaises(ValueError, plistlib.readPlistFromBytes,
|
||||
self.assertRaises(ValueError, plistlib.loads,
|
||||
("<plist><array>%s</array></plist>"%i).encode())
|
||||
|
||||
def test_invaliddict(self):
|
||||
|
@ -206,22 +343,130 @@ class TestPlistlib(unittest.TestCase):
|
|||
"<string>missing key</string>",
|
||||
"<key>k1</key><string>v1</string><real>5.3</real>"
|
||||
"<key>k1</key><key>k2</key><string>double key</string>"]:
|
||||
self.assertRaises(ValueError, plistlib.readPlistFromBytes,
|
||||
self.assertRaises(ValueError, plistlib.loads,
|
||||
("<plist><dict>%s</dict></plist>"%i).encode())
|
||||
self.assertRaises(ValueError, plistlib.readPlistFromBytes,
|
||||
self.assertRaises(ValueError, plistlib.loads,
|
||||
("<plist><array><dict>%s</dict></array></plist>"%i).encode())
|
||||
|
||||
def test_invalidinteger(self):
|
||||
self.assertRaises(ValueError, plistlib.readPlistFromBytes,
|
||||
self.assertRaises(ValueError, plistlib.loads,
|
||||
b"<plist><integer>not integer</integer></plist>")
|
||||
|
||||
def test_invalidreal(self):
|
||||
self.assertRaises(ValueError, plistlib.readPlistFromBytes,
|
||||
self.assertRaises(ValueError, plistlib.loads,
|
||||
b"<plist><integer>not real</integer></plist>")
|
||||
|
||||
def test_xml_encodings(self):
|
||||
base = TESTDATA[plistlib.FMT_XML]
|
||||
|
||||
for xml_encoding, encoding, bom in [
|
||||
(b'utf-8', 'utf-8', codecs.BOM_UTF8),
|
||||
(b'utf-16', 'utf-16-le', codecs.BOM_UTF16_LE),
|
||||
(b'utf-16', 'utf-16-be', codecs.BOM_UTF16_BE),
|
||||
# Expat does not support UTF-32
|
||||
#(b'utf-32', 'utf-32-le', codecs.BOM_UTF32_LE),
|
||||
#(b'utf-32', 'utf-32-be', codecs.BOM_UTF32_BE),
|
||||
]:
|
||||
|
||||
pl = self._create(fmt=plistlib.FMT_XML)
|
||||
with self.subTest(encoding=encoding):
|
||||
data = base.replace(b'UTF-8', xml_encoding)
|
||||
data = bom + data.decode('utf-8').encode(encoding)
|
||||
pl2 = plistlib.loads(data)
|
||||
self.assertEqual(dict(pl), dict(pl2))
|
||||
|
||||
|
||||
class TestPlistlibDeprecated(unittest.TestCase):
|
||||
def test_io_deprecated(self):
|
||||
pl_in = {
|
||||
'key': 42,
|
||||
'sub': {
|
||||
'key': 9,
|
||||
'alt': 'value',
|
||||
'data': b'buffer',
|
||||
}
|
||||
}
|
||||
pl_out = plistlib._InternalDict({
|
||||
'key': 42,
|
||||
'sub': plistlib._InternalDict({
|
||||
'key': 9,
|
||||
'alt': 'value',
|
||||
'data': plistlib.Data(b'buffer'),
|
||||
})
|
||||
})
|
||||
|
||||
self.addCleanup(support.unlink, support.TESTFN)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
plistlib.writePlist(pl_in, support.TESTFN)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
pl2 = plistlib.readPlist(support.TESTFN)
|
||||
|
||||
self.assertEqual(pl_out, pl2)
|
||||
|
||||
os.unlink(support.TESTFN)
|
||||
|
||||
with open(support.TESTFN, 'wb') as fp:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
plistlib.writePlist(pl_in, fp)
|
||||
|
||||
with open(support.TESTFN, 'rb') as fp:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
pl2 = plistlib.readPlist(fp)
|
||||
|
||||
self.assertEqual(pl_out, pl2)
|
||||
|
||||
def test_bytes_deprecated(self):
|
||||
pl = {
|
||||
'key': 42,
|
||||
'sub': {
|
||||
'key': 9,
|
||||
'alt': 'value',
|
||||
'data': b'buffer',
|
||||
}
|
||||
}
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
data = plistlib.writePlistToBytes(pl)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
pl2 = plistlib.readPlistFromBytes(data)
|
||||
|
||||
self.assertIsInstance(pl2, plistlib._InternalDict)
|
||||
self.assertEqual(pl2, plistlib._InternalDict(
|
||||
key=42,
|
||||
sub=plistlib._InternalDict(
|
||||
key=9,
|
||||
alt='value',
|
||||
data=plistlib.Data(b'buffer'),
|
||||
)
|
||||
))
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
data2 = plistlib.writePlistToBytes(pl2)
|
||||
self.assertEqual(data, data2)
|
||||
|
||||
def test_dataobject_deprecated(self):
|
||||
in_data = { 'key': plistlib.Data(b'hello') }
|
||||
out_data = { 'key': b'hello' }
|
||||
|
||||
buf = plistlib.dumps(in_data)
|
||||
|
||||
cur = plistlib.loads(buf)
|
||||
self.assertEqual(cur, out_data)
|
||||
self.assertNotEqual(cur, in_data)
|
||||
|
||||
cur = plistlib.loads(buf, use_builtin_types=False)
|
||||
self.assertNotEqual(cur, out_data)
|
||||
self.assertEqual(cur, in_data)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
cur = plistlib.readPlistFromBytes(buf)
|
||||
self.assertNotEqual(cur, out_data)
|
||||
self.assertEqual(cur, in_data)
|
||||
|
||||
|
||||
def test_main():
|
||||
support.run_unittest(TestPlistlib)
|
||||
support.run_unittest(TestPlistlib, TestPlistlibDeprecated)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from Cocoa import NSMutableDictionary, NSMutableArray, NSString, NSDate
|
||||
from Cocoa import NSPropertyListSerialization, NSPropertyListOpenStepFormat
|
||||
from Cocoa import NSPropertyListXMLFormat_v1_0, NSPropertyListBinaryFormat_v1_0
|
||||
from Cocoa import CFUUIDCreateFromString, NSNull, NSUUID, CFPropertyListCreateData
|
||||
from Cocoa import NSURL
|
||||
|
||||
import datetime
|
||||
from collections import OrderedDict
|
||||
import binascii
|
||||
|
||||
FORMATS=[
|
||||
# ('openstep', NSPropertyListOpenStepFormat),
|
||||
('plistlib.FMT_XML', NSPropertyListXMLFormat_v1_0),
|
||||
('plistlib.FMT_BINARY', NSPropertyListBinaryFormat_v1_0),
|
||||
]
|
||||
|
||||
def nsstr(value):
|
||||
return NSString.alloc().initWithString_(value)
|
||||
|
||||
|
||||
def main():
|
||||
pl = OrderedDict()
|
||||
|
||||
seconds = datetime.datetime(2004, 10, 26, 10, 33, 33, tzinfo=datetime.timezone(datetime.timedelta(0))).timestamp()
|
||||
pl[nsstr('aDate')] = NSDate.dateWithTimeIntervalSince1970_(seconds)
|
||||
|
||||
pl[nsstr('aDict')] = d = OrderedDict()
|
||||
d[nsstr('aFalseValue')] = False
|
||||
d[nsstr('aTrueValue')] = True
|
||||
d[nsstr('aUnicodeValue')] = "M\xe4ssig, Ma\xdf"
|
||||
d[nsstr('anotherString')] = "<hello & 'hi' there!>"
|
||||
d[nsstr('deeperDict')] = dd = OrderedDict()
|
||||
dd[nsstr('a')] = 17
|
||||
dd[nsstr('b')] = 32.5
|
||||
dd[nsstr('c')] = a = NSMutableArray.alloc().init()
|
||||
a.append(1)
|
||||
a.append(2)
|
||||
a.append(nsstr('text'))
|
||||
|
||||
pl[nsstr('aFloat')] = 0.5
|
||||
|
||||
pl[nsstr('aList')] = a = NSMutableArray.alloc().init()
|
||||
a.append(nsstr('A'))
|
||||
a.append(nsstr('B'))
|
||||
a.append(12)
|
||||
a.append(32.5)
|
||||
aa = NSMutableArray.alloc().init()
|
||||
a.append(aa)
|
||||
aa.append(1)
|
||||
aa.append(2)
|
||||
aa.append(3)
|
||||
|
||||
pl[nsstr('aString')] = nsstr('Doodah')
|
||||
|
||||
pl[nsstr('anEmptyDict')] = NSMutableDictionary.alloc().init()
|
||||
|
||||
pl[nsstr('anEmptyList')] = NSMutableArray.alloc().init()
|
||||
|
||||
pl[nsstr('anInt')] = 728
|
||||
|
||||
pl[nsstr('nestedData')] = a = NSMutableArray.alloc().init()
|
||||
a.append(b'''<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03''')
|
||||
|
||||
|
||||
pl[nsstr('someData')] = b'<binary gunk>'
|
||||
|
||||
pl[nsstr('someMoreData')] = b'''<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03<lots of binary gunk>\x00\x01\x02\x03'''
|
||||
|
||||
pl[nsstr('\xc5benraa')] = nsstr("That was a unicode key.")
|
||||
|
||||
print("TESTDATA={")
|
||||
for fmt_name, fmt_key in FORMATS:
|
||||
data, error = NSPropertyListSerialization.dataWithPropertyList_format_options_error_(
|
||||
pl, fmt_key, 0, None)
|
||||
if data is None:
|
||||
print("Cannot serialize", fmt_name, error)
|
||||
|
||||
else:
|
||||
print(" %s: binascii.a2b_base64(b'''\n %s'''),"%(fmt_name, _encode_base64(bytes(data)).decode('ascii')[:-1]))
|
||||
|
||||
print("}")
|
||||
print()
|
||||
|
||||
def _encode_base64(s, maxlinelength=60):
|
||||
maxbinsize = (maxlinelength//4)*3
|
||||
pieces = []
|
||||
for i in range(0, len(s), maxbinsize):
|
||||
chunk = s[i : i + maxbinsize]
|
||||
pieces.append(binascii.b2a_base64(chunk))
|
||||
return b' '.join(pieces)
|
||||
|
||||
main()
|
Loading…
Reference in New Issue