[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 490eea0281)

Co-authored-by: AN Long <aisk@users.noreply.github.com>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
Co-authored-by: sobolevn <mail@sobolevn.me>
This commit is contained in:
Miss Islington (bot) 2025-07-08 18:11:48 +02:00 committed by GitHub
parent 0a15ae71ad
commit f937b9fffb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 62 additions and 17 deletions

View file

@ -17,7 +17,7 @@
wait, wait,
as_completed) as_completed)
__all__ = ( __all__ = [
'FIRST_COMPLETED', 'FIRST_COMPLETED',
'FIRST_EXCEPTION', 'FIRST_EXCEPTION',
'ALL_COMPLETED', 'ALL_COMPLETED',
@ -29,10 +29,18 @@
'Executor', 'Executor',
'wait', 'wait',
'as_completed', 'as_completed',
'InterpreterPoolExecutor',
'ProcessPoolExecutor', 'ProcessPoolExecutor',
'ThreadPoolExecutor', 'ThreadPoolExecutor',
) ]
try:
import _interpreters
except ImportError:
_interpreters = None
if _interpreters:
__all__.append('InterpreterPoolExecutor')
def __dir__(): def __dir__():
@ -43,22 +51,15 @@ def __getattr__(name):
global ProcessPoolExecutor, ThreadPoolExecutor, InterpreterPoolExecutor global ProcessPoolExecutor, ThreadPoolExecutor, InterpreterPoolExecutor
if name == 'ProcessPoolExecutor': if name == 'ProcessPoolExecutor':
from .process import ProcessPoolExecutor as pe from .process import ProcessPoolExecutor
ProcessPoolExecutor = pe return ProcessPoolExecutor
return pe
if name == 'ThreadPoolExecutor': if name == 'ThreadPoolExecutor':
from .thread import ThreadPoolExecutor as te from .thread import ThreadPoolExecutor
ThreadPoolExecutor = te return ThreadPoolExecutor
return te
if name == 'InterpreterPoolExecutor': if _interpreters and name == 'InterpreterPoolExecutor':
try: from .interpreter import InterpreterPoolExecutor
from .interpreter import InterpreterPoolExecutor as ie return InterpreterPoolExecutor
except ModuleNotFoundError:
ie = InterpreterPoolExecutor = None
else:
InterpreterPoolExecutor = ie
return ie
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View file

@ -2,7 +2,9 @@
import contextlib import contextlib
import io import io
import os import os
import subprocess
import sys import sys
import textwrap
import time import time
import unittest import unittest
from concurrent.futures.interpreter import BrokenInterpreterPool from concurrent.futures.interpreter import BrokenInterpreterPool
@ -457,6 +459,45 @@ def test_free_reference(self):
# Weak references don't cross between interpreters. # Weak references don't cross between interpreters.
raise unittest.SkipTest('not applicable') 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): class AsyncioTest(InterpretersMixin, testasyncio_utils.TestCase):

View file

@ -0,0 +1,3 @@
Raises :exc:`AttributeError` when accessing
:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are
not available.