From f771719f3864b1fd1ccf9cca848e82c0321f2c18 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 5 Nov 2026 21:38:20 -0800 Subject: [PATCH 1/3] gh-71199: Test loading os pickles through os and the platform module Pickles name posixmodule types and functions either through os or through the platform module (posix or nt). Both spellings must keep loading. Co-Authored-By: Claude Opus 5.5 --- Lib/test/test_os/test_os.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 662b57702fe26cd..31397a9a0f4cf42 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -188,6 +188,18 @@ def fqdn(obj): if hasattr(os, "waitid_result"): self.assertEqual(fqdn(os.waitid_result), (native, "waitid_result")) + def test_pickle_global_names(self): + # Existing pickles name these via os or the platform module (posix + # or nt); both must keep loading, as must new pickles. + for name in ('stat', 'stat_result', 'statvfs_result', 'times_result', + 'uname_result'): + with self.subTest(name): + obj = getattr(os, name) + for module in ('os', os.name): + p = f'c{module}\n{name}\n.'.encode() + self.assertIs(pickle.loads(p), obj) + self.assertIs(pickle.loads(pickle.dumps(obj)), obj) + # Tests creating TESTFN class FileTests(unittest.TestCase): From 89769fa0dcded31013c51a4ff165c37dbc89a47a Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 5 Nov 2026 21:42:11 -0800 Subject: [PATCH 2/3] gh-71199: Name posixmodule types os. on every platform 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. like stat_result (gh-63408). Older pickles keep loading through the platform module. Co-Authored-By: Claude Opus 5.5 --- Lib/test/test_os/test_os.py | 9 +++++---- .../2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst | 3 +++ Modules/posixmodule.c | 12 ++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 31397a9a0f4cf42..6e7201d0b056c21 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -178,15 +178,16 @@ def test_type_fqdn(self): def fqdn(obj): return (obj.__module__, obj.__qualname__) - native = os.name self.assertEqual(fqdn(os.stat_result), ("os", "stat_result")) - self.assertEqual(fqdn(os.times_result), (native, "times_result")) + self.assertEqual(fqdn(os.times_result), ("os", "times_result")) + self.assertEqual(fqdn(os.DirEntry), ("os", "DirEntry")) if hasattr(os, "statvfs_result"): self.assertEqual(fqdn(os.statvfs_result), ("os", "statvfs_result")) + self.assertEqual(fqdn(os.uname_result), ("os", "uname_result")) if hasattr(os, "sched_param"): - self.assertEqual(fqdn(os.sched_param), (native, "sched_param")) + self.assertEqual(fqdn(os.sched_param), ("os", "sched_param")) if hasattr(os, "waitid_result"): - self.assertEqual(fqdn(os.waitid_result), (native, "waitid_result")) + self.assertEqual(fqdn(os.waitid_result), ("os", "waitid_result")) def test_pickle_global_names(self): # Existing pickles name these via os or the platform module (posix diff --git a/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst b/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst new file mode 100644 index 000000000000000..3530b960bc603a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst @@ -0,0 +1,3 @@ +Change :mod:`os` module types such as :class:`~os.DirEntry` to report ``os`` +as their module on every platform. :ref:`Pickled ` objects +using the platform specific qualified name, such as ``nt.times_result``, still load. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 9709c4e1c9454b2..67461fdb3730860 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2561,7 +2561,7 @@ static PyStructSequence_Field waitid_result_fields[] = { }; static PyStructSequence_Desc waitid_result_desc = { - MODNAME ".waitid_result", /* name */ + "os.waitid_result", /* name */ waitid_result__doc__, /* doc */ waitid_result_fields, 5 @@ -6768,7 +6768,7 @@ or via the attributes sysname, nodename, release, version, and machine.\n\ See os.uname for more information."); static PyStructSequence_Desc uname_result_desc = { - MODNAME ".uname_result", /* name */ + "os.uname_result", /* name */ uname_result__doc__, /* doc */ uname_result_fields, 5 @@ -8818,7 +8818,7 @@ static PyStructSequence_Field sched_param_fields[] = { }; static PyStructSequence_Desc sched_param_desc = { - MODNAME ".sched_param", /* name */ + "os.sched_param", /* name */ os_sched_param__doc__, /* doc */ sched_param_fields, 1 @@ -11376,7 +11376,7 @@ and elapsed.\n\ See os.times for more information."); static PyStructSequence_Desc times_result_desc = { - MODNAME ".times_result", /* name */ + "os.times_result", /* name */ times_result__doc__, /* doc */ times_result_fields, 5 @@ -16835,7 +16835,7 @@ static PyType_Slot DirEntryType_slots[] = { }; static PyType_Spec DirEntryType_spec = { - .name = MODNAME ".DirEntry", + .name = "os.DirEntry", .basicsize = sizeof(DirEntry), .flags = ( Py_TPFLAGS_DEFAULT @@ -17392,7 +17392,7 @@ static PyType_Slot ScandirIteratorType_slots[] = { }; static PyType_Spec ScandirIteratorType_spec = { - .name = MODNAME ".ScandirIterator", + .name = "os.ScandirIterator", .basicsize = sizeof(ScandirIterator), // bpo-40549: Py_TPFLAGS_BASETYPE should not be used, since // PyType_GetModule(Py_TYPE(self)) doesn't work on a subclass instance. From 214536e47816f9471c26d31d147d4e1fb79c175d Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 6 Nov 2026 00:15:47 -0800 Subject: [PATCH 3/3] Update Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst Co-authored-by: Stan Ulbrych --- .../next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst b/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst index 3530b960bc603a1..fde88d5facc9511 100644 --- a/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst +++ b/Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst @@ -1,3 +1,3 @@ Change :mod:`os` module types such as :class:`~os.DirEntry` to report ``os`` as their module on every platform. :ref:`Pickled ` objects -using the platform specific qualified name, such as ``nt.times_result``, still load. +using the platform-specific qualified name, such as ``nt.times_result``, still load.