Skip to content

Commit 490eea0

Browse files
aiskserhiy-storchakaZeroIntensitysobolevn
authored
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>
1 parent ba9c198 commit 490eea0

File tree

3 files changed

+62
-17
lines changed

3 files changed

+62
-17
lines changed

Lib/concurrent/futures/__init__.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
wait,
1818
as_completed)
1919

20-
__all__ = (
20+
__all__ = [
2121
'FIRST_COMPLETED',
2222
'FIRST_EXCEPTION',
2323
'ALL_COMPLETED',
@@ -29,10 +29,18 @@
2929
'Executor',
3030
'wait',
3131
'as_completed',
32-
'InterpreterPoolExecutor',
3332
'ProcessPoolExecutor',
3433
'ThreadPoolExecutor',
35-
)
34+
]
35+
36+
37+
try:
38+
import _interpreters
39+
except ImportError:
40+
_interpreters = None
41+
42+
if _interpreters:
43+
__all__.append('InterpreterPoolExecutor')
3644

3745

3846
def __dir__():
@@ -43,22 +51,15 @@ def __getattr__(name):
4351
global ProcessPoolExecutor, ThreadPoolExecutor, InterpreterPoolExecutor
4452

4553
if name == 'ProcessPoolExecutor':
46-
from .process import ProcessPoolExecutor as pe
47-
ProcessPoolExecutor = pe
48-
return pe
54+
from .process import ProcessPoolExecutor
55+
return ProcessPoolExecutor
4956

5057
if name == 'ThreadPoolExecutor':
51-
from .thread import ThreadPoolExecutor as te
52-
ThreadPoolExecutor = te
53-
return te
58+
from .thread import ThreadPoolExecutor
59+
return ThreadPoolExecutor
5460

55-
if name == 'InterpreterPoolExecutor':
56-
try:
57-
from .interpreter import InterpreterPoolExecutor as ie
58-
except ModuleNotFoundError:
59-
ie = InterpreterPoolExecutor = None
60-
else:
61-
InterpreterPoolExecutor = ie
62-
return ie
61+
if _interpreters and name == 'InterpreterPoolExecutor':
62+
from .interpreter import InterpreterPoolExecutor
63+
return InterpreterPoolExecutor
6364

6465
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

Lib/test/test_concurrent_futures/test_interpreter_pool.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
import contextlib
33
import io
44
import os
5+
import subprocess
56
import sys
7+
import textwrap
68
import time
79
import unittest
810
from concurrent.futures.interpreter import BrokenInterpreterPool
@@ -457,6 +459,45 @@ def test_free_reference(self):
457459
# Weak references don't cross between interpreters.
458460
raise unittest.SkipTest('not applicable')
459461

462+
@support.requires_subprocess()
463+
def test_import_interpreter_pool_executor(self):
464+
# Test the import behavior normally if _interpreters is unavailable.
465+
code = textwrap.dedent("""
466+
import sys
467+
# Set it to None to emulate the case when _interpreter is unavailable.
468+
sys.modules['_interpreters'] = None
469+
from concurrent import futures
470+
471+
try:
472+
futures.InterpreterPoolExecutor
473+
except AttributeError:
474+
pass
475+
else:
476+
print('AttributeError not raised!', file=sys.stderr)
477+
sys.exit(1)
478+
479+
try:
480+
from concurrent.futures import InterpreterPoolExecutor
481+
except ImportError:
482+
pass
483+
else:
484+
print('ImportError not raised!', file=sys.stderr)
485+
sys.exit(1)
486+
487+
from concurrent.futures import *
488+
489+
if 'InterpreterPoolExecutor' in globals():
490+
print('InterpreterPoolExecutor should not be imported!',
491+
file=sys.stderr)
492+
sys.exit(1)
493+
""")
494+
495+
cmd = [sys.executable, '-c', code]
496+
p = subprocess.run(cmd, capture_output=True)
497+
self.assertEqual(p.returncode, 0, p.stderr.decode())
498+
self.assertEqual(p.stdout.decode(), '')
499+
self.assertEqual(p.stderr.decode(), '')
500+
460501

461502
class AsyncioTest(InterpretersMixin, testasyncio_utils.TestCase):
462503

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Raises :exc:`AttributeError` when accessing
2+
:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are
3+
not available.

0 commit comments

Comments
 (0)