From 18c6ce8445bbab924751025425f2027974bd3e2c Mon Sep 17 00:00:00 2001 From: AN Long Date: Sat, 30 Dec 2023 20:30:08 +0800 Subject: [PATCH 01/10] gh-113537: support loads str in plistlib.loads --- Doc/library/plistlib.rst | 6 +++--- Lib/plistlib.py | 2 ++ Lib/test/test_plistlib.py | 8 ++++++++ .../2023-12-30-20-30-05.gh-issue-113537.v1W5_X.rst | 1 + 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-30-20-30-05.gh-issue-113537.v1W5_X.rst diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 732ef3536863cc..00b22905004313 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -27,7 +27,7 @@ top level object is a dictionary. To write out and to parse a plist file, use the :func:`dump` and :func:`load` functions. -To work with plist data in bytes objects, use :func:`dumps` +To work with plist data in bytes or string objects, use :func:`dumps` and :func:`loads`. Values can be strings, integers, floats, booleans, tuples, lists, dictionaries @@ -82,8 +82,8 @@ This module defines the following functions: .. 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. + Load a plist from a bytes or string object. See :func:`load` for an + explanation of the keyword arguments. .. versionadded:: 3.4 diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 3292c30d5fb29b..a68b54e3cdde20 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -888,6 +888,8 @@ 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). """ + if type(value) == str: + value = value.encode() fp = BytesIO(value) return load(fp, fmt=fmt, dict_type=dict_type) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index b08ababa341cfe..c1cc63cb40d544 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -508,6 +508,14 @@ def test_bytes(self): data2 = plistlib.dumps(pl2) self.assertEqual(data, data2) + def test_loads_str(self): + pl = self._create() + b = plistlib.dumps(pl) + s = b.decode() + self.assertEqual(type(s), str) + pl2 = plistlib.loads(s) + self.assertEqual(pl, pl2) + def test_indentation_array(self): data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]] self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) diff --git a/Misc/NEWS.d/next/Library/2023-12-30-20-30-05.gh-issue-113537.v1W5_X.rst b/Misc/NEWS.d/next/Library/2023-12-30-20-30-05.gh-issue-113537.v1W5_X.rst new file mode 100644 index 00000000000000..a6150815b285a9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-30-20-30-05.gh-issue-113537.v1W5_X.rst @@ -0,0 +1 @@ +Support loads ``str`` in :func:`plistlib.loads`. From 489c7313886c09954dec913b49aa0fd1bd67f6b1 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 31 Dec 2023 20:54:00 +0800 Subject: [PATCH 02/10] Update Lib/plistlib.py Co-authored-by: Ronald Oussoren --- Lib/plistlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index a68b54e3cdde20..931a06fa229c82 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -888,7 +888,7 @@ 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). """ - if type(value) == str: + if isinstance(value, str): value = value.encode() fp = BytesIO(value) return load(fp, fmt=fmt, dict_type=dict_type) From 113ebbc1374825e457626a519ff70cbb6b1594de Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 31 Dec 2023 20:54:08 +0800 Subject: [PATCH 03/10] Update Lib/test/test_plistlib.py Co-authored-by: Ronald Oussoren --- Lib/test/test_plistlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index c1cc63cb40d544..fcd201bc9a4ac1 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -512,7 +512,7 @@ def test_loads_str(self): pl = self._create() b = plistlib.dumps(pl) s = b.decode() - self.assertEqual(type(s), str) + self.assertIsInstance(s, str) pl2 = plistlib.loads(s) self.assertEqual(pl, pl2) From 63ee02f7d12d46c93c65f679f2be817cb7b0b423 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 31 Dec 2023 20:55:30 +0800 Subject: [PATCH 04/10] Update Doc/library/plistlib.rst Co-authored-by: Ronald Oussoren --- Doc/library/plistlib.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 00b22905004313..a8e2e4f371fcb3 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -86,7 +86,9 @@ This module defines the following functions: explanation of the keyword arguments. .. versionadded:: 3.4 - + + .. version changed:: 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) From d192968bd9bd58f6a1aeb5f7d0da7c53c13d2a1f Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 31 Dec 2023 20:56:43 +0800 Subject: [PATCH 05/10] fix rst grammar --- Doc/library/plistlib.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index a8e2e4f371fcb3..eada23a1332034 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -86,9 +86,9 @@ This module defines the following functions: explanation of the keyword arguments. .. versionadded:: 3.4 - - .. version changed:: 3.13 - *data* can be a string when *fmt* equals :data:`FMT_XML`. + + .. 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) From da368f33a1b592e44cb57a649331c92225128e73 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 31 Dec 2023 21:08:37 +0800 Subject: [PATCH 06/10] raise TypeError when value is str and fmt is FMT_BINARY --- Lib/plistlib.py | 2 ++ Lib/test/test_plistlib.py | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 931a06fa229c82..3f9d89aff21d54 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -889,6 +889,8 @@ def loads(value, *, fmt=None, dict_type=dict): Return the unpacked root object (which usually is a dictionary). """ if isinstance(value, str): + if fmt == FMT_BINARY: + raise TypeError("value must be bytes when fmt is FMT_BINARY") value = value.encode() fp = BytesIO(value) return load(fp, fmt=fmt, dict_type=dict_type) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index fcd201bc9a4ac1..5257be86218fa8 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -508,7 +508,7 @@ def test_bytes(self): data2 = plistlib.dumps(pl2) self.assertEqual(data, data2) - def test_loads_str(self): + def test_loads_str_with_xml_fmt(self): pl = self._create() b = plistlib.dumps(pl) s = b.decode() @@ -516,6 +516,11 @@ def test_loads_str(self): pl2 = plistlib.loads(s) self.assertEqual(pl, pl2) + def test_loads_str_with_binary_fmt(self): + msg = 'value must be bytes when fmt is FMT_BINARY' + with self.assertRaisesRegex(TypeError, msg): + plistlib.loads('test', fmt=plistlib.FMT_BINARY) + def test_indentation_array(self): data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]] self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) From aee0cafa46839792df03a322730fb8589329b3a9 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 31 Dec 2023 21:35:06 +0800 Subject: [PATCH 07/10] update error message --- Lib/plistlib.py | 3 ++- Lib/test/test_plistlib.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 3f9d89aff21d54..fdc0cdcad7f992 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -890,7 +890,8 @@ def loads(value, *, fmt=None, dict_type=dict): """ if isinstance(value, str): if fmt == FMT_BINARY: - raise TypeError("value must be bytes when fmt is FMT_BINARY") + msg = "value must be bytes-like oebject when fmt is FMT_BINARY" + raise TypeError(msg) value = value.encode() fp = BytesIO(value) return load(fp, fmt=fmt, dict_type=dict_type) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 5257be86218fa8..51ddb82a5251cc 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -517,7 +517,7 @@ def test_loads_str_with_xml_fmt(self): self.assertEqual(pl, pl2) def test_loads_str_with_binary_fmt(self): - msg = 'value must be bytes when fmt is FMT_BINARY' + msg = "value must be bytes-like oebject when fmt is FMT_BINARY" with self.assertRaisesRegex(TypeError, msg): plistlib.loads('test', fmt=plistlib.FMT_BINARY) From 37c8965219ca9ef70b8e35a3367acb69f1714679 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 2 Jan 2024 22:37:20 +0800 Subject: [PATCH 08/10] Update Lib/test/test_plistlib.py Co-authored-by: Ronald Oussoren --- Lib/test/test_plistlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 51ddb82a5251cc..9a7a2be775d507 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -517,7 +517,7 @@ def test_loads_str_with_xml_fmt(self): self.assertEqual(pl, pl2) def test_loads_str_with_binary_fmt(self): - msg = "value must be bytes-like oebject when fmt is FMT_BINARY" + msg = "value must be bytes-like object when fmt is FMT_BINARY" with self.assertRaisesRegex(TypeError, msg): plistlib.loads('test', fmt=plistlib.FMT_BINARY) From abe7cdfe35e2d2a74b94a36d9dce8e189dd3a9f9 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 2 Jan 2024 22:40:15 +0800 Subject: [PATCH 09/10] fix typo --- Lib/plistlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index fdc0cdcad7f992..15654dc7700239 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -890,7 +890,7 @@ def loads(value, *, fmt=None, dict_type=dict): """ if isinstance(value, str): if fmt == FMT_BINARY: - msg = "value must be bytes-like oebject when fmt is FMT_BINARY" + msg = "value must be bytes-like object when fmt is FMT_BINARY" raise TypeError(msg) value = value.encode() fp = BytesIO(value) From e097d4c9d5bdf0d8b722db7c5a0292c52d12f2c7 Mon Sep 17 00:00:00 2001 From: AN Long Date: Fri, 5 Jan 2024 16:46:54 +0800 Subject: [PATCH 10/10] remove the msg temp variable --- Lib/plistlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 15654dc7700239..df1c1b32891540 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -890,8 +890,8 @@ def loads(value, *, fmt=None, dict_type=dict): """ if isinstance(value, str): if fmt == FMT_BINARY: - msg = "value must be bytes-like object when fmt is FMT_BINARY" - raise TypeError(msg) + raise TypeError("value must be bytes-like object when fmt is " + "FMT_BINARY") value = value.encode() fp = BytesIO(value) return load(fp, fmt=fmt, dict_type=dict_type)