]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-136380: Fix import behavior for `concurrent.futures.InterpreterPoolExecutor` ...
authorAN Long <aisk@users.noreply.github.com>
Tue, 8 Jul 2025 13:32:14 +0000 (22:32 +0900)
committerGitHub <noreply@github.com>
Tue, 8 Jul 2025 13:32:14 +0000 (13:32 +0000)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
Co-authored-by: sobolevn <mail@sobolevn.me>
Lib/concurrent/futures/__init__.py
Lib/test/test_concurrent_futures/test_interpreter_pool.py
Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst [new file with mode: 0644]

index 7ada7431c1ab8c2b4c20b73ea0224d7927849e6f..e717222cf98b324afc0a4b9edc99173c9ec8e745 100644 (file)
@@ -17,7 +17,7 @@ from concurrent.futures._base import (FIRST_COMPLETED,
                                       wait,
                                       as_completed)
 
-__all__ = (
+__all__ = [
     'FIRST_COMPLETED',
     'FIRST_EXCEPTION',
     'ALL_COMPLETED',
@@ -29,10 +29,18 @@ __all__ = (
     '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}")
index 844dfdd6fc901cf4986dff901c31877bb9af3dd6..b10bbebd0984c408565c93f5cde6e52718d01f0d 100644 (file)
@@ -2,7 +2,9 @@ import asyncio
 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 @@ class InterpreterPoolExecutorTest(
         # 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 (file)
index 0000000..4ac04b9
--- /dev/null
@@ -0,0 +1,3 @@
+Raises :exc:`AttributeError` when accessing
+:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are
+not available.