From eec508c12740136281a37122b3a5476cc5645f95 Mon Sep 17 00:00:00 2001 From: Wulian <1055917385@qq.com> Date: Mon, 28 Oct 2024 06:52:46 +0800 Subject: [PATCH 1/7] --- Doc/library/plistlib.rst | 12 ++++++-- Lib/plistlib.py | 61 ++++++++++++++++++++++++++++----------- Lib/test/test_plistlib.py | 7 +++++ 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 2906ebe7822f52..2d731d1e61b164 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -97,7 +97,7 @@ This module defines the following functions: .. versionchanged:: 3.13 *data* can be a string when *fmt* equals :data:`FMT_XML`. -.. function:: dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, aware_datetime=False) +.. function:: dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, aware_datetime=False, compact=False) Write *value* to a plist file. *Fp* should be a writable, binary file object. @@ -120,6 +120,8 @@ This module defines the following functions: is set as an :ref:`aware object `, it will convert to UTC timezone before writing it. + When *compact* is true, the XML elements are written without indentation. + A :exc:`TypeError` will be raised if the object is of an unsupported type or a container that contains objects of unsupported types. @@ -131,8 +133,11 @@ This module defines the following functions: .. versionchanged:: 3.13 The keyword-only parameter *aware_datetime* has been added. + .. versionchanged:: 3.13 + The keyword-only parameter *compact* has been added. + -.. function:: dumps(value, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, aware_datetime=False) +.. function:: dumps(value, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, aware_datetime=False, compact=False) Return *value* as a plist-formatted bytes object. See the documentation for :func:`dump` for an explanation of the keyword @@ -140,6 +145,9 @@ This module defines the following functions: .. versionadded:: 3.4 + .. versionchanged:: 3.13 + The keyword-only parameter *compact* has been added. + The following classes are available: diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 67e832db217319..80fe8d44aac2d8 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -287,30 +287,42 @@ def end_date(self): class _DumbXMLWriter: - def __init__(self, file, indent_level=0, indent="\t"): + def __init__(self, file, indent_level=0, indent="\t", compact=False): self.file = file self.stack = [] self._indent_level = indent_level self.indent = indent + self.compact = compact def begin_element(self, element): self.stack.append(element) - self.writeln("<%s>" % element) + if not self.compact: + self.writeln("<%s>" % element) + else: + self.write("<%s>" % element) self._indent_level += 1 def end_element(self, element): assert self._indent_level > 0 assert self.stack.pop() == element self._indent_level -= 1 - self.writeln("" % element) + if not self.compact: + self.writeln("" % element) + else: + self.write("" % element) def simple_element(self, element, value=None): if value is not None: value = _escape(value) - self.writeln("<%s>%s" % (element, value, element)) - + if not self.compact: + self.writeln("<%s>%s" % (element, value, element)) + else: + self.write("<%s>%s" % (element, value, element)) else: - self.writeln("<%s/>" % element) + if not self.compact: + self.writeln("<%s/>" % element) + else: + self.write("<%s/>" % element) def writeln(self, line): if line: @@ -323,23 +335,39 @@ def writeln(self, line): self.file.write(line) self.file.write(b'\n') + def write(self, line): + if line: + # plist has fixed encoding of utf-8 + + # XXX: is this test needed? + if isinstance(line, str): + line = line.encode('utf-8') + self.file.write(self._indent_level * self.indent) + self.file.write(line) + class _PlistWriter(_DumbXMLWriter): def __init__( self, file, indent_level=0, indent=b"\t", writeHeader=1, - sort_keys=True, skipkeys=False, aware_datetime=False): + sort_keys=True, skipkeys=False, aware_datetime=False, compact=False): if writeHeader: file.write(PLISTHEADER) - _DumbXMLWriter.__init__(self, file, indent_level, indent) + _DumbXMLWriter.__init__(self, file, indent_level, indent, compact) self._sort_keys = sort_keys self._skipkeys = skipkeys self._aware_datetime = aware_datetime def write(self, value): - self.writeln("") + if not self.compact: + self.writeln("") + else: + self.write("") self.write_value(value) - self.writeln("") + if not self.compact: + self.writeln("") + else: + self.write("") def write_value(self, value): if isinstance(value, str): @@ -724,9 +752,8 @@ def _flatten(self, value): if not isinstance(k, str): if self._skipkeys: continue - raise TypeError("keys must be strings") - keys.append(k) - values.append(v) + keys.append(k) + values.append(v) for o in itertools.chain(keys, values): self._flatten(o) @@ -917,7 +944,7 @@ def loads(value, *, fmt=None, dict_type=dict, aware_datetime=False): def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, - aware_datetime=False): + aware_datetime=False, compact=False): """Write 'value' to a .plist file. 'fp' should be a writable, binary file object. """ @@ -925,15 +952,15 @@ def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, raise ValueError("Unsupported format: %r"%(fmt,)) writer = _FORMATS[fmt]["writer"](fp, sort_keys=sort_keys, skipkeys=skipkeys, - aware_datetime=aware_datetime) + aware_datetime=aware_datetime, compact=compact) writer.write(value) def dumps(value, *, fmt=FMT_XML, skipkeys=False, sort_keys=True, - aware_datetime=False): + aware_datetime=False, compact=False): """Return a bytes object with the contents for a .plist file. """ fp = BytesIO() dump(value, fp, fmt=fmt, skipkeys=skipkeys, sort_keys=sort_keys, - aware_datetime=aware_datetime) + aware_datetime=aware_datetime, compact=compact) return fp.getvalue() diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index b231b05f864ab9..67bcad03047fdc 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -900,6 +900,13 @@ def test_dump_naive_datetime_with_aware_datetime_option(self): expected = dt.astimezone(datetime.UTC).replace(tzinfo=None) self.assertEqual(parsed, expected) + def test_compact_mode(self): + pl = self._create() + for fmt in ALL_FORMATS: + with self.subTest(fmt=fmt): + data = plistlib.dumps(pl, fmt=fmt, compact=True) + pl2 = plistlib.loads(data) + self.assertEqual(dict(pl), dict(pl2)) class TestBinaryPlistlib(unittest.TestCase): From 1fcb78962580f5cb47cca210774c79c6112f453d Mon Sep 17 00:00:00 2001 From: Wulian Date: Mon, 28 Oct 2024 20:47:29 +0800 Subject: [PATCH 2/7] finish --- Doc/library/plistlib.rst | 9 ++-- Doc/whatsnew/3.14.rst | 8 +++ Lib/plistlib.py | 51 +++++-------------- Lib/test/test_plistlib.py | 1 - ...-10-28-20-19-35.gh-issue-113056.ndF_hX.rst | 3 ++ 5 files changed, 28 insertions(+), 44 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 2d731d1e61b164..829c34a7115bff 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -120,7 +120,7 @@ This module defines the following functions: is set as an :ref:`aware object `, it will convert to UTC timezone before writing it. - When *compact* is true, the XML elements are written without indentation. + When *compact* is true, the XML elements will be written without indentation. A :exc:`TypeError` will be raised if the object is of an unsupported type or a container that contains objects of unsupported types. @@ -133,8 +133,8 @@ This module defines the following functions: .. versionchanged:: 3.13 The keyword-only parameter *aware_datetime* has been added. - .. versionchanged:: 3.13 - The keyword-only parameter *compact* has been added. + .. versionchanged:: 3.14 + The parameter *compact* has been added. .. function:: dumps(value, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, aware_datetime=False, compact=False) @@ -145,9 +145,6 @@ This module defines the following functions: .. versionadded:: 3.4 - .. versionchanged:: 3.13 - The keyword-only parameter *compact* has been added. - The following classes are available: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index a6f595ccf08bf4..982a4bed0df351 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -413,6 +413,14 @@ pydoc (Contributed by Jelle Zijlstra in :gh:`101552`.) +plist +----- +* Add a new parameter *compact* to :func:`plist.dump` and + :func:`plist.dumps`. When *compact* is true, the XML elements will + be written without indentation. + (Contributed by Jiahao Li in :gh:`113056`.) + + symtable -------- diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 80fe8d44aac2d8..40699885055128 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -296,33 +296,21 @@ def __init__(self, file, indent_level=0, indent="\t", compact=False): def begin_element(self, element): self.stack.append(element) - if not self.compact: - self.writeln("<%s>" % element) - else: - self.write("<%s>" % element) + self.writeln("<%s>" % element) self._indent_level += 1 def end_element(self, element): assert self._indent_level > 0 assert self.stack.pop() == element self._indent_level -= 1 - if not self.compact: - self.writeln("" % element) - else: - self.write("" % element) + self.writeln("" % element) def simple_element(self, element, value=None): if value is not None: value = _escape(value) - if not self.compact: - self.writeln("<%s>%s" % (element, value, element)) - else: - self.write("<%s>%s" % (element, value, element)) + self.writeln("<%s>%s" % (element, value, element)) else: - if not self.compact: - self.writeln("<%s/>" % element) - else: - self.write("<%s/>" % element) + self.writeln("<%s/>" % element) def writeln(self, line): if line: @@ -331,18 +319,11 @@ def writeln(self, line): # XXX: is this test needed? if isinstance(line, str): line = line.encode('utf-8') + if not self.compact: self.file.write(self._indent_level * self.indent) self.file.write(line) - self.file.write(b'\n') - - def write(self, line): - if line: - # plist has fixed encoding of utf-8 - - # XXX: is this test needed? - if isinstance(line, str): - line = line.encode('utf-8') - self.file.write(self._indent_level * self.indent) + self.file.write(b'\n') + else: self.file.write(line) @@ -359,15 +340,9 @@ def __init__( self._aware_datetime = aware_datetime def write(self, value): - if not self.compact: - self.writeln("") - else: - self.write("") + self.writeln("") self.write_value(value) - if not self.compact: - self.writeln("") - else: - self.write("") + self.writeln("") def write_value(self, value): if isinstance(value, str): @@ -670,11 +645,12 @@ def _count_to_size(count): _scalars = (str, int, float, datetime.datetime, bytes) class _BinaryPlistWriter (object): - def __init__(self, fp, sort_keys, skipkeys, aware_datetime=False): + def __init__(self, fp, sort_keys, skipkeys, aware_datetime=False, compact=False): self._fp = fp self._sort_keys = sort_keys self._skipkeys = skipkeys self._aware_datetime = aware_datetime + self._compact = compact def write(self, value): @@ -752,8 +728,9 @@ def _flatten(self, value): if not isinstance(k, str): if self._skipkeys: continue - keys.append(k) - values.append(v) + raise TypeError("keys must be strings") + keys.append(k) + values.append(v) for o in itertools.chain(keys, values): self._flatten(o) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 67bcad03047fdc..51a743e84a5a74 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -1,4 +1,3 @@ -# Copyright (C) 2003-2013 Python Software Foundation import copy import operator import pickle diff --git a/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst b/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst new file mode 100644 index 00000000000000..b91ac32450d12b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst @@ -0,0 +1,3 @@ +Add a new parameter *compact* to :func:`plist.dump` and :func:`plist.dumps`. +When *compact* is true, the XML elements will be written without +indentation. From f619d4b8fdbb52292d95576adb5f2b54f89b8405 Mon Sep 17 00:00:00 2001 From: Wulian Date: Mon, 28 Oct 2024 20:55:42 +0800 Subject: [PATCH 3/7] typo --- Doc/whatsnew/3.14.rst | 8 ++++---- .../2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 982a4bed0df351..dce9a4d2521b71 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -413,10 +413,10 @@ pydoc (Contributed by Jelle Zijlstra in :gh:`101552`.) -plist ------ -* Add a new parameter *compact* to :func:`plist.dump` and - :func:`plist.dumps`. When *compact* is true, the XML elements will +plistlib +-------- +* Add a new parameter *compact* to :func:`plistlib.dump` and + :func:`plistlib.dumps`. When *compact* is true, the XML elements will be written without indentation. (Contributed by Jiahao Li in :gh:`113056`.) diff --git a/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst b/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst index b91ac32450d12b..71f762319dc998 100644 --- a/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst +++ b/Misc/NEWS.d/next/Library/2024-10-28-20-19-35.gh-issue-113056.ndF_hX.rst @@ -1,3 +1,3 @@ -Add a new parameter *compact* to :func:`plist.dump` and :func:`plist.dumps`. -When *compact* is true, the XML elements will be written without -indentation. +Add a new parameter *compact* to :func:`plistlib.dump` and +:func:`plistlib.dumps`. When *compact* is true, the XML elements will be +written without indentation. From f7e40ba78dab7dfb5814193bd844d54426fbfe8b Mon Sep 17 00:00:00 2001 From: Wulian Date: Mon, 28 Oct 2024 20:56:24 +0800 Subject: [PATCH 4/7] lint --- Doc/whatsnew/3.14.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index dce9a4d2521b71..67db41c63e7cc5 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -415,6 +415,7 @@ pydoc plistlib -------- + * Add a new parameter *compact* to :func:`plistlib.dump` and :func:`plistlib.dumps`. When *compact* is true, the XML elements will be written without indentation. From 5d5cb3d3821bdf47280878cafe140dcdd470a259 Mon Sep 17 00:00:00 2001 From: Wulian <1055917385@qq.com> Date: Wed, 30 Oct 2024 19:42:20 +0800 Subject: [PATCH 5/7] fix not work if line is empty --- Lib/plistlib.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 40699885055128..2f2bca75858cc0 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -319,12 +319,12 @@ def writeln(self, line): # XXX: is this test needed? if isinstance(line, str): line = line.encode('utf-8') - if not self.compact: - self.file.write(self._indent_level * self.indent) - self.file.write(line) - self.file.write(b'\n') - else: - self.file.write(line) + if not self.compact: + self.file.write(self._indent_level * self.indent) + self.file.write(line) + self.file.write(b'\n') + else: + self.file.write(line) class _PlistWriter(_DumbXMLWriter): From 01933ba20893b80ac1695371e76185d06796931e Mon Sep 17 00:00:00 2001 From: Wulian <1055917385@qq.com> Date: Thu, 31 Oct 2024 06:26:02 +0800 Subject: [PATCH 6/7] Update plistlib.py --- Lib/plistlib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 2f2bca75858cc0..6b55cf17eae24c 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -650,7 +650,6 @@ def __init__(self, fp, sort_keys, skipkeys, aware_datetime=False, compact=False) self._sort_keys = sort_keys self._skipkeys = skipkeys self._aware_datetime = aware_datetime - self._compact = compact def write(self, value): From 5fb0950f5a2c6a302d6453f47ecb2e119f4df589 Mon Sep 17 00:00:00 2001 From: Wulian <1055917385@qq.com> Date: Thu, 31 Oct 2024 06:29:14 +0800 Subject: [PATCH 7/7] keyword-only parameter --- Doc/library/plistlib.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 829c34a7115bff..52e1bc0f76a620 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -134,7 +134,7 @@ This module defines the following functions: The keyword-only parameter *aware_datetime* has been added. .. versionchanged:: 3.14 - The parameter *compact* has been added. + The keyword-only parameter *compact* has been added. .. function:: dumps(value, *, fmt=FMT_XML, sort_keys=True, skipkeys=False, aware_datetime=False, compact=False)