Skip to content

Commit f3b83ef

Browse files
authored
Update test_structseq.py to 3.14.5 (#7951)
1 parent 1a01393 commit f3b83ef

2 files changed

Lines changed: 281 additions & 73 deletions

File tree

Lib/test/test_structseq.py

Lines changed: 241 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import copy
2+
import gc
13
import os
4+
import pickle
5+
import re
6+
import textwrap
27
import time
38
import unittest
9+
from test.support import script_helper
410

511

612
class StructSeqTest(unittest.TestCase):
@@ -37,7 +43,7 @@ def test_repr(self):
3743
# os.stat() gives a complicated struct sequence.
3844
st = os.stat(__file__)
3945
rep = repr(st)
40-
self.assertTrue(rep.startswith("os.stat_result"))
46+
self.assertStartsWith(rep, "os.stat_result")
4147
self.assertIn("st_mode=", rep)
4248
self.assertIn("st_ino=", rep)
4349
self.assertIn("st_dev=", rep)
@@ -81,6 +87,7 @@ def test_fields(self):
8187
self.assertEqual(t.n_unnamed_fields, 0)
8288
self.assertEqual(t.n_fields, time._STRUCT_TM_ITEMS)
8389

90+
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Unexpected keyword argument dict
8491
def test_constructor(self):
8592
t = time.struct_time
8693

@@ -89,10 +96,72 @@ def test_constructor(self):
8996
self.assertRaises(TypeError, t, "123")
9097
self.assertRaises(TypeError, t, "123", dict={})
9198
self.assertRaises(TypeError, t, "123456789", dict=None)
99+
self.assertRaises(TypeError, t, seq="123456789", dict={})
100+
101+
self.assertEqual(t("123456789"), tuple("123456789"))
102+
self.assertEqual(t("123456789", {}), tuple("123456789"))
103+
self.assertEqual(t("123456789", dict={}), tuple("123456789"))
104+
self.assertEqual(t(sequence="123456789", dict={}), tuple("123456789"))
105+
106+
self.assertEqual(t("1234567890"), tuple("123456789"))
107+
self.assertEqual(t("1234567890").tm_zone, "0")
108+
self.assertEqual(t("123456789", {"tm_zone": "some zone"}), tuple("123456789"))
109+
self.assertEqual(t("123456789", {"tm_zone": "some zone"}).tm_zone, "some zone")
92110

93111
s = "123456789"
94112
self.assertEqual("".join(t(s)), s)
95113

114+
@unittest.expectedFailure # TODO: RUSTPYTHON; Wrong error message
115+
def test_constructor_with_duplicate_fields(self):
116+
t = time.struct_time
117+
118+
error_message = re.escape("got duplicate or unexpected field name(s)")
119+
with self.assertRaisesRegex(TypeError, error_message):
120+
t("1234567890", dict={"tm_zone": "some zone"})
121+
with self.assertRaisesRegex(TypeError, error_message):
122+
t("1234567890", dict={"tm_zone": "some zone", "tm_mon": 1})
123+
with self.assertRaisesRegex(TypeError, error_message):
124+
t("1234567890", dict={"error": 0, "tm_zone": "some zone"})
125+
with self.assertRaisesRegex(TypeError, error_message):
126+
t("1234567890", dict={"error": 0, "tm_zone": "some zone", "tm_mon": 1})
127+
128+
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: expected at most 1 arguments, got 2
129+
def test_constructor_with_duplicate_unnamed_fields(self):
130+
assert os.stat_result.n_unnamed_fields > 0
131+
n_visible_fields = os.stat_result.n_sequence_fields
132+
133+
r = os.stat_result(range(n_visible_fields), {'st_atime': -1.0})
134+
self.assertEqual(r.st_atime, -1.0)
135+
self.assertEqual(r, tuple(range(n_visible_fields)))
136+
137+
r = os.stat_result((*range(n_visible_fields), -1.0))
138+
self.assertEqual(r.st_atime, -1.0)
139+
self.assertEqual(r, tuple(range(n_visible_fields)))
140+
141+
with self.assertRaisesRegex(TypeError,
142+
re.escape("got duplicate or unexpected field name(s)")):
143+
os.stat_result((*range(n_visible_fields), -1.0), {'st_atime': -1.0})
144+
145+
@unittest.expectedFailure # TODO: RUSTPYTHON; Wrong error message
146+
def test_constructor_with_unknown_fields(self):
147+
t = time.struct_time
148+
149+
error_message = re.escape("got duplicate or unexpected field name(s)")
150+
with self.assertRaisesRegex(TypeError, error_message):
151+
t("123456789", dict={"tm_year": 0})
152+
with self.assertRaisesRegex(TypeError, error_message):
153+
t("123456789", dict={"tm_year": 0, "tm_mon": 1})
154+
with self.assertRaisesRegex(TypeError, error_message):
155+
t("123456789", dict={"tm_zone": "some zone", "tm_mon": 1})
156+
with self.assertRaisesRegex(TypeError, error_message):
157+
t("123456789", dict={"tm_zone": "some zone", "error": 0})
158+
with self.assertRaisesRegex(TypeError, error_message):
159+
t("123456789", dict={"error": 0, "tm_zone": "some zone", "tm_mon": 1})
160+
with self.assertRaisesRegex(TypeError, error_message):
161+
t("123456789", dict={"error": 0})
162+
with self.assertRaisesRegex(TypeError, error_message):
163+
t("123456789", dict={"tm_zone": "some zone", "error": 0})
164+
96165
def test_eviltuple(self):
97166
class Exc(Exception):
98167
pass
@@ -106,9 +175,80 @@ def __len__(self):
106175

107176
self.assertRaises(Exc, time.struct_time, C())
108177

109-
def test_reduce(self):
178+
def test_pickling(self):
110179
t = time.gmtime()
111-
x = t.__reduce__()
180+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
181+
p = pickle.dumps(t, proto)
182+
t2 = pickle.loads(p)
183+
self.assertEqual(t2.__class__, t.__class__)
184+
self.assertEqual(t2, t)
185+
self.assertEqual(t2.tm_year, t.tm_year)
186+
self.assertEqual(t2.tm_zone, t.tm_zone)
187+
188+
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: expected at most 1 arguments, got 2
189+
def test_pickling_with_unnamed_fields(self):
190+
assert os.stat_result.n_unnamed_fields > 0
191+
192+
r = os.stat_result(range(os.stat_result.n_sequence_fields),
193+
{'st_atime': 1.0, 'st_atime_ns': 2.0})
194+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
195+
p = pickle.dumps(r, proto)
196+
r2 = pickle.loads(p)
197+
self.assertEqual(r2.__class__, r.__class__)
198+
self.assertEqual(r2, r)
199+
self.assertEqual(r2.st_mode, r.st_mode)
200+
self.assertEqual(r2.st_atime, r.st_atime)
201+
self.assertEqual(r2.st_atime_ns, r.st_atime_ns)
202+
203+
def test_copying(self):
204+
n_fields = time.struct_time.n_fields
205+
t = time.struct_time([[i] for i in range(n_fields)])
206+
207+
t2 = copy.copy(t)
208+
self.assertEqual(t2.__class__, t.__class__)
209+
self.assertEqual(t2, t)
210+
self.assertEqual(t2.tm_year, t.tm_year)
211+
self.assertEqual(t2.tm_zone, t.tm_zone)
212+
self.assertIs(t2[0], t[0])
213+
self.assertIs(t2.tm_year, t.tm_year)
214+
215+
t3 = copy.deepcopy(t)
216+
self.assertEqual(t3.__class__, t.__class__)
217+
self.assertEqual(t3, t)
218+
self.assertEqual(t3.tm_year, t.tm_year)
219+
self.assertEqual(t3.tm_zone, t.tm_zone)
220+
self.assertIsNot(t3[0], t[0])
221+
self.assertIsNot(t3.tm_year, t.tm_year)
222+
223+
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: expected at most 1 arguments, got 2
224+
def test_copying_with_unnamed_fields(self):
225+
assert os.stat_result.n_unnamed_fields > 0
226+
227+
n_sequence_fields = os.stat_result.n_sequence_fields
228+
r = os.stat_result([[i] for i in range(n_sequence_fields)],
229+
{'st_atime': [1.0], 'st_atime_ns': [2.0]})
230+
231+
r2 = copy.copy(r)
232+
self.assertEqual(r2.__class__, r.__class__)
233+
self.assertEqual(r2, r)
234+
self.assertEqual(r2.st_mode, r.st_mode)
235+
self.assertEqual(r2.st_atime, r.st_atime)
236+
self.assertEqual(r2.st_atime_ns, r.st_atime_ns)
237+
self.assertIs(r2[0], r[0])
238+
self.assertIs(r2.st_mode, r.st_mode)
239+
self.assertIs(r2.st_atime, r.st_atime)
240+
self.assertIs(r2.st_atime_ns, r.st_atime_ns)
241+
242+
r3 = copy.deepcopy(r)
243+
self.assertEqual(r3.__class__, r.__class__)
244+
self.assertEqual(r3, r)
245+
self.assertEqual(r3.st_mode, r.st_mode)
246+
self.assertEqual(r3.st_atime, r.st_atime)
247+
self.assertEqual(r3.st_atime_ns, r.st_atime_ns)
248+
self.assertIsNot(r3[0], r[0])
249+
self.assertIsNot(r3.st_mode, r.st_mode)
250+
self.assertIsNot(r3.st_atime, r.st_atime)
251+
self.assertIsNot(r3.st_atime_ns, r.st_atime_ns)
112252

113253
def test_extended_getslice(self):
114254
# Test extended slicing by comparing with list slicing.
@@ -133,6 +273,104 @@ def test_match_args_with_unnamed_fields(self):
133273
self.assertEqual(os.stat_result.n_unnamed_fields, 3)
134274
self.assertEqual(os.stat_result.__match_args__, expected_args)
135275

276+
def test_copy_replace_all_fields_visible(self):
277+
assert os.times_result.n_unnamed_fields == 0
278+
assert os.times_result.n_sequence_fields == os.times_result.n_fields
279+
280+
t = os.times()
281+
282+
# visible fields
283+
self.assertEqual(copy.replace(t), t)
284+
self.assertIsInstance(copy.replace(t), os.times_result)
285+
self.assertEqual(copy.replace(t, user=1.5), (1.5, *t[1:]))
286+
self.assertEqual(copy.replace(t, system=2.5), (t[0], 2.5, *t[2:]))
287+
self.assertEqual(copy.replace(t, user=1.5, system=2.5), (1.5, 2.5, *t[2:]))
288+
289+
# unknown fields
290+
with self.assertRaisesRegex(TypeError, 'unexpected field name'):
291+
copy.replace(t, error=-1)
292+
with self.assertRaisesRegex(TypeError, 'unexpected field name'):
293+
copy.replace(t, user=1, error=-1)
294+
295+
@unittest.expectedFailure # TODO: RUSTPYTHON; Wrong error message
296+
def test_copy_replace_with_invisible_fields(self):
297+
assert time.struct_time.n_unnamed_fields == 0
298+
assert time.struct_time.n_sequence_fields < time.struct_time.n_fields
299+
300+
t = time.gmtime(0)
301+
302+
# visible fields
303+
t2 = copy.replace(t)
304+
self.assertEqual(t2, (1970, 1, 1, 0, 0, 0, 3, 1, 0))
305+
self.assertIsInstance(t2, time.struct_time)
306+
t3 = copy.replace(t, tm_year=2000)
307+
self.assertEqual(t3, (2000, 1, 1, 0, 0, 0, 3, 1, 0))
308+
self.assertEqual(t3.tm_year, 2000)
309+
t4 = copy.replace(t, tm_mon=2)
310+
self.assertEqual(t4, (1970, 2, 1, 0, 0, 0, 3, 1, 0))
311+
self.assertEqual(t4.tm_mon, 2)
312+
t5 = copy.replace(t, tm_year=2000, tm_mon=2)
313+
self.assertEqual(t5, (2000, 2, 1, 0, 0, 0, 3, 1, 0))
314+
self.assertEqual(t5.tm_year, 2000)
315+
self.assertEqual(t5.tm_mon, 2)
316+
317+
# named invisible fields
318+
self.assertHasAttr(t, 'tm_zone')
319+
with self.assertRaisesRegex(AttributeError, 'readonly attribute'):
320+
t.tm_zone = 'some other zone'
321+
self.assertEqual(t2.tm_zone, t.tm_zone)
322+
self.assertEqual(t3.tm_zone, t.tm_zone)
323+
self.assertEqual(t4.tm_zone, t.tm_zone)
324+
t6 = copy.replace(t, tm_zone='some other zone')
325+
self.assertEqual(t, t6)
326+
self.assertEqual(t6.tm_zone, 'some other zone')
327+
t7 = copy.replace(t, tm_year=2000, tm_zone='some other zone')
328+
self.assertEqual(t7, (2000, 1, 1, 0, 0, 0, 3, 1, 0))
329+
self.assertEqual(t7.tm_year, 2000)
330+
self.assertEqual(t7.tm_zone, 'some other zone')
331+
332+
# unknown fields
333+
with self.assertRaisesRegex(TypeError, 'unexpected field name'):
334+
copy.replace(t, error=2)
335+
with self.assertRaisesRegex(TypeError, 'unexpected field name'):
336+
copy.replace(t, tm_year=2000, error=2)
337+
with self.assertRaisesRegex(TypeError, 'unexpected field name'):
338+
copy.replace(t, tm_zone='some other zone', error=2)
339+
340+
def test_copy_replace_with_unnamed_fields(self):
341+
assert os.stat_result.n_unnamed_fields > 0
342+
343+
r = os.stat_result(range(os.stat_result.n_sequence_fields))
344+
345+
error_message = re.escape('__replace__() is not supported')
346+
with self.assertRaisesRegex(TypeError, error_message):
347+
copy.replace(r)
348+
with self.assertRaisesRegex(TypeError, error_message):
349+
copy.replace(r, st_mode=1)
350+
with self.assertRaisesRegex(TypeError, error_message):
351+
copy.replace(r, error=2)
352+
with self.assertRaisesRegex(TypeError, error_message):
353+
copy.replace(r, st_mode=1, error=2)
354+
355+
def test_reference_cycle(self):
356+
# gh-122527: Check that a structseq that's part of a reference cycle
357+
# with its own type doesn't crash. Previously, if the type's dictionary
358+
# was cleared first, the structseq instance would crash in the
359+
# destructor.
360+
script_helper.assert_python_ok("-c", textwrap.dedent(r"""
361+
import time
362+
t = time.gmtime()
363+
type(t).refcyle = t
364+
"""))
365+
366+
def test_replace_gc_tracked(self):
367+
# Verify that __replace__ results are properly GC-tracked
368+
time_struct = time.gmtime(0)
369+
lst = []
370+
replaced_struct = time_struct.__replace__(tm_year=lst)
371+
lst.append(replaced_struct)
372+
373+
self.assertTrue(gc.is_tracked(replaced_struct))
136374

137375
if __name__ == "__main__":
138376
unittest.main()

0 commit comments

Comments
 (0)