https://github.com/python/cpython/commit/d900c8f9aa7e71cca78536845340b19f9706e871
commit: d900c8f9aa7e71cca78536845340b19f9706e871
branch: main
author: Cody Maloney <[email protected]>
committer: cmaloney <[email protected]>
date: 2026-11-06T11:05:52-08:00
summary:

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 <[email protected]>
Co-authored-by: Stan Ulbrych <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-11-06-05-42-11.gh-issue-71199.qJN4cV.rst
M Lib/test/test_os/test_os.py
M Modules/posixmodule.c

diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py
index 662b57702fe26cd..6e7201d0b056c21 100644
--- a/Lib/test/test_os/test_os.py
+++ b/Lib/test/test_os/test_os.py
@@ -178,15 +178,28 @@ 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
+        # 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
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..fde88d5facc9511
--- /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 <pickle-picklable>` 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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to