From e94b7e9c93d646af2300f885ad28847840ec660d Mon Sep 17 00:00:00 2001 From: Xuehai Pan Date: Tue, 3 Oct 2023 18:51:18 +0800 Subject: [PATCH 1/4] Add tests for pickling and copying PyStructSequence objects --- Lib/test/test_structseq.py | 95 +++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py index a9fe193028ebe4..986c33826196fe 100644 --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -1,4 +1,6 @@ +import copy import os +import pickle import time import unittest @@ -108,7 +110,98 @@ def __len__(self): def test_reduce(self): t = time.gmtime() - x = t.__reduce__() + cls, (tup, dct) = t.__reduce__() + self.assertIs(cls, time.struct_time) + self.assertIsInstance(tup, tuple) + self.assertIsInstance(dct, dict) + self.assertEqual(tup, t) + self.assertEqual(len(tup), len(t)) + self.assertEqual(len(dct), t.n_fields - t.n_sequence_fields) + + def test_reduce_with_unnamed_fields(self): + assert os.stat_result.n_unnamed_fields > 0 + + r = os.stat_result(range(os.stat_result.n_sequence_fields), {'st_atime': 1.0}) + self.assertEqual(r.st_atime, 1.0) + cls, (tup, dct) = r.__reduce__() + self.assertIs(cls, os.stat_result) + self.assertIsInstance(tup, tuple) + self.assertIsInstance(dct, dict) + self.assertEqual(tup, tuple(r)) + self.assertIn('st_atime', dct) + self.assertEqual(len(dct), r.n_fields - r.n_sequence_fields) + + def test_pickling(self): + t = time.gmtime() + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(t, proto) + t2 = pickle.loads(p) + self.assertEqual(t.__class__, t2.__class__) + self.assertEqual(t, t2) + self.assertEqual(t.tm_year, t2.tm_year) + self.assertEqual(t.tm_zone, t2.tm_zone) + + def test_pickling_with_unnamed_fields(self): + assert os.stat_result.n_unnamed_fields > 0 + + r = os.stat_result(range(os.stat_result.n_sequence_fields), {'st_atime': 1.0}) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(r, proto) + r2 = pickle.loads(p) + self.assertEqual(r.__class__, r2.__class__) + self.assertEqual(r, r2) + self.assertEqual(r.st_mode, r2.st_mode) + self.assertEqual(r.st_atime, r2.st_atime) + self.assertEqual(r.st_atime_ns, r2.st_atime_ns) + + def test_copying(self): + n_fields = time.struct_time.n_fields + t = time.struct_time([[i] for i in range(n_fields)]) + + t2 = copy.copy(t) + self.assertEqual(t.__class__, t2.__class__) + self.assertEqual(t, t2) + self.assertEqual(t.tm_year, t2.tm_year) + self.assertEqual(t.tm_zone, t2.tm_zone) + self.assertIs(t[0], t2[0]) + self.assertIs(t.tm_year, t2.tm_year) + + t3 = copy.deepcopy(t) + self.assertEqual(t.__class__, t3.__class__) + self.assertEqual(t, t3) + self.assertEqual(t.tm_year, t3.tm_year) + self.assertEqual(t.tm_zone, t3.tm_zone) + self.assertIsNot(t[0], t3[0]) + self.assertIsNot(t.tm_year, t3.tm_year) + + def test_copying_with_unnamed_fields(self): + assert os.stat_result.n_unnamed_fields > 0 + + n_sequence_fields = os.stat_result.n_sequence_fields + r = os.stat_result([[i] for i in range(n_sequence_fields)], + {'st_atime': [1.0], 'st_atime_ns': [2.0]}) + + r2 = copy.copy(r) + self.assertEqual(r.__class__, r2.__class__) + self.assertEqual(r, r2) + self.assertEqual(r.st_mode, r2.st_mode) + self.assertEqual(r.st_atime, r2.st_atime) + self.assertEqual(r.st_atime_ns, r2.st_atime_ns) + self.assertIs(r[0], r2[0]) + self.assertIs(r.st_mode, r2.st_mode) + self.assertIs(r.st_atime, r2.st_atime) + self.assertIs(r.st_atime_ns, r2.st_atime_ns) + + r3 = copy.deepcopy(r) + self.assertEqual(r.__class__, r3.__class__) + self.assertEqual(r, r3) + self.assertEqual(r.st_mode, r3.st_mode) + self.assertEqual(r.st_atime, r3.st_atime) + self.assertEqual(r.st_atime_ns, r3.st_atime_ns) + self.assertIsNot(r[0], r3[0]) + self.assertIsNot(r.st_mode, r3.st_mode) + self.assertIsNot(r.st_atime, r3.st_atime) + self.assertIsNot(r.st_atime_ns, r3.st_atime_ns) def test_extended_getslice(self): # Test extended slicing by comparing with list slicing. From a64af6ccece2494f393aa86c263426c1d955b064 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:54:10 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst diff --git a/Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst b/Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst new file mode 100644 index 00000000000000..2bae7715cc3d5b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-03-10-54-09.gh-issue-110267.O-c47G.rst @@ -0,0 +1,2 @@ +Add tests for pickling and copying PyStructSequence objects. +Patched by Xuehai Pan. From e682580a55f403b8173bebf865d621da572d982d Mon Sep 17 00:00:00 2001 From: Xuehai Pan Date: Tue, 3 Oct 2023 18:57:14 +0800 Subject: [PATCH 3/4] Add more test cases --- Lib/test/test_structseq.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py index 986c33826196fe..03c0e3da197a14 100644 --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -121,7 +121,8 @@ def test_reduce(self): def test_reduce_with_unnamed_fields(self): assert os.stat_result.n_unnamed_fields > 0 - r = os.stat_result(range(os.stat_result.n_sequence_fields), {'st_atime': 1.0}) + r = os.stat_result(range(os.stat_result.n_sequence_fields), + {'st_atime': 1.0, 'st_atime_ns': 2.0}) self.assertEqual(r.st_atime, 1.0) cls, (tup, dct) = r.__reduce__() self.assertIs(cls, os.stat_result) @@ -129,6 +130,7 @@ def test_reduce_with_unnamed_fields(self): self.assertIsInstance(dct, dict) self.assertEqual(tup, tuple(r)) self.assertIn('st_atime', dct) + self.assertIn('st_atime_ns', dct) self.assertEqual(len(dct), r.n_fields - r.n_sequence_fields) def test_pickling(self): @@ -144,7 +146,8 @@ def test_pickling(self): def test_pickling_with_unnamed_fields(self): assert os.stat_result.n_unnamed_fields > 0 - r = os.stat_result(range(os.stat_result.n_sequence_fields), {'st_atime': 1.0}) + r = os.stat_result(range(os.stat_result.n_sequence_fields), + {'st_atime': 1.0, 'st_atime_ns': 2.0}) for proto in range(pickle.HIGHEST_PROTOCOL + 1): p = pickle.dumps(r, proto) r2 = pickle.loads(p) From 15b72bce6f514a497cd306bade7c6fd5f1a2ee4e Mon Sep 17 00:00:00 2001 From: Xuehai Pan Date: Tue, 3 Oct 2023 19:48:57 +0800 Subject: [PATCH 4/4] Apply suggestions from code review --- Lib/test/test_structseq.py | 103 ++++++++++++++----------------------- 1 file changed, 39 insertions(+), 64 deletions(-) diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py index 03c0e3da197a14..c6c0afaf077acc 100644 --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -108,40 +108,15 @@ def __len__(self): self.assertRaises(Exc, time.struct_time, C()) - def test_reduce(self): - t = time.gmtime() - cls, (tup, dct) = t.__reduce__() - self.assertIs(cls, time.struct_time) - self.assertIsInstance(tup, tuple) - self.assertIsInstance(dct, dict) - self.assertEqual(tup, t) - self.assertEqual(len(tup), len(t)) - self.assertEqual(len(dct), t.n_fields - t.n_sequence_fields) - - def test_reduce_with_unnamed_fields(self): - assert os.stat_result.n_unnamed_fields > 0 - - r = os.stat_result(range(os.stat_result.n_sequence_fields), - {'st_atime': 1.0, 'st_atime_ns': 2.0}) - self.assertEqual(r.st_atime, 1.0) - cls, (tup, dct) = r.__reduce__() - self.assertIs(cls, os.stat_result) - self.assertIsInstance(tup, tuple) - self.assertIsInstance(dct, dict) - self.assertEqual(tup, tuple(r)) - self.assertIn('st_atime', dct) - self.assertIn('st_atime_ns', dct) - self.assertEqual(len(dct), r.n_fields - r.n_sequence_fields) - def test_pickling(self): t = time.gmtime() for proto in range(pickle.HIGHEST_PROTOCOL + 1): p = pickle.dumps(t, proto) t2 = pickle.loads(p) - self.assertEqual(t.__class__, t2.__class__) - self.assertEqual(t, t2) - self.assertEqual(t.tm_year, t2.tm_year) - self.assertEqual(t.tm_zone, t2.tm_zone) + self.assertEqual(t2.__class__, t.__class__) + self.assertEqual(t2, t) + self.assertEqual(t2.tm_year, t.tm_year) + self.assertEqual(t2.tm_zone, t.tm_zone) def test_pickling_with_unnamed_fields(self): assert os.stat_result.n_unnamed_fields > 0 @@ -151,31 +126,31 @@ def test_pickling_with_unnamed_fields(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): p = pickle.dumps(r, proto) r2 = pickle.loads(p) - self.assertEqual(r.__class__, r2.__class__) - self.assertEqual(r, r2) - self.assertEqual(r.st_mode, r2.st_mode) - self.assertEqual(r.st_atime, r2.st_atime) - self.assertEqual(r.st_atime_ns, r2.st_atime_ns) + self.assertEqual(r2.__class__, r.__class__) + self.assertEqual(r2, r) + self.assertEqual(r2.st_mode, r.st_mode) + self.assertEqual(r2.st_atime, r.st_atime) + self.assertEqual(r2.st_atime_ns, r.st_atime_ns) def test_copying(self): n_fields = time.struct_time.n_fields t = time.struct_time([[i] for i in range(n_fields)]) t2 = copy.copy(t) - self.assertEqual(t.__class__, t2.__class__) - self.assertEqual(t, t2) - self.assertEqual(t.tm_year, t2.tm_year) - self.assertEqual(t.tm_zone, t2.tm_zone) - self.assertIs(t[0], t2[0]) - self.assertIs(t.tm_year, t2.tm_year) + self.assertEqual(t2.__class__, t.__class__) + self.assertEqual(t2, t) + self.assertEqual(t2.tm_year, t.tm_year) + self.assertEqual(t2.tm_zone, t.tm_zone) + self.assertIs(t2[0], t[0]) + self.assertIs(t2.tm_year, t.tm_year) t3 = copy.deepcopy(t) - self.assertEqual(t.__class__, t3.__class__) - self.assertEqual(t, t3) - self.assertEqual(t.tm_year, t3.tm_year) - self.assertEqual(t.tm_zone, t3.tm_zone) - self.assertIsNot(t[0], t3[0]) - self.assertIsNot(t.tm_year, t3.tm_year) + self.assertEqual(t3.__class__, t.__class__) + self.assertEqual(t3, t) + self.assertEqual(t3.tm_year, t.tm_year) + self.assertEqual(t3.tm_zone, t.tm_zone) + self.assertIsNot(t3[0], t[0]) + self.assertIsNot(t3.tm_year, t.tm_year) def test_copying_with_unnamed_fields(self): assert os.stat_result.n_unnamed_fields > 0 @@ -185,26 +160,26 @@ def test_copying_with_unnamed_fields(self): {'st_atime': [1.0], 'st_atime_ns': [2.0]}) r2 = copy.copy(r) - self.assertEqual(r.__class__, r2.__class__) - self.assertEqual(r, r2) - self.assertEqual(r.st_mode, r2.st_mode) - self.assertEqual(r.st_atime, r2.st_atime) - self.assertEqual(r.st_atime_ns, r2.st_atime_ns) - self.assertIs(r[0], r2[0]) - self.assertIs(r.st_mode, r2.st_mode) - self.assertIs(r.st_atime, r2.st_atime) - self.assertIs(r.st_atime_ns, r2.st_atime_ns) + self.assertEqual(r2.__class__, r.__class__) + self.assertEqual(r2, r) + self.assertEqual(r2.st_mode, r.st_mode) + self.assertEqual(r2.st_atime, r.st_atime) + self.assertEqual(r2.st_atime_ns, r.st_atime_ns) + self.assertIs(r2[0], r[0]) + self.assertIs(r2.st_mode, r.st_mode) + self.assertIs(r2.st_atime, r.st_atime) + self.assertIs(r2.st_atime_ns, r.st_atime_ns) r3 = copy.deepcopy(r) - self.assertEqual(r.__class__, r3.__class__) - self.assertEqual(r, r3) - self.assertEqual(r.st_mode, r3.st_mode) - self.assertEqual(r.st_atime, r3.st_atime) - self.assertEqual(r.st_atime_ns, r3.st_atime_ns) - self.assertIsNot(r[0], r3[0]) - self.assertIsNot(r.st_mode, r3.st_mode) - self.assertIsNot(r.st_atime, r3.st_atime) - self.assertIsNot(r.st_atime_ns, r3.st_atime_ns) + self.assertEqual(r3.__class__, r.__class__) + self.assertEqual(r3, r) + self.assertEqual(r3.st_mode, r.st_mode) + self.assertEqual(r3.st_atime, r.st_atime) + self.assertEqual(r3.st_atime_ns, r.st_atime_ns) + self.assertIsNot(r3[0], r[0]) + self.assertIsNot(r3.st_mode, r.st_mode) + self.assertIsNot(r3.st_atime, r.st_atime) + self.assertIsNot(r3.st_atime_ns, r.st_atime_ns) def test_extended_getslice(self): # Test extended slicing by comparing with list slicing.