Skip to content

Commit 829e4d0

Browse files
hugovkvstinner
andauthored
gh-141510: Support frozendict in plistlib (#145590)
Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 67354b2 commit 829e4d0

File tree

5 files changed

+32
-10
lines changed

5 files changed

+32
-10
lines changed

Doc/library/plistlib.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ and XML plist files.
1818

1919
The property list (``.plist``) file format is a simple serialization supporting
2020
basic object types, like dictionaries, lists, numbers and strings. Usually the
21-
top level object is a dictionary.
21+
top level object is a dictionary or a frozen dictionary.
2222

2323
To write out and to parse a plist file, use the :func:`dump` and
2424
:func:`load` functions.

Doc/whatsnew/3.15.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ For example::
217217

218218
The following standard library modules have been updated to accept
219219
:class:`!frozendict`: :mod:`copy`, :mod:`decimal`, :mod:`json`, :mod:`marshal`,
220-
:mod:`pickle`, :mod:`pprint` and :mod:`xml.etree.ElementTree`.
220+
:mod:`plistlib` (only for serialization), :mod:`pickle`, :mod:`pprint` and
221+
:mod:`xml.etree.ElementTree`.
221222

222223
:func:`eval` and :func:`exec` accept :class:`!frozendict` for *globals*, and
223224
:func:`type` and :meth:`str.maketrans` accept :class:`!frozendict` for *dict*.

Lib/plistlib.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
The property list (.plist) file format is a simple XML pickle supporting
44
basic object types, like dictionaries, lists, numbers and strings.
5-
Usually the top level object is a dictionary.
5+
Usually the top level object is a dictionary or a frozen dictionary.
66
77
To write out a plist file, use the dump(value, file)
88
function. 'value' is the top level object, 'file' is
@@ -357,7 +357,7 @@ def write_value(self, value):
357357
elif isinstance(value, float):
358358
self.simple_element("real", repr(value))
359359

360-
elif isinstance(value, dict):
360+
elif isinstance(value, (dict, frozendict)):
361361
self.write_dict(value)
362362

363363
elif isinstance(value, (bytes, bytearray)):
@@ -715,7 +715,7 @@ def _flatten(self, value):
715715
self._objidtable[id(value)] = refnum
716716

717717
# And finally recurse into containers
718-
if isinstance(value, dict):
718+
if isinstance(value, (dict, frozendict)):
719719
keys = []
720720
values = []
721721
items = value.items()
@@ -836,7 +836,7 @@ def _write_object(self, value):
836836
self._write_size(0xA0, s)
837837
self._fp.write(struct.pack('>' + self._ref_format * s, *refs))
838838

839-
elif isinstance(value, dict):
839+
elif isinstance(value, (dict, frozendict)):
840840
keyRefs, valRefs = [], []
841841

842842
if self._sort_keys:
@@ -869,18 +869,18 @@ def _is_fmt_binary(header):
869869
# Generic bits
870870
#
871871

872-
_FORMATS={
873-
FMT_XML: dict(
872+
_FORMATS=frozendict({
873+
FMT_XML: frozendict(
874874
detect=_is_fmt_xml,
875875
parser=_PlistParser,
876876
writer=_PlistWriter,
877877
),
878-
FMT_BINARY: dict(
878+
FMT_BINARY: frozendict(
879879
detect=_is_fmt_binary,
880880
parser=_BinaryPlistParser,
881881
writer=_BinaryPlistWriter,
882882
)
883-
}
883+
})
884884

885885

886886
def load(fp, *, fmt=None, dict_type=dict, aware_datetime=False):

Lib/test/test_plistlib.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,25 @@ def test_dict_members(self):
792792
})
793793
self.assertIsNot(pl2['first'], pl2['second'])
794794

795+
def test_frozendict(self):
796+
pl = frozendict(
797+
aString="Doodah",
798+
anInt=728,
799+
aDict=frozendict(
800+
anotherString="hello",
801+
aTrueValue=True,
802+
),
803+
aList=["A", "B", 12],
804+
)
805+
806+
for fmt in ALL_FORMATS:
807+
with self.subTest(fmt=fmt):
808+
data = plistlib.dumps(pl, fmt=fmt)
809+
pl2 = plistlib.loads(data)
810+
self.assertEqual(pl2, dict(pl))
811+
self.assertIsInstance(pl2, dict)
812+
self.assertIsInstance(pl2['aDict'], dict)
813+
795814
def test_controlcharacters(self):
796815
for i in range(128):
797816
c = chr(i)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Support :class:`frozendict` in :mod:`plistlib`, for serialization only.
2+
Patch by Hugo van Kemenade.

0 commit comments

Comments
 (0)