From f53e861950e1bf79361c14de89c94be7db703f0a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 11:11:15 -0700 Subject: [PATCH 1/7] Add a dummy _is_main_interpreter() if missing from _thread. --- Lib/test/test_threading.py | 33 +++++++++++++++++++++++++++++++++ Lib/threading.py | 15 ++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index ce477d2df7d7d4..81054a9335a936 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -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 * + 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 + del sys.modules[{modname!r}] + + import threading + print({expected_output!r}, end='') + """ + proc = subprocess.run( + [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): diff --git a/Lib/threading.py b/Lib/threading.py index 624e7ed8f07c8f..8dcaf8ca6a03c6 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -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 From 067f55b25501f0ec89fbf6b0d96a4b30fd997267 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 11:34:32 -0700 Subject: [PATCH 2/7] Add a "What's New" entry. --- Doc/whatsnew/3.12.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index c79f2e71ba9da5..067bec418d286d 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -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 `_thread` module to have + a ``_is_main_interpreter`` attribute. Is 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 + must make sure it provides ``_is_main_interpreter()``. + (See :gh:`112826`.) + Build Changes ============= From ad0515fc93da1c2d1052c842601fc8c311a45f7a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 11:51:37 -0700 Subject: [PATCH 3/7] Update Doc/whatsnew/3.12.rst Co-authored-by: Alex Waygood --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 067bec418d286d..ac8fc7d17b097f 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1895,7 +1895,7 @@ 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 `_thread` module to have + * The :mod:`threading` module now expects the :mod:`!_thread` module to have a ``_is_main_interpreter`` attribute. Is is a function with no arguments that returns ``True`` if the current interpreter is the main interpreter. From 656be2089552d1ac367e57c13cd63792da632e1d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 12:25:46 -0700 Subject: [PATCH 4/7] Update Doc/whatsnew/3.12.rst Co-authored-by: Hugo van Kemenade --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index ac8fc7d17b097f..37b14410fb0840 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1896,7 +1896,7 @@ Changes in the Python API 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 + an ``_is_main_interpreter`` attribute. It is a function with no arguments that returns ``True`` if the current interpreter is the main interpreter. From b12bb92582eb3972d9caee0c5054e86793ec9713 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 12:26:18 -0700 Subject: [PATCH 5/7] Update Doc/whatsnew/3.12.rst Co-authored-by: T. Wouters --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 37b14410fb0840..e849b466e6a855 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1901,7 +1901,7 @@ Changes in the Python API main interpreter. Any library or application that provides a custom ``_thread`` module - must make sure it provides ``_is_main_interpreter()``. + should provide ``_is_main_interpreter()``. (See :gh:`112826`.) Build Changes From 17b0f674584340820d304437b418f45a0d807730 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 12:48:33 -0700 Subject: [PATCH 6/7] Clean up the test. --- Lib/test/test_threading.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 81054a9335a936..756d5e329fc6d3 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1836,29 +1836,24 @@ def test_gh112826_missing__thread__is_main_interpreter(self): with open(filename, 'w') as outfile: outfile.write("""if True: import _thread - ns = globals().update(vars(_thread)) - #from _thread import * + globals().update(vars(_thread)) del _is_main_interpreter """) - expected_output = 'success!' - script = f"""if True: + + 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'] = _thread_fake + sys.modules['_thread'] = {modname} del sys.modules[{modname!r}] import threading - print({expected_output!r}, end='') - """ - proc = subprocess.run( - [sys.executable, "-c", script], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - self.assertEqual(proc.stdout, expected_output) - self.assertEqual(proc.returncode, 0) + print({expected_output.decode('utf-8')!r}, end='') + """) + + self.assertEqual(out, expected_output) + self.assertEqual(err, b'') class InterruptMainTests(unittest.TestCase): From 98c80b96579ca6fc5a470eeff0a340e1fe0e24ae Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Dec 2023 12:55:28 -0700 Subject: [PATCH 7/7] Fix the indent. --- Doc/whatsnew/3.12.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index e849b466e6a855..257f9ee1f5c410 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1895,14 +1895,14 @@ 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`.) +* 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 =============