bpo-36409: Remove old plistlib API deprecated in 3.4 (GH-15615)
* Remove implementation for old plistlib API deprecated in 3.4
This commit is contained in:
parent
9b51570ffd
commit
ce81a925ef
|
@ -30,7 +30,7 @@ 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`, :class:`bytes`, :class:`bytesarray`
|
||||
(but only with string keys), :class:`bytes`, :class:`bytearray`
|
||||
or :class:`datetime.datetime` objects.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
|
@ -40,6 +40,9 @@ or :class:`datetime.datetime` objects.
|
|||
Support added for reading and writing :class:`UID` tokens in binary plists as used
|
||||
by NSKeyedArchiver and NSKeyedUnarchiver.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Old API removed.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`PList manual page <https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PropertyLists/>`_
|
||||
|
@ -48,7 +51,7 @@ or :class:`datetime.datetime` objects.
|
|||
|
||||
This module defines the following functions:
|
||||
|
||||
.. function:: load(fp, \*, fmt=None, use_builtin_types=True, dict_type=dict)
|
||||
.. function:: load(fp, \*, fmt=None, dict_type=dict)
|
||||
|
||||
Read a plist file. *fp* should be a readable and binary file object.
|
||||
Return the unpacked root object (which usually is a
|
||||
|
@ -62,10 +65,6 @@ This module defines the following functions:
|
|||
|
||||
* :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.
|
||||
|
||||
|
@ -80,7 +79,7 @@ This module defines the following functions:
|
|||
.. versionadded:: 3.4
|
||||
|
||||
|
||||
.. function:: loads(data, \*, fmt=None, use_builtin_types=True, dict_type=dict)
|
||||
.. function:: loads(data, \*, fmt=None, dict_type=dict)
|
||||
|
||||
Load a plist from a bytes object. See :func:`load` for an explanation of
|
||||
the keyword arguments.
|
||||
|
@ -124,75 +123,6 @@ This module defines the following functions:
|
|||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
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, see the documentation
|
||||
of :func:`that function <load>` for an explanation of the keyword arguments.
|
||||
|
||||
.. deprecated:: 3.4 Use :func:`load` instead.
|
||||
|
||||
.. versionchanged:: 3.7
|
||||
Dict values in the result are now normal dicts. You no longer can use
|
||||
attribute access to access items of these dictionaries.
|
||||
|
||||
|
||||
.. function:: writePlist(rootObject, pathOrFile)
|
||||
|
||||
Write *rootObject* to an XML plist file. *pathOrFile* may be either a file name
|
||||
or a (writable and binary) file object
|
||||
|
||||
.. 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.
|
||||
|
||||
.. deprecated:: 3.4 Use :func:`loads` instead.
|
||||
|
||||
.. versionchanged:: 3.7
|
||||
Dict values in the result are now normal dicts. You no longer can use
|
||||
attribute access to access items of these dictionaries.
|
||||
|
||||
|
||||
.. function:: writePlistToBytes(rootObject)
|
||||
|
||||
Return *rootObject* as an XML plist-formatted bytes object.
|
||||
|
||||
.. deprecated:: 3.4 Use :func:`dumps` instead.
|
||||
|
||||
|
||||
The following classes are available:
|
||||
|
||||
.. class:: Data(data)
|
||||
|
||||
Return a "data" wrapper object around the bytes object *data*. This is used
|
||||
in functions converting from/to plists to represent the ``<data>`` type
|
||||
available in plists.
|
||||
|
||||
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.
|
||||
|
||||
.. class:: UID(data)
|
||||
|
||||
Wraps an :class:`int`. This is used when reading or writing NSKeyedArchiver
|
||||
encoded data, which contains UID (see PList manual).
|
||||
|
||||
It has one attribute, :attr:`data` which can be used to retrieve the int value
|
||||
of the UID. :attr:`data` must be in the range `0 <= data <= 2**64`.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
The following constants are available:
|
||||
|
||||
|
|
|
@ -213,6 +213,12 @@ Removed
|
|||
instead. The ``xml.etree.cElementTree`` module has been removed.
|
||||
(Contributed by Serhiy Storchaka in :issue:`36543`.)
|
||||
|
||||
* The old :mod:`plistlib` API has been removed, it was deprecated since Python
|
||||
3.4. Use the :func:`load`, :func:`loads`, :func:`dump`, and :func:`dumps`
|
||||
functions. Additionally, the ``use_builtin_types`` parameter was removed,
|
||||
standard :class:`bytes` objects are always used.
|
||||
(Contributed by Jon Janzen in :issue:`36409`.)
|
||||
|
||||
|
||||
Porting to Python 3.9
|
||||
=====================
|
||||
|
|
155
Lib/plistlib.py
155
Lib/plistlib.py
|
@ -46,9 +46,7 @@ Parse Plist example:
|
|||
print(pl["aKey"])
|
||||
"""
|
||||
__all__ = [
|
||||
"readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
|
||||
"Data", "InvalidFileException", "FMT_XML", "FMT_BINARY",
|
||||
"load", "dump", "loads", "dumps", "UID"
|
||||
"InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"
|
||||
]
|
||||
|
||||
import binascii
|
||||
|
@ -69,112 +67,6 @@ PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
|
|||
globals().update(PlistFormat.__members__)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# Deprecated functionality
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _maybe_open(pathOrFile, mode):
|
||||
if isinstance(pathOrFile, str):
|
||||
with open(pathOrFile, mode) as fp:
|
||||
yield fp
|
||||
|
||||
else:
|
||||
yield pathOrFile
|
||||
|
||||
|
||||
def readPlist(pathOrFile):
|
||||
"""
|
||||
Read a .plist from a path or file. pathOrFile should either
|
||||
be a file name, or a readable binary file object.
|
||||
|
||||
This function is deprecated, use load instead.
|
||||
"""
|
||||
warn("The readPlist function is deprecated, use load() instead",
|
||||
DeprecationWarning, 2)
|
||||
|
||||
with _maybe_open(pathOrFile, 'rb') as fp:
|
||||
return load(fp, fmt=None, use_builtin_types=False)
|
||||
|
||||
def writePlist(value, pathOrFile):
|
||||
"""
|
||||
Write 'value' to a .plist file. 'pathOrFile' may either be a
|
||||
file name or a (writable) file object.
|
||||
|
||||
This function is deprecated, use dump instead.
|
||||
"""
|
||||
warn("The writePlist function is deprecated, use dump() instead",
|
||||
DeprecationWarning, 2)
|
||||
with _maybe_open(pathOrFile, 'wb') as fp:
|
||||
dump(value, fp, fmt=FMT_XML, sort_keys=True, skipkeys=False)
|
||||
|
||||
|
||||
def readPlistFromBytes(data):
|
||||
"""
|
||||
Read a plist data from a bytes object. Return the root object.
|
||||
|
||||
This function is deprecated, use loads instead.
|
||||
"""
|
||||
warn("The readPlistFromBytes function is deprecated, use loads() instead",
|
||||
DeprecationWarning, 2)
|
||||
return load(BytesIO(data), fmt=None, use_builtin_types=False)
|
||||
|
||||
|
||||
def writePlistToBytes(value):
|
||||
"""
|
||||
Return 'value' as a plist-formatted bytes object.
|
||||
|
||||
This function is deprecated, use dumps instead.
|
||||
"""
|
||||
warn("The writePlistToBytes function is deprecated, use dumps() instead",
|
||||
DeprecationWarning, 2)
|
||||
f = BytesIO()
|
||||
dump(value, f, fmt=FMT_XML, sort_keys=True, skipkeys=False)
|
||||
return f.getvalue()
|
||||
|
||||
|
||||
class Data:
|
||||
"""
|
||||
Wrapper for binary data.
|
||||
|
||||
This class is deprecated, use a bytes object instead.
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
if not isinstance(data, bytes):
|
||||
raise TypeError("data must be as bytes")
|
||||
self.data = data
|
||||
|
||||
@classmethod
|
||||
def fromBase64(cls, data):
|
||||
# base64.decodebytes just calls binascii.a2b_base64;
|
||||
# it seems overkill to use both base64 and binascii.
|
||||
return cls(_decode_base64(data))
|
||||
|
||||
def asBase64(self, maxlinelength=76):
|
||||
return _encode_base64(self.data, maxlinelength)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, self.__class__):
|
||||
return self.data == other.data
|
||||
elif isinstance(other, bytes):
|
||||
return self.data == other
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%s)" % (self.__class__.__name__, repr(self.data))
|
||||
|
||||
#
|
||||
#
|
||||
# End of deprecated functionality
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class UID:
|
||||
def __init__(self, data):
|
||||
if not isinstance(data, int):
|
||||
|
@ -202,7 +94,6 @@ class UID:
|
|||
def __hash__(self):
|
||||
return hash(self.data)
|
||||
|
||||
|
||||
#
|
||||
# XML support
|
||||
#
|
||||
|
@ -273,11 +164,10 @@ def _escape(text):
|
|||
return text
|
||||
|
||||
class _PlistParser:
|
||||
def __init__(self, use_builtin_types, dict_type):
|
||||
def __init__(self, dict_type):
|
||||
self.stack = []
|
||||
self.current_key = None
|
||||
self.root = None
|
||||
self._use_builtin_types = use_builtin_types
|
||||
self._dict_type = dict_type
|
||||
|
||||
def parse(self, fileobj):
|
||||
|
@ -366,11 +256,7 @@ class _PlistParser:
|
|||
self.add_object(self.get_data())
|
||||
|
||||
def end_data(self):
|
||||
if self._use_builtin_types:
|
||||
self.add_object(_decode_base64(self.get_data()))
|
||||
|
||||
else:
|
||||
self.add_object(Data.fromBase64(self.get_data()))
|
||||
self.add_object(_decode_base64(self.get_data()))
|
||||
|
||||
def end_date(self):
|
||||
self.add_object(_date_from_string(self.get_data()))
|
||||
|
@ -452,9 +338,6 @@ class _PlistWriter(_DumbXMLWriter):
|
|||
elif isinstance(value, dict):
|
||||
self.write_dict(value)
|
||||
|
||||
elif isinstance(value, Data):
|
||||
self.write_data(value)
|
||||
|
||||
elif isinstance(value, (bytes, bytearray)):
|
||||
self.write_bytes(value)
|
||||
|
||||
|
@ -467,9 +350,6 @@ class _PlistWriter(_DumbXMLWriter):
|
|||
else:
|
||||
raise TypeError("unsupported type: %s" % type(value))
|
||||
|
||||
def write_data(self, data):
|
||||
self.write_bytes(data.data)
|
||||
|
||||
def write_bytes(self, data):
|
||||
self.begin_element("data")
|
||||
self._indent_level -= 1
|
||||
|
@ -563,8 +443,7 @@ class _BinaryPlistParser:
|
|||
|
||||
see also: http://opensource.apple.com/source/CF/CF-744.18/CFBinaryPList.c
|
||||
"""
|
||||
def __init__(self, use_builtin_types, dict_type):
|
||||
self._use_builtin_types = use_builtin_types
|
||||
def __init__(self, dict_type):
|
||||
self._dict_type = dict_type
|
||||
|
||||
def parse(self, fp):
|
||||
|
@ -664,10 +543,7 @@ class _BinaryPlistParser:
|
|||
|
||||
elif tokenH == 0x40: # data
|
||||
s = self._get_size(tokenL)
|
||||
if self._use_builtin_types:
|
||||
result = self._fp.read(s)
|
||||
else:
|
||||
result = Data(self._fp.read(s))
|
||||
result = self._fp.read(s)
|
||||
|
||||
elif tokenH == 0x50: # ascii string
|
||||
s = self._get_size(tokenL)
|
||||
|
@ -783,10 +659,6 @@ class _BinaryPlistWriter (object):
|
|||
if (type(value), value) in self._objtable:
|
||||
return
|
||||
|
||||
elif isinstance(value, Data):
|
||||
if (type(value.data), value.data) in self._objtable:
|
||||
return
|
||||
|
||||
elif id(value) in self._objidtable:
|
||||
return
|
||||
|
||||
|
@ -795,8 +667,6 @@ class _BinaryPlistWriter (object):
|
|||
self._objlist.append(value)
|
||||
if isinstance(value, _scalars):
|
||||
self._objtable[(type(value), value)] = refnum
|
||||
elif isinstance(value, Data):
|
||||
self._objtable[(type(value.data), value.data)] = refnum
|
||||
else:
|
||||
self._objidtable[id(value)] = refnum
|
||||
|
||||
|
@ -826,8 +696,6 @@ class _BinaryPlistWriter (object):
|
|||
def _getrefnum(self, value):
|
||||
if isinstance(value, _scalars):
|
||||
return self._objtable[(type(value), value)]
|
||||
elif isinstance(value, Data):
|
||||
return self._objtable[(type(value.data), value.data)]
|
||||
else:
|
||||
return self._objidtable[id(value)]
|
||||
|
||||
|
@ -885,10 +753,6 @@ class _BinaryPlistWriter (object):
|
|||
f = (value - datetime.datetime(2001, 1, 1)).total_seconds()
|
||||
self._fp.write(struct.pack('>Bd', 0x33, f))
|
||||
|
||||
elif isinstance(value, Data):
|
||||
self._write_size(0x40, len(value.data))
|
||||
self._fp.write(value.data)
|
||||
|
||||
elif isinstance(value, (bytes, bytearray)):
|
||||
self._write_size(0x40, len(value))
|
||||
self._fp.write(value)
|
||||
|
@ -970,7 +834,7 @@ _FORMATS={
|
|||
}
|
||||
|
||||
|
||||
def load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict):
|
||||
def load(fp, *, fmt=None, dict_type=dict):
|
||||
"""Read a .plist file. 'fp' should be a readable and binary file object.
|
||||
Return the unpacked root object (which usually is a dictionary).
|
||||
"""
|
||||
|
@ -988,17 +852,16 @@ def load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict):
|
|||
else:
|
||||
P = _FORMATS[fmt]['parser']
|
||||
|
||||
p = P(use_builtin_types=use_builtin_types, dict_type=dict_type)
|
||||
p = P(dict_type=dict_type)
|
||||
return p.parse(fp)
|
||||
|
||||
|
||||
def loads(value, *, fmt=None, use_builtin_types=True, dict_type=dict):
|
||||
def loads(value, *, fmt=None, dict_type=dict):
|
||||
"""Read a .plist file from a bytes object.
|
||||
Return the unpacked root object (which usually is a dictionary).
|
||||
"""
|
||||
fp = BytesIO(value)
|
||||
return load(
|
||||
fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
|
||||
return load(fp, fmt=fmt, dict_type=dict_type)
|
||||
|
||||
|
||||
def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False):
|
||||
|
|
|
@ -263,14 +263,12 @@ class TestPlistlib(unittest.TestCase):
|
|||
self.assertEqual(copy.deepcopy(UID(1)), UID(1))
|
||||
|
||||
def test_appleformatting(self):
|
||||
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")
|
||||
for fmt in ALL_FORMATS:
|
||||
with self.subTest(fmt=fmt):
|
||||
pl = plistlib.loads(TESTDATA[fmt])
|
||||
data = plistlib.dumps(pl, fmt=fmt)
|
||||
self.assertEqual(data, TESTDATA[fmt],
|
||||
"generated data was not identical to Apple's output")
|
||||
|
||||
|
||||
def test_appleformattingfromliteral(self):
|
||||
|
@ -524,8 +522,7 @@ class TestBinaryPlistlib(unittest.TestCase):
|
|||
# Test effectiveness of saving duplicated objects
|
||||
for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde',
|
||||
datetime.datetime(2004, 10, 26, 10, 33, 33),
|
||||
plistlib.Data(b'abcde'), bytearray(b'abcde'),
|
||||
[12, 345], (12, 345), {'12': 345}):
|
||||
bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
|
||||
with self.subTest(x=x):
|
||||
data = plistlib.dumps([x]*1000, fmt=plistlib.FMT_BINARY)
|
||||
self.assertLess(len(data), 1100, repr(data))
|
||||
|
@ -533,8 +530,7 @@ class TestBinaryPlistlib(unittest.TestCase):
|
|||
def test_identity(self):
|
||||
for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde',
|
||||
datetime.datetime(2004, 10, 26, 10, 33, 33),
|
||||
plistlib.Data(b'abcde'), bytearray(b'abcde'),
|
||||
[12, 345], (12, 345), {'12': 345}):
|
||||
bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
|
||||
with self.subTest(x=x):
|
||||
data = plistlib.dumps([x]*2, fmt=plistlib.FMT_BINARY)
|
||||
a, b = plistlib.loads(data)
|
||||
|
@ -621,95 +617,6 @@ class TestBinaryPlistlib(unittest.TestCase):
|
|||
plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
|
||||
|
||||
|
||||
class TestPlistlibDeprecated(unittest.TestCase):
|
||||
def test_io_deprecated(self):
|
||||
pl_in = {
|
||||
'key': 42,
|
||||
'sub': {
|
||||
'key': 9,
|
||||
'alt': 'value',
|
||||
'data': b'buffer',
|
||||
}
|
||||
}
|
||||
pl_out = {
|
||||
'key': 42,
|
||||
'sub': {
|
||||
'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, dict)
|
||||
self.assertEqual(pl2, dict(
|
||||
key=42,
|
||||
sub=dict(
|
||||
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.assertEqual(cur, in_data)
|
||||
|
||||
cur = plistlib.loads(buf, use_builtin_types=False)
|
||||
self.assertEqual(cur, out_data)
|
||||
self.assertEqual(cur, in_data)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
cur = plistlib.readPlistFromBytes(buf)
|
||||
self.assertEqual(cur, out_data)
|
||||
self.assertEqual(cur, in_data)
|
||||
|
||||
|
||||
class TestKeyedArchive(unittest.TestCase):
|
||||
def test_keyed_archive_data(self):
|
||||
# This is the structure of a NSKeyedArchive packed plist
|
||||
|
@ -749,7 +656,7 @@ class MiscTestCase(unittest.TestCase):
|
|||
|
||||
|
||||
def test_main():
|
||||
support.run_unittest(TestPlistlib, TestPlistlibDeprecated, TestKeyedArchive, MiscTestCase)
|
||||
support.run_unittest(TestPlistlib, TestKeyedArchive, MiscTestCase)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Remove the old plistlib API deprecated in Python 3.4
|
Loading…
Reference in New Issue