Skip to content

Commit d900c8f

Browse files
cmaloneyclaudeStanFromIreland
authored
gh-71199: Name posixmodule types os.<type> on every platform (#161461)
Pickles name posixmodule types and functions either through os or through the platform module (posix or nt). Both spellings must keep loading. times_result, uname_result, waitid_result, sched_param, DirEntry and ScandirIterator were named after the platform module (posix or nt), so pickles referenced posix.times_result and could not be loaded on Windows. Name them os.<type> like stat_result (gh-63408). Older pickles keep loading through the platform module. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 2781ffe commit d900c8f

3 files changed

Lines changed: 26 additions & 10 deletions

File tree

‎Lib/test/test_os/test_os.py‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,28 @@ def test_type_fqdn(self):
178178
def fqdn(obj):
179179
return (obj.__module__, obj.__qualname__)
180180

181-
native = os.name
182181
self.assertEqual(fqdn(os.stat_result), ("os", "stat_result"))
183-
self.assertEqual(fqdn(os.times_result), (native, "times_result"))
182+
self.assertEqual(fqdn(os.times_result), ("os", "times_result"))
183+
self.assertEqual(fqdn(os.DirEntry), ("os", "DirEntry"))
184184
if hasattr(os, "statvfs_result"):
185185
self.assertEqual(fqdn(os.statvfs_result), ("os", "statvfs_result"))
186+
self.assertEqual(fqdn(os.uname_result), ("os", "uname_result"))
186187
if hasattr(os, "sched_param"):
187-
self.assertEqual(fqdn(os.sched_param), (native, "sched_param"))
188+
self.assertEqual(fqdn(os.sched_param), ("os", "sched_param"))
188189
if hasattr(os, "waitid_result"):
189-
self.assertEqual(fqdn(os.waitid_result), (native, "waitid_result"))
190+
self.assertEqual(fqdn(os.waitid_result), ("os", "waitid_result"))
191+
192+
def test_pickle_global_names(self):
193+
# Existing pickles name these via os or the platform module (posix
194+
# or nt); both must keep loading, as must new pickles.
195+
for name in ('stat', 'stat_result', 'statvfs_result', 'times_result',
196+
'uname_result'):
197+
with self.subTest(name):
198+
obj = getattr(os, name)
199+
for module in ('os', os.name):
200+
p = f'c{module}\n{name}\n.'.encode()
201+
self.assertIs(pickle.loads(p), obj)
202+
self.assertIs(pickle.loads(pickle.dumps(obj)), obj)
190203

191204

192205
# Tests creating TESTFN
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Change :mod:`os` module types such as :class:`~os.DirEntry` to report ``os``
2+
as their module on every platform. :ref:`Pickled <pickle-picklable>` objects
3+
using the platform-specific qualified name, such as ``nt.times_result``, still load.

‎Modules/posixmodule.c‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,7 +2561,7 @@ static PyStructSequence_Field waitid_result_fields[] = {
25612561
};
25622562

25632563
static PyStructSequence_Desc waitid_result_desc = {
2564-
MODNAME ".waitid_result", /* name */
2564+
"os.waitid_result", /* name */
25652565
waitid_result__doc__, /* doc */
25662566
waitid_result_fields,
25672567
5
@@ -6768,7 +6768,7 @@ or via the attributes sysname, nodename, release, version, and machine.\n\
67686768
See os.uname for more information.");
67696769

67706770
static PyStructSequence_Desc uname_result_desc = {
6771-
MODNAME ".uname_result", /* name */
6771+
"os.uname_result", /* name */
67726772
uname_result__doc__, /* doc */
67736773
uname_result_fields,
67746774
5
@@ -8818,7 +8818,7 @@ static PyStructSequence_Field sched_param_fields[] = {
88188818
};
88198819

88208820
static PyStructSequence_Desc sched_param_desc = {
8821-
MODNAME ".sched_param", /* name */
8821+
"os.sched_param", /* name */
88228822
os_sched_param__doc__, /* doc */
88238823
sched_param_fields,
88248824
1
@@ -11376,7 +11376,7 @@ and elapsed.\n\
1137611376
See os.times for more information.");
1137711377

1137811378
static PyStructSequence_Desc times_result_desc = {
11379-
MODNAME ".times_result", /* name */
11379+
"os.times_result", /* name */
1138011380
times_result__doc__, /* doc */
1138111381
times_result_fields,
1138211382
5
@@ -16835,7 +16835,7 @@ static PyType_Slot DirEntryType_slots[] = {
1683516835
};
1683616836

1683716837
static PyType_Spec DirEntryType_spec = {
16838-
.name = MODNAME ".DirEntry",
16838+
.name = "os.DirEntry",
1683916839
.basicsize = sizeof(DirEntry),
1684016840
.flags = (
1684116841
Py_TPFLAGS_DEFAULT
@@ -17392,7 +17392,7 @@ static PyType_Slot ScandirIteratorType_slots[] = {
1739217392
};
1739317393

1739417394
static PyType_Spec ScandirIteratorType_spec = {
17395-
.name = MODNAME ".ScandirIterator",
17395+
.name = "os.ScandirIterator",
1739617396
.basicsize = sizeof(ScandirIterator),
1739717397
// bpo-40549: Py_TPFLAGS_BASETYPE should not be used, since
1739817398
// PyType_GetModule(Py_TYPE(self)) doesn't work on a subclass instance.

0 commit comments

Comments
 (0)