Skip to content

Commit bd1a8df

Browse files
Copilotakhmerov
andcommitted
Add InterpreterPoolExecutor support for Python 3.14+
Co-authored-by: akhmerov <2069677+akhmerov@users.noreply.github.com>
1 parent b186cd3 commit bd1a8df

3 files changed

Lines changed: 64 additions & 13 deletions

File tree

adaptive/_types.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# Workaround described in https://github.com/agronholm/typeguard/issues/456
33

44
import concurrent.futures as concurrent
5-
from typing import TypeAlias
5+
import sys
6+
from typing import TYPE_CHECKING, TypeAlias
67

78
import distributed
89
import ipyparallel
@@ -11,14 +12,31 @@
1112

1213
from adaptive.utils import SequentialExecutor
1314

14-
ExecutorTypes: TypeAlias = (
15-
concurrent.ProcessPoolExecutor
16-
| concurrent.ThreadPoolExecutor
17-
| SequentialExecutor
18-
| loky.reusable_executor._ReusablePoolExecutor
19-
| distributed.Client
20-
| distributed.cfexecutor.ClientExecutor
21-
| mpi4py.futures.MPIPoolExecutor
22-
| ipyparallel.Client
23-
| ipyparallel.client.view.ViewExecutor
24-
)
15+
# For Python 3.14+, include InterpreterPoolExecutor in the type alias
16+
if sys.version_info >= (3, 14):
17+
if TYPE_CHECKING:
18+
# Type checkers will see this when checking Python 3.14+ code
19+
ExecutorTypes: TypeAlias = (
20+
concurrent.ProcessPoolExecutor
21+
| concurrent.ThreadPoolExecutor
22+
| concurrent.InterpreterPoolExecutor # type: ignore[attr-defined]
23+
| SequentialExecutor
24+
| loky.reusable_executor._ReusablePoolExecutor
25+
| distributed.Client
26+
| distributed.cfexecutor.ClientExecutor
27+
| mpi4py.futures.MPIPoolExecutor
28+
| ipyparallel.Client
29+
| ipyparallel.client.view.ViewExecutor
30+
)
31+
else:
32+
ExecutorTypes: TypeAlias = (
33+
concurrent.ProcessPoolExecutor
34+
| concurrent.ThreadPoolExecutor
35+
| SequentialExecutor
36+
| loky.reusable_executor._ReusablePoolExecutor
37+
| distributed.Client
38+
| distributed.cfexecutor.ClientExecutor
39+
| mpi4py.futures.MPIPoolExecutor
40+
| ipyparallel.Client
41+
| ipyparallel.client.view.ViewExecutor
42+
)

adaptive/runner.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@
4444

4545

4646
# -- Runner definitions
47-
if platform.system() == "Linux":
47+
# Check if InterpreterPoolExecutor is available (Python 3.14+)
48+
_has_interpreter_pool = hasattr(concurrent, "InterpreterPoolExecutor")
49+
50+
if _has_interpreter_pool:
51+
# Use InterpreterPoolExecutor for Python 3.14+
52+
# It provides better isolation and performance than ProcessPoolExecutor
53+
_default_executor = concurrent.InterpreterPoolExecutor # type: ignore[misc,attr-defined]
54+
elif platform.system() == "Linux":
4855
_default_executor = concurrent.ProcessPoolExecutor # type: ignore[misc]
4956
else:
5057
# On Windows and MacOS functions, the __main__ module must be
@@ -1025,6 +1032,8 @@ def _get_ncores(
10251032
return len(ex.view)
10261033
elif isinstance(ex, concurrent.ProcessPoolExecutor | concurrent.ThreadPoolExecutor):
10271034
return ex._max_workers # type: ignore[union-attr]
1035+
elif _has_interpreter_pool and isinstance(ex, concurrent.InterpreterPoolExecutor): # type: ignore[attr-defined]
1036+
return ex._max_workers # type: ignore[union-attr]
10281037
elif isinstance(ex, loky.reusable_executor._ReusablePoolExecutor):
10291038
return ex._max_workers # type: ignore[union-attr]
10301039
elif isinstance(ex, SequentialExecutor):

adaptive/tests/test_runner.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,27 @@ def counting_ask(self, n, tell_pending=True):
263263
finally:
264264
# Restore original method
265265
Learner1D.ask = original_ask
266+
267+
268+
def test_interpreter_pool_executor_detection():
269+
"""Test that InterpreterPoolExecutor is detected and used in Python 3.14+."""
270+
import concurrent.futures as concurrent
271+
import sys
272+
273+
from adaptive.runner import _default_executor, _has_interpreter_pool
274+
275+
if sys.version_info >= (3, 14) and hasattr(concurrent, "InterpreterPoolExecutor"):
276+
# On Python 3.14+, InterpreterPoolExecutor should be available and used
277+
assert _has_interpreter_pool is True
278+
assert _default_executor == concurrent.InterpreterPoolExecutor
279+
else:
280+
# On older Python versions, it should not be available
281+
assert _has_interpreter_pool is False
282+
# Should fall back to ProcessPoolExecutor on Linux or loky on others
283+
if OPERATING_SYSTEM == "Linux":
284+
assert _default_executor == concurrent.ProcessPoolExecutor
285+
else:
286+
import loky
287+
288+
assert _default_executor == loky.get_reusable_executor
289+

0 commit comments

Comments
 (0)