]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-112826: Fix the threading Module When _thread is Missing _is_main_interpret...
authorEric Snow <ericsnowcurrently@gmail.com>
Thu, 7 Dec 2023 20:15:20 +0000 (13:15 -0700)
committerGitHub <noreply@github.com>
Thu, 7 Dec 2023 20:15:20 +0000 (20:15 +0000)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
Doc/whatsnew/3.12.rst
Lib/test/test_threading.py
Lib/threading.py

index c79f2e71ba9da56817f3c4e1eb648d243e2eb323..257f9ee1f5c410708b74c9d40bfa9740c996d519 100644 (file)
@@ -1895,6 +1895,15 @@ Changes in the Python API
   * Mixing tabs and spaces as indentation in the same file is not supported anymore and will
     raise a :exc:`TabError`.
 
+* The :mod:`threading` module now expects the :mod:`!_thread` module to have
+  an ``_is_main_interpreter`` attribute.  It is a function with no
+  arguments that returns ``True`` if the current interpreter is the
+  main interpreter.
+
+  Any library or application that provides a custom ``_thread`` module
+  should provide ``_is_main_interpreter()``.
+  (See :gh:`112826`.)
+
 Build Changes
 =============
 
index ce477d2df7d7d48bf38b2c5d7ca793f95f369de6..756d5e329fc6d3c410f1638953b0a74524c85c07 100644 (file)
@@ -1827,6 +1827,34 @@ class MiscTestCase(unittest.TestCase):
         support.check__all__(self, threading, ('threading', '_thread'),
                              extra=extra, not_exported=not_exported)
 
+    @requires_subprocess()
+    def test_gh112826_missing__thread__is_main_interpreter(self):
+        with os_helper.temp_dir() as tempdir:
+            modname = '_thread_fake'
+            import os.path
+            filename = os.path.join(tempdir, modname + '.py')
+            with open(filename, 'w') as outfile:
+                outfile.write("""if True:
+                    import _thread
+                    globals().update(vars(_thread))
+                    del _is_main_interpreter
+                    """)
+
+            expected_output = b'success!'
+            _, out, err = assert_python_ok("-c", f"""if True:
+                import sys
+                sys.path.insert(0, {tempdir!r})
+                import {modname}
+                sys.modules['_thread'] = {modname}
+                del sys.modules[{modname!r}]
+
+                import threading
+                print({expected_output.decode('utf-8')!r}, end='')
+                """)
+
+            self.assertEqual(out, expected_output)
+            self.assertEqual(err, b'')
+
 
 class InterruptMainTests(unittest.TestCase):
     def check_interrupt_main_with_signal_handler(self, signum):
index 624e7ed8f07c8f66b726e36b246a5fd23ad9d294..8dcaf8ca6a03c6685208d94181234437f6afa609 100644 (file)
@@ -37,7 +37,20 @@ _daemon_threads_allowed = _thread.daemon_threads_allowed
 _allocate_lock = _thread.allocate_lock
 _set_sentinel = _thread._set_sentinel
 get_ident = _thread.get_ident
-_is_main_interpreter = _thread._is_main_interpreter
+try:
+    _is_main_interpreter = _thread._is_main_interpreter
+except AttributeError:
+    # See https://github.com/python/cpython/issues/112826.
+    # We can pretend a subinterpreter is the main interpreter for the
+    # sake of _shutdown(), since that only means we do not wait for the
+    # subinterpreter's threads to finish.  Instead, they will be stopped
+    # later by the mechanism we use for daemon threads.  The likelihood
+    # of this case is small because rarely will the _thread module be
+    # replaced by a module without _is_main_interpreter().
+    # Furthermore, this is all irrelevant in applications
+    # that do not use subinterpreters.
+    def _is_main_interpreter():
+        return True
 try:
     get_native_id = _thread.get_native_id
     _HAVE_THREAD_NATIVE_ID = True