Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c4655ca

Browse files
committed
Make copy.replace() to raise TypeError for PyStructSequence with unnamed fields
1 parent 350a3d0 commit c4655ca

File tree

2 files changed

+56
-12
lines changed

2 files changed

+56
-12
lines changed

Lib/test/test_structseq.py

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -135,29 +135,66 @@ def test_match_args_with_unnamed_fields(self):
135135
self.assertEqual(os.stat_result.__match_args__, expected_args)
136136

137137
def test_copy_replace_all_fields_visible(self):
138+
assert os.times_result.n_unnamed_fields == 0 and os.times_result.n_sequence_fields == os.times_result.n_fields
139+
140+
expected_args = ('user', 'system', 'children_user', 'children_system', 'elapsed')
141+
self.assertEqual(os.times_result.__match_args__, expected_args)
142+
self.assertEqual(os.times_result.n_fields, len(expected_args))
143+
144+
n_fields = os.times_result.n_fields # 5
145+
t = os.times_result(range(os.times_result.n_fields))
146+
147+
# visible fields
148+
self.assertEqual(copy.replace(t), tuple(range(n_fields)))
149+
self.assertEqual(copy.replace(t, user=1), (1, *range(1, n_fields)))
150+
self.assertEqual(copy.replace(t, system=2), (0, 2, *range(2, n_fields)))
151+
self.assertEqual(copy.replace(t, user=1, system=2), (1, 2, *range(2, n_fields)))
152+
153+
# named invisible fields
154+
with self.assertRaisesRegex(ValueError, 'unexpected field name'):
155+
copy.replace(t, error=-1)
156+
with self.assertRaisesRegex(ValueError, 'unexpected field name'):
157+
copy.replace(t, user=1, error=-1)
158+
159+
def test_copy_replace_has_invisible_fields(self):
138160
t = time.gmtime(0)
139-
assert t.n_unnamed_fields == 0 and t.n_sequence_fields == t.n_fields
161+
assert t.n_unnamed_fields == 0 and t.n_sequence_fields < t.n_fields
140162

163+
# visible fields
141164
self.assertEqual(copy.replace(t), (1970, 1, 1, 0, 0, 0, 3, 1, 0))
142165
self.assertEqual(copy.replace(t, tm_year=2000),
143166
(2000, 1, 1, 0, 0, 0, 3, 1, 0))
144167
self.assertEqual(copy.replace(t, tm_mon=2),
145168
(1970, 2, 1, 0, 0, 0, 3, 1, 0))
146169
self.assertEqual(copy.replace(t, tm_year=2000, tm_mon=2),
147170
(2000, 2, 1, 0, 0, 0, 3, 1, 0))
148-
with self.assertRaisesRegex(ValueError, 'unexpected field name'):
149-
copy.replace(t, tm_year=2000, error=2)
150171

151-
def test_copy_replace_has_invisible_fields(self):
152-
assert os.stat_result.n_sequence_fields + os.stat_result.n_unnamed_fields < os.stat_result.n_fields
153-
r = os.stat_result(range(os.stat_result.n_sequence_fields), {'st_atime_ns': -1})
154-
self.assertHasAttr(r, 'st_atime_ns')
155-
self.assertEqual(r.st_atime_ns, -1)
172+
# named invisible fields
156173
with self.assertRaisesRegex(AttributeError, 'readonly attribute'):
157-
r.st_atime_ns = -2
158-
r2 = copy.replace(r, st_atime_ns=-3)
159-
self.assertHasAttr(r2, 'st_atime_ns')
160-
self.assertEqual(r2.st_atime_ns, -3)
174+
t.tm_zone = 'some other zone'
175+
self.assertHasAttr(t, 'tm_zone')
176+
self.assertEqual(copy.replace(t, tm_zone='some other zone').tm_zone,
177+
'some other zone')
178+
179+
# unknown fields
180+
with self.assertRaisesRegex(ValueError, 'unexpected field name'):
181+
copy.replace(t, error=2)
182+
with self.assertRaisesRegex(ValueError, 'unexpected field name'):
183+
copy.replace(t, tm_year=2000, error=2)
184+
with self.assertRaisesRegex(ValueError, 'unexpected field name'):
185+
copy.replace(t, tm_zone='some other zone', error=2)
186+
187+
def test_copy_replace_has_unnamed_fields(self):
188+
assert os.stat_result.n_unnamed_fields > 0
189+
r = os.stat_result(range(os.stat_result.n_sequence_fields))
190+
with self.assertRaisesRegex(TypeError, '__replace__() is not supported'):
191+
copy.replace(r)
192+
with self.assertRaisesRegex(TypeError, '__replace__() is not supported'):
193+
copy.replace(r, st_mode=1)
194+
with self.assertRaisesRegex(TypeError, '__replace__() is not supported'):
195+
copy.replace(r, error=2)
196+
with self.assertRaisesRegex(TypeError, '__replace__() is not supported'):
197+
copy.replace(r, st_mode=1, error=2)
161198

162199

163200
if __name__ == "__main__":

Objects/structseq.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,13 @@ structseq_replace(PyStructSequence *self, PyObject *args, PyObject *kwargs)
388388
if (n_unnamed_fields < 0) {
389389
return NULL;
390390
}
391+
if (n_unnamed_fields > 0) {
392+
PyErr_Format(PyExc_TypeError,
393+
"__replace__() is not supported for %s "
394+
"because it has unnamed field(s)",
395+
Py_TYPE(self)->tp_name);
396+
return NULL;
397+
}
391398

392399
tup = _PyTuple_FromArray(self->ob_item, n_visible_fields);
393400
if (!tup) {

0 commit comments

Comments
 (0)