This implementation is a proof-of-concept that I've been using for
awhile. Its certain that any version that made it into the stdlib would have to be more carefully designed than the implementation I threw together. However, my implementation demonstrates the concept and there are reasons for the choices I made.
First, the choice to create a SerialFuture object that inherits from the base Future was because I only wanted a process to run if the SerialFuture.result method was called. The most obvious way to do that was to overload the `result` method to execute the function when called. Perhaps there is a better way, but in an effort to KISS I just went with the <100 line version that seemed to work well enough.
The `set_result` is overloaded because in Python 3.8, the base Future.set_result function asserts that the _state is not FINISHED when it is called. In my proof-of-concept implementation I had to set state of the SerialFuture._state to FINISHED in order for `as_completed` to yield it. Again, there may be a better way to do this, but I don't claim to know what that is yet.
I was thinking that a factory function might be a good idea, but if I was designing the system I would have put that in the abstract Executor class. Maybe something like
```
@classmethod
def create(cls, mode, max_workers=0):
""" Create an instance of a serial, thread, or process-based executor """
from concurrent import futures
if mode == 'serial' or max_workers == 0:
return futures.SerialExecutor()
elif mode == 'thread':
return futures.ThreadPoolExecutor(max_workers=max_workers)
elif mode == 'process':
return futures.ProcessPoolExecutor(max_workers=max_workers)
else:
raise KeyError(mode)
```
I do think that it would improve the standard lib to have something like this --- again perhaps not this exact version (it does seem a bit weird to give this method to an abstract class), but some common API that makes it easy for the user to swap between the backend Executor implementation. Even though the implementation is "trivial", lots of things in the standard lib are, but they the reduce boilerplate that developers would otherwise need, provide examples of good practices to new developers, and provide a defacto way to do something that might otherwise be implemented differently by different people, so it adds value to the stdlib.
That being said, while I will advocate for the inclusion of such a factory method or wrapper class, it would only be a minor annoyance to not have it. On the other hand I think a SerialExecutor is something that is sorely missing from the standard library.