Skip to content

Commit 799722c

Browse files
authored
[3.9] bpo-42163, bpo-42189, bpo-42659: Support uname_tuple._replace (for all but processor) (GH-23010) (#24232)
* Add test capturing missed expectation with uname_result._replace. * bpo-42163: Override uname_result._make to allow uname_result._replace to work (for everything but 'processor'. * Replace hard-coded length with one derived from the definition. * Add test capturing missed expectation with copy/deepcopy on namedtuple (bpo-42189). * bpo-42189: Exclude processor parameter when constructing uname_result. * In _make, rely on __new__ to strip processor. * Add blurb. * iter is not necessary here. * Rely on num_fields in __new__ * Add test for slices on uname * Add test for copy and pickle. Co-authored-by: Serhiy Storchaka <[email protected]> * import pickle * Fix equality test after pickling. * Simply rely on __reduce__ for pickling. Co-authored-by: Serhiy Storchaka <[email protected]> (cherry picked from commit a6fd0f4) Co-authored-by: Jason R. Coombs <[email protected]>
1 parent 17c1f0c commit 799722c

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

Lib/platform.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ class uname_result(
782782
):
783783
"""
784784
A uname_result that's largely compatible with a
785-
simple namedtuple except that 'platform' is
785+
simple namedtuple except that 'processor' is
786786
resolved late and cached to avoid calling "uname"
787787
except when needed.
788788
"""
@@ -797,12 +797,25 @@ def __iter__(self):
797797
(self.processor,)
798798
)
799799

800+
@classmethod
801+
def _make(cls, iterable):
802+
# override factory to affect length check
803+
num_fields = len(cls._fields)
804+
result = cls.__new__(cls, *iterable)
805+
if len(result) != num_fields + 1:
806+
msg = f'Expected {num_fields} arguments, got {len(result)}'
807+
raise TypeError(msg)
808+
return result
809+
800810
def __getitem__(self, key):
801-
return tuple(iter(self))[key]
811+
return tuple(self)[key]
802812

803813
def __len__(self):
804814
return len(tuple(iter(self)))
805815

816+
def __reduce__(self):
817+
return uname_result, tuple(self)[:len(self._fields)]
818+
806819

807820
_uname_cache = None
808821

Lib/test/test_platform.py

+34
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
import copy
3+
import pickle
24
import platform
35
import subprocess
46
import sys
@@ -175,6 +177,38 @@ def test_uname_cast_to_tuple(self):
175177
)
176178
self.assertEqual(tuple(res), expected)
177179

180+
def test_uname_replace(self):
181+
res = platform.uname()
182+
new = res._replace(
183+
system='system', node='node', release='release',
184+
version='version', machine='machine')
185+
self.assertEqual(new.system, 'system')
186+
self.assertEqual(new.node, 'node')
187+
self.assertEqual(new.release, 'release')
188+
self.assertEqual(new.version, 'version')
189+
self.assertEqual(new.machine, 'machine')
190+
# processor cannot be replaced
191+
self.assertEqual(new.processor, res.processor)
192+
193+
def test_uname_copy(self):
194+
uname = platform.uname()
195+
self.assertEqual(copy.copy(uname), uname)
196+
self.assertEqual(copy.deepcopy(uname), uname)
197+
198+
def test_uname_pickle(self):
199+
orig = platform.uname()
200+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
201+
with self.subTest(protocol=proto):
202+
pickled = pickle.dumps(orig, proto)
203+
restored = pickle.loads(pickled)
204+
self.assertEqual(restored, orig)
205+
206+
def test_uname_slices(self):
207+
res = platform.uname()
208+
expected = tuple(res)
209+
self.assertEqual(res[:], expected)
210+
self.assertEqual(res[:5], expected[:5])
211+
178212
@unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used")
179213
def test_uname_processor(self):
180214
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Restore compatibility for ``uname_result`` around deepcopy and _replace.

0 commit comments

Comments
 (0)