* 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
=============
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):
_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