https://github.com/python/cpython/commit/f937b9fffbaaf5e604d5a575758c81d411b33c81
commit: f937b9fffbaaf5e604d5a575758c81d411b33c81
branch: 3.14
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: sobolevn <m...@sobolevn.me>
date: 2025-07-08T19:11:48+03:00
summary:

[3.14] gh-136380: Fix import behavior for 
`concurrent.futures.InterpreterPoolExecutor` (GH-136381) (#136420)

gh-136380: Fix import behavior for `concurrent.futures.InterpreterPoolExecutor` 
(GH-136381)
(cherry picked from commit 490eea02819ad303a5042529af7507b7b1fdabdc)

Co-authored-by: AN Long <a...@users.noreply.github.com>
Co-authored-by: Serhiy Storchaka <storch...@gmail.com>
Co-authored-by: Peter Bierma <zintensity...@gmail.com>
Co-authored-by: sobolevn <m...@sobolevn.me>

files:
A Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst
M Lib/concurrent/futures/__init__.py
M Lib/test/test_concurrent_futures/test_interpreter_pool.py

diff --git a/Lib/concurrent/futures/__init__.py 
b/Lib/concurrent/futures/__init__.py
index 7ada7431c1ab8c..e717222cf98b32 100644
--- a/Lib/concurrent/futures/__init__.py
+++ b/Lib/concurrent/futures/__init__.py
@@ -17,7 +17,7 @@
                                       wait,
                                       as_completed)
 
-__all__ = (
+__all__ = [
     'FIRST_COMPLETED',
     'FIRST_EXCEPTION',
     'ALL_COMPLETED',
@@ -29,10 +29,18 @@
     'Executor',
     'wait',
     'as_completed',
-    'InterpreterPoolExecutor',
     'ProcessPoolExecutor',
     'ThreadPoolExecutor',
-)
+]
+
+
+try:
+    import _interpreters
+except ImportError:
+    _interpreters = None
+
+if _interpreters:
+    __all__.append('InterpreterPoolExecutor')
 
 
 def __dir__():
@@ -43,22 +51,15 @@ def __getattr__(name):
     global ProcessPoolExecutor, ThreadPoolExecutor, InterpreterPoolExecutor
 
     if name == 'ProcessPoolExecutor':
-        from .process import ProcessPoolExecutor as pe
-        ProcessPoolExecutor = pe
-        return pe
+        from .process import ProcessPoolExecutor
+        return ProcessPoolExecutor
 
     if name == 'ThreadPoolExecutor':
-        from .thread import ThreadPoolExecutor as te
-        ThreadPoolExecutor = te
-        return te
+        from .thread import ThreadPoolExecutor
+        return ThreadPoolExecutor
 
-    if name == 'InterpreterPoolExecutor':
-        try:
-            from .interpreter import InterpreterPoolExecutor as ie
-        except ModuleNotFoundError:
-            ie = InterpreterPoolExecutor = None
-        else:
-            InterpreterPoolExecutor = ie
-        return ie
+    if _interpreters and name == 'InterpreterPoolExecutor':
+        from .interpreter import InterpreterPoolExecutor
+        return InterpreterPoolExecutor
 
     raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
diff --git a/Lib/test/test_concurrent_futures/test_interpreter_pool.py 
b/Lib/test/test_concurrent_futures/test_interpreter_pool.py
index 844dfdd6fc901c..b10bbebd0984c4 100644
--- a/Lib/test/test_concurrent_futures/test_interpreter_pool.py
+++ b/Lib/test/test_concurrent_futures/test_interpreter_pool.py
@@ -2,7 +2,9 @@
 import contextlib
 import io
 import os
+import subprocess
 import sys
+import textwrap
 import time
 import unittest
 from concurrent.futures.interpreter import BrokenInterpreterPool
@@ -457,6 +459,45 @@ def test_free_reference(self):
         # Weak references don't cross between interpreters.
         raise unittest.SkipTest('not applicable')
 
+    @support.requires_subprocess()
+    def test_import_interpreter_pool_executor(self):
+        # Test the import behavior normally if _interpreters is unavailable.
+        code = textwrap.dedent("""
+        import sys
+        # Set it to None to emulate the case when _interpreter is unavailable.
+        sys.modules['_interpreters'] = None
+        from concurrent import futures
+
+        try:
+            futures.InterpreterPoolExecutor
+        except AttributeError:
+            pass
+        else:
+            print('AttributeError not raised!', file=sys.stderr)
+            sys.exit(1)
+
+        try:
+            from concurrent.futures import InterpreterPoolExecutor
+        except ImportError:
+            pass
+        else:
+            print('ImportError not raised!', file=sys.stderr)
+            sys.exit(1)
+
+        from concurrent.futures import *
+
+        if 'InterpreterPoolExecutor' in globals():
+            print('InterpreterPoolExecutor should not be imported!',
+                  file=sys.stderr)
+            sys.exit(1)
+        """)
+
+        cmd = [sys.executable, '-c', code]
+        p = subprocess.run(cmd, capture_output=True)
+        self.assertEqual(p.returncode, 0, p.stderr.decode())
+        self.assertEqual(p.stdout.decode(), '')
+        self.assertEqual(p.stderr.decode(), '')
+
 
 class AsyncioTest(InterpretersMixin, testasyncio_utils.TestCase):
 
diff --git 
a/Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst 
b/Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst
new file mode 100644
index 00000000000000..4ac04b9c0a6c5c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst
@@ -0,0 +1,3 @@
+Raises :exc:`AttributeError` when accessing
+:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are
+not available.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to