The example code for the ProcessPoolExecutor listed on the concurrent.futures page does not actually run on Python 3.14 (I only tested on Linux). This seems to be caused by the change of the start method away from fork as mentioned in a change notice further up on the page. I must say I don’t fully understand why the code would not work and find it a bit odd. Changing the start method to fork fixes the issue.
Here is a quick example I used to test:
podman run -it docker.io/python:3.14 python3
(change to docker or run with you local interpreter,I just wanted to exclude that it is caused by my distributions Python install)
Copy-and-paste the example code:
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
This will raise a AttributeError: module '__main__' has no attribute 'is_prime'.
Full error message
Process SpawnProcess-4:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/multiprocessing/process.py", line 320, in _bootstrap
self.run()
~~~~~~~~^^
File "/usr/local/lib/python3.14/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/concurrent/futures/process.py", line 242, in _process_worker
call_item = call_queue.get(block=True)
File "/usr/local/lib/python3.14/multiprocessing/queues.py", line 120, in get
return _ForkingPickler.loads(res)
~~~~~~~~~~~~~~~~~~~~~^^^^^
AttributeError: module '__main__' has no attribute 'is_prime'
Process SpawnProcess-1:
Traceback (most recent call last):
Traceback (most recent call last):
File "<python-input-2>", line 32, in <module>
main()
~~~~^^
File "<python-input-2>", line 28, in main
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/concurrent/futures/process.py", line 712, in _chain_from_iterable_of_lists
for element in iterable:
^^^^^^^^
File "/usr/local/lib/python3.14/concurrent/futures/_base.py", line 645, in result_iterator
yield _result_or_cancel(fs.pop())
~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "/usr/local/lib/python3.14/concurrent/futures/_base.py", line 312, in _result_or_cancel
return fut.result(timeout)
~~~~~~~~~~^^^^^^^^^
File "/usr/local/lib/python3.14/concurrent/futures/_base.py", line 454, in result
return self.__get_result()
~~~~~~~~~~~~~~~~~^^
File "/usr/local/lib/python3.14/concurrent/futures/_base.py", line 396, in __get_result
raise self._exception
concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
Python 3.13 runs this without said exception. To fix the code under 3.14, you would have to force the start method to fork:
import multiprocessing
...
with concurrent.futures.ProcessPoolExecutor(mp_context=multiprocessing.get_context("fork")) as executor:
...
Full code snippet of the working example
import concurrent.futures
import math
import multiprocessing
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor(mp_context=multiprocessing.get_context("fork")) as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
I’m not so sure if the example code should be changed to use the fork method, but I also don’t understand why this code would fail for methods other then fork. (Maybe this thread should go in the Help category?) Can someone maybe shine some light onto why it fails? And I guess the documentation should be updated accordingly?