Repository navigation
InterpreterPoolExecutor does not work as replacement of ThreadPoolExecutor. #136603
Copy link
Copy link
Closed
Labels
3.14bugs and security fixesbugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-subinterpreterstype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
According to PEP-734, it should work as same as ThreadPoolExecutor, but it looks not.
Code
import concurrent.futures
from concurrent.futures import InterpreterPoolExecutor, ThreadPoolExecutor
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
with InterpreterPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(fib, n): n for n in range(10)}
for future in concurrent.futures.as_completed(futures):
n = futures[future]
data = future.result()
print(f"fib({n}): {data}")Execution (InterpreterPoolExecutor)
Exception: AttributeError: module '__main__' has no attribute 'fib'
The above exception was the direct cause of the following exception:
concurrent.interpreters.NotShareableError: object could not be unpickled
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/xxxx/oss/cpython/op.py", line 14, in <module>
data = future.result()
File "/Users/xxxx/oss/cpython/Lib/concurrent/futures/_base.py", line 443, in result
return self.__get_result()
~~~~~~~~~~~~~~~~~^^
File "/Users/xxxx/oss/cpython/Lib/concurrent/futures/_base.py", line 395, in __get_result
raise self._exception
File "/Users/xxxx/oss/cpython/Lib/concurrent/futures/thread.py", line 86, in run
result = ctx.run(self.task)
File "/Users/denny.na/oss/cpython/Lib/concurrent/futures/interpreter.py", line 85, in run
return self.interp.call(do_call, self.results, *task)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/xxxx/oss/cpython/Lib/concurrent/interpreters/__init__.py", line 233, in call
return self._call(callable, args, kwargs)
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/xxxx/oss/cpython/Lib/concurrent/interpreters/__init__.py", line 215, in _call
res, excinfo = _interpreters.call(self._id, callable, args, kwargs, restrict=True)
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
concurrent.interpreters.NotShareableError: args not shareableExecution (ThreadPoolExecutor)
fib(0): 0
fib(7): 13
fib(2): 1
fib(6): 8
fib(1): 1
fib(4): 3
fib(3): 2
fib(5): 5
fib(9): 34
fib(8): 21
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.14bugs and security fixesbugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-subinterpreterstype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Projects
- StatusShow more project fieldsDone
Activity
corona10 commentedon Jul 13, 2025
cc @hugovk We may need to add label release blocker for 3.14
xuantengh commentedon Jul 13, 2025
I think this is because
do_callis treated as a stateful function that cannot be shared between multiple interpreters:The
_interp_call_packfunction will check whether the function is stateless:cpython/Modules/_interpretersmodule.c
Lines 467 to 488 in 42b251b
where
do_callfails due to:cpython/Objects/codeobject.c
Lines 2006 to 2008 in 42b251b
And the unbound vars seem to be:
['BaseException', 'interpreters', 'print', 'sys', 'traceback']corona10 commentedon Jul 13, 2025
@xuantengh
I am waiting for @ericsnowcurrently 's reply. If this is the intention, most people will treat InterpreterPoolExecutor as the same as ThreadPoolExecutor, but actual behavior does not seem to be so; it should be documented.
But if it is not intended, it should be resolved before officially releasing 3.14.
efimov-mikhail commentedon Jul 13, 2025
JFTR: it works if we slightly rewrite this code:
Tapeline commentedon Jul 13, 2025
As
fibaccesses the global scope to get itself it won't work. Thx to @efimov-mikhail for his example. I think we should consider adding some note to docs/howto about subinterpreters, since recursive functions are pretty commonly used for calculations that we want to parallelise.xuantengh commentedon Jul 13, 2025
Maybe I can file a PR for this?
paultiq commentedon Jul 13, 2025
There used to be a comment in concurrent.futures.rst for InterpreterPoolExecutor saying:
Functions defined in the main module cannot be pickled and thus cannot be used.This comment was removed in #133957
Tapeline commentedon Jul 13, 2025
It would be great, but it totally needs prior discussion. I think we should add a big section about recipes with subinterpreters, because there are more problems, than just defining recursive functions. E.g. top-level imports don't work either for the same reason. We should define preferred way to handle all these cases (ofc the discussion should involve Eric Snow and others involved in subinterpreters). We need to open another issue (or a discuss.python.org topic, potentially) :)
efimov-mikhail commentedon Jul 13, 2025
Maybe we should start a d.p.o. thread on "How to teach subinterpreters" or something similar?
corona10 commentedon Jul 13, 2025
Can we just wait Eric's comment before do something?
corona10 commentedon Jul 14, 2025
I clarified the issue that I met at #136659 (comment)
I think that we can reduce the scope of the issue because it is okay to run from Python 3.14b4 with
__name__ == '__main__'idiom.So it looks reasonable to close this issue and focus on #136659
cc @ericsnowcurrently