Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
a ``_is_main_interpreter`` attribute. Is is a function with no
Comment thread
ericsnowcurrently marked this conversation as resolved.
Outdated
Comment thread
ericsnowcurrently marked this conversation as resolved.
Outdated
arguments that returns ``True`` if the current interpreter is the
main interpreter.

Any library or application that provides a custom ``_thread`` module
must make sure it provides ``_is_main_interpreter()``.
Comment thread
ericsnowcurrently marked this conversation as resolved.
Outdated
(See :gh:`112826`.)

Build Changes
=============

Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,39 @@ def test__all__(self):
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
ns = globals().update(vars(_thread))
#from _thread import *
Comment thread
Yhg1s marked this conversation as resolved.
Outdated
del _is_main_interpreter
""")
expected_output = 'success!'
script = f"""if True:
import sys
sys.path.insert(0, {tempdir!r})
import {modname}
sys.modules['_thread'] = _thread_fake
Comment thread
Yhg1s marked this conversation as resolved.
Outdated
del sys.modules[{modname!r}]

import threading
print({expected_output!r}, end='')
"""
proc = subprocess.run(
Comment thread
Yhg1s marked this conversation as resolved.
Outdated
[sys.executable, "-c", script],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
self.assertEqual(proc.stdout, expected_output)
self.assertEqual(proc.returncode, 0)


class InterruptMainTests(unittest.TestCase):
def check_interrupt_main_with_signal_handler(self, signum):
Expand Down
15 changes: 14 additions & 1 deletion Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,20 @@
_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
Expand Down