2A snippet for running multiple, concurrent invocations of a Python function.
5from collections.abc import Callable, Iterable
6import concurrent.futures
11In = typing.TypeVar("In")
12Out = typing.TypeVar("Out")
16 handler: Callable[[In], Out], inputs: Iterable[In], *, max_concurrency: int = 5
17) -> Iterable[tuple[In, Out]]:
19 Call the function ``handler`` on the values ``inputs``.
21 ``handler`` should be a function that takes a single input, which is the
22 individual values in the iterable ``inputs``.
24 Generates (input, output) tuples as the calls to ``handler`` complete.
26 See https://alexwlchan.net/2019/10/adventures-with-concurrent-futures/
27 for an explanation of how this function works.
30 # Make sure we get a consistent iterator throughout, rather than
31 # getting the first element repeatedly.
32 handler_inputs = iter(inputs)
34 with concurrent.futures.ThreadPoolExecutor() as executor:
36 executor.submit(handler, input): input
37 for input in itertools.islice(handler_inputs, max_concurrency)
41 done, _ = concurrent.futures.wait(
42 futures, return_when=concurrent.futures.FIRST_COMPLETED
46 original_input = futures.pop(fut)
47 yield original_input, fut.result()
49 for input in itertools.islice(handler_inputs, len(done)):
50 fut = executor.submit(handler, input)