I bumped into a bit of a rough edge related to how concurrent.futures.Executor instances behave when they're used as context managers and there is an exception raised from the with statement body.
I had some code like this (reduced to the bare minimum):
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
futures = {
executor.submit(check_status, item): item
for item in to_check
}
for index, future in enumerate(concurrent.futures.as_completed(futures)):
print(f"{index}/{len(futures)} {future.result()}")
Now, as it happens check_status liked to raise an exception from time to time and this would be fine if the application stopped because of that but it kept going but in a weird way. What I saw was check_status kept producing logs but the print calls in the snippet above stopped being executed.
This is because of how Executor implements __exit__ – it waits for all pending futures to complete before propagating the exception:
def __exit__(self, exc_type, exc_val, exc_tb):
self.shutdown(wait=True)
return False
No in my case I started with thousands of futures so it could take hours for the process to finally crash.
Since Python 3.9 the shutdown method has the cancel_futures parameter (implemented in #18057): https://docs.python.org/3.9/library/concurrent.futures.html#concurrent.futures.Executor.shutdown.
I'm proposing changing the behavior of the context manager __exit__ to cancel futures if there is an exception:
def __exit__(self, exc_type, exc_val, exc_tb):
self.shutdown(wait=True, cancel_futures=exc_val is not None)
return False
This would allow the in-flight futures to finish but any further processing wouldn't happen and the exception would be reported in a (more) timely fashion.
cc @brianquinlan @pitrou as I saw you participate in the cancel_futures PR, if there's someone else I should ping here for visibility please let me know.
I bumped into a bit of a rough edge related to how
concurrent.futures.Executorinstances behave when they're used as context managers and there is an exception raised from the with statement body.I had some code like this (reduced to the bare minimum):
Now, as it happens
check_statusliked to raise an exception from time to time and this would be fine if the application stopped because of that but it kept going but in a weird way. What I saw wascheck_statuskept producing logs but theprintcalls in the snippet above stopped being executed.This is because of how
Executorimplements__exit__– it waits for all pending futures to complete before propagating the exception:No in my case I started with thousands of futures so it could take hours for the process to finally crash.
Since Python 3.9 the
shutdownmethod has thecancel_futuresparameter (implemented in #18057): https://docs.python.org/3.9/library/concurrent.futures.html#concurrent.futures.Executor.shutdown.I'm proposing changing the behavior of the context manager
__exit__to cancel futures if there is an exception:This would allow the in-flight futures to finish but any further processing wouldn't happen and the exception would be reported in a (more) timely fashion.
cc @brianquinlan @pitrou as I saw you participate in the
cancel_futuresPR, if there's someone else I should ping here for visibility please let me know.