diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index d84fcac0ef2..f71aaa2354e 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -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 `_ @@ -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 ` 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 ```` 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: diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 0a0b91670ca..009a66adff7 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -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 ===================== diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 04f8a87634d..a84bb57371e 100644 --- a/Lib/plistlib.py +++ b/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): diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 5c2d0265079..d3ef6f8ffe3 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -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__': diff --git a/Misc/NEWS.d/next/Library/2019-03-23-16-42-46.bpo-36409.ZYVKao.rst b/Misc/NEWS.d/next/Library/2019-03-23-16-42-46.bpo-36409.ZYVKao.rst new file mode 100644 index 00000000000..e516d3190f8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-03-23-16-42-46.bpo-36409.ZYVKao.rst @@ -0,0 +1 @@ +Remove the old plistlib API deprecated in Python 3.4 \ No newline at end of file