bpo-42163, bpo-42189, bpo-42659: Support uname_tuple._replace (for all but processor) (#23010)

* 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 <storchaka@gmail.com>

* import pickle

* Fix equality test after pickling.

* Simply rely on __reduce__ for pickling.

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Jason R. Coombs 2020-12-31 14:08:03 -05:00 committed by GitHub
parent dfdca85dfa
commit a6fd0f414c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 2 deletions

View File

@ -769,7 +769,7 @@ class uname_result(
): ):
""" """
A uname_result that's largely compatible with a A uname_result that's largely compatible with a
simple namedtuple except that 'platform' is simple namedtuple except that 'processor' is
resolved late and cached to avoid calling "uname" resolved late and cached to avoid calling "uname"
except when needed. except when needed.
""" """
@ -784,12 +784,25 @@ class uname_result(
(self.processor,) (self.processor,)
) )
@classmethod
def _make(cls, iterable):
# override factory to affect length check
num_fields = len(cls._fields)
result = cls.__new__(cls, *iterable)
if len(result) != num_fields + 1:
msg = f'Expected {num_fields} arguments, got {len(result)}'
raise TypeError(msg)
return result
def __getitem__(self, key): def __getitem__(self, key):
return tuple(iter(self))[key] return tuple(self)[key]
def __len__(self): def __len__(self):
return len(tuple(iter(self))) return len(tuple(iter(self)))
def __reduce__(self):
return uname_result, tuple(self)[:len(self._fields)]
_uname_cache = None _uname_cache = None

View File

@ -1,4 +1,6 @@
import os import os
import copy
import pickle
import platform import platform
import subprocess import subprocess
import sys import sys
@ -234,6 +236,38 @@ class PlatformTest(unittest.TestCase):
) )
self.assertEqual(tuple(res), expected) self.assertEqual(tuple(res), expected)
def test_uname_replace(self):
res = platform.uname()
new = res._replace(
system='system', node='node', release='release',
version='version', machine='machine')
self.assertEqual(new.system, 'system')
self.assertEqual(new.node, 'node')
self.assertEqual(new.release, 'release')
self.assertEqual(new.version, 'version')
self.assertEqual(new.machine, 'machine')
# processor cannot be replaced
self.assertEqual(new.processor, res.processor)
def test_uname_copy(self):
uname = platform.uname()
self.assertEqual(copy.copy(uname), uname)
self.assertEqual(copy.deepcopy(uname), uname)
def test_uname_pickle(self):
orig = platform.uname()
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(protocol=proto):
pickled = pickle.dumps(orig, proto)
restored = pickle.loads(pickled)
self.assertEqual(restored, orig)
def test_uname_slices(self):
res = platform.uname()
expected = tuple(res)
self.assertEqual(res[:], expected)
self.assertEqual(res[:5], expected[:5])
@unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used") @unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used")
def test_uname_processor(self): def test_uname_processor(self):
""" """

View File

@ -0,0 +1 @@
Restore compatibility for ``uname_result`` around deepcopy and _replace.