gh-136380: Fix import behavior for concurrent.futures.InterpreterPoolExecutor (#136381)

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:
AN Long 2025-07-08 22:32:14 +09:00 committed by GitHub
parent ba9c198630
commit 490eea0281
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 @@ 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}")

View 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):

View file

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