Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 6 additions & 66 deletions Doc/library/plistlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ 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
New API, old API deprecated. Support for binary format plists added.

.. versionchanged:: 3.8
Old API removed.

.. seealso::

`PList manual page <https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PropertyLists/>`_
Expand All @@ -44,7 +47,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
Expand All @@ -58,10 +61,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.

Expand All @@ -76,7 +75,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.
Expand Down Expand Up @@ -120,65 +119,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.


The following constants are available:

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,10 @@ The following features and APIs have been removed from Python 3.8:
* "unicode_internal" codec is removed.
(Contributed by Inada Naoki in :issue:`36297`.)

* The old :mod:`plistlib` API has been removed, it was deprecated since Python
3.4: use :func:`load`, :func:`loads`, :func:`dump`, :func:`dumps`, and
standard :class:`bytes` objects instead.
(Contributed by Jon Janzen)

Porting to Python 3.8
=====================
Expand Down
154 changes: 9 additions & 145 deletions Lib/plistlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@
print(pl["aKey"])
"""
__all__ = [
"readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
"Data", "InvalidFileException", "FMT_XML", "FMT_BINARY",
"load", "dump", "loads", "dumps"
"InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps"
]

import binascii
Expand All @@ -69,112 +67,6 @@
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
#
#


#
# XML support
#
Expand Down Expand Up @@ -245,11 +137,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):
Expand Down Expand Up @@ -338,11 +229,7 @@ def end_string(self):
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()))
Expand Down Expand Up @@ -424,9 +311,6 @@ def write_value(self, value):
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)

Expand All @@ -439,9 +323,6 @@ def write_value(self, value):
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
Expand Down Expand Up @@ -535,8 +416,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):
Expand Down Expand Up @@ -636,10 +516,7 @@ def _read_object(self, ref):

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)
Expand Down Expand Up @@ -754,10 +631,6 @@ def _flatten(self, value):
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

Expand All @@ -766,8 +639,6 @@ def _flatten(self, value):
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

Expand Down Expand Up @@ -797,8 +668,6 @@ def _flatten(self, value):
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)]

Expand Down Expand Up @@ -856,10 +725,6 @@ def _write_object(self, value):
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)
Expand Down Expand Up @@ -927,7 +792,7 @@ def _is_fmt_binary(header):
}


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 (readable) file object.
Return the unpacked root object (which usually is a dictionary).
"""
Expand All @@ -945,17 +810,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):
Expand Down
Loading