> I've never felt the need for either of these myself, nor have I observed it in others I worked with. In general I feel the difference between processes and threads is so large that I can't believe a realistic application would work with either.
Also, ThreadPoolExecutor and ProcessPoolExecutor both have their specific purposes in concurrent.futures: TPE for IO-bound parallelism, and PPE for CPU-bound parallelism, what niche would the proposed SerialExecutor fall under? Fake/dummy parallelism? If so, I personally don't see that as being worth the cost of adding it and then maintaining it in the standard library. But, that's not to say that it wouldn't have a place on PyPI. > (Then again I've never had much use for ProcessExecutor period.) I've also made use of TPE far more times than PPE, but I've definitely seen several interesting and useful real-world applications of PPE. Particularly with image processing. I can also imagine it also being quite useful for scientific computing, although I've not personally used it for that purpose. > IOW I'm rather lukewarm about this -- even if you (Jonathan) have found use for it, I'm not sure how many other people would use it, so I doubt it's worth adding it to the stdlib. (The only thing the stdlib might grow could be a public API that makes implementing this feasible without overriding private methods.) Expanding a bit upon the public API for the cf.Future class would likely allow something like this to be possible without accessing any private members. In particular, I believe there would have to be an public means of accessing the state of the future without having to go through the condition (currently, this can only be done with ``future._state``), and accessing a constant for each of the possible states: PENDING. RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, and FINISHED. Since that would actually be quite useful for debugging purposes (I had to access ``future._state`` several times while testing the new *cancel_futures*), I'd be willing to work on implementing something like this. On Sat, Feb 15, 2020 at 10:16 PM Guido van Rossum <gu...@python.org> wrote: > Having tried my hand at a simpler version for about 15 minutes, I see the > reason for the fiddly subclass of Future -- it seems over-engineered > because concurrent.future is complicated. > > I've never felt the need for either of these myself, nor have I observed > it in others I worked with. In general I feel the difference between > processes and threads is so large that I can't believe a realistic > application would work with either. (Then again I've never had much use for > ProcessExecutor period.) > > The "Serial" variants somehow remind me of the "dummy_thread.py" module we > had in Python 2. It was removed in Python 3, mostly because we ran out of > cases where real threads weren't an option. > > IOW I'm rather lukewarm about this -- even if you (Jonathan) have found > use for it, I'm not sure how many other people would use it, so I doubt > it's worth adding it to the stdlib. (The only thing the stdlib might grow > could be a public API that makes implementing this feasible without > overriding private methods.) > > On Sat, Feb 15, 2020 at 3:16 PM Jonathan Crall <erote...@gmail.com> wrote: > >> This implementation is a proof-of-concept that I've been using for awhile >> <https://gitlab.kitware.com/computer-vision/ndsampler/blob/master/ndsampler/util_futures.py>. >> 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. >> >> On Sat, Feb 15, 2020 at 5:16 PM Andrew Barnert <abarn...@yahoo.com> >> wrote: >> >>> > On Feb 15, 2020, at 13:36, Jonathan Crall <erote...@gmail.com> wrote: >>> > >>> > Also, there is no duck-typed class that behaves like an executor, but >>> does its processing in serial. Often times a develop will want to run a >>> task in parallel, but depending on the environment they may want to disable >>> threading or process execution. To address this I use a utility called a >>> `SerialExecutor` which shares an API with >>> ThreadPoolExecutor/ProcessPoolExecutor but executes processes sequentially >>> in the same python thread: >>> >>> This makes sense. I think most futures-and-executors frameworks in other >>> languages have a serial/synchronous/immediate/blocking executor just like >>> this. (And the ones that don’t, it’s usually because they have a different >>> way to specify the same functionality—e.g., in C++, you only use executors >>> via the std::async function, and you can just pass a launch option instead >>> of an executor to run synchronously.) >>> >>> And I’ve wanted this, and even built it myself at least once—it’s a >>> great way to get all of the logging in order to make things easier to >>> debug, for example. >>> >>> However, I think you may have overengineered this. >>> >>> Why can’t you use the existing Future type as-is? Yes, there’s a bit of >>> unnecessary overhead, but your reimplementation seems to add almost the >>> same unnecessary overhead. And does it make enough difference in practice >>> to be worth worrying about anyway? (It doesn’t for my uses, but maybe >>> you’re are different.) >>> >>> Also, why are you overriding set_result to restore pre-3.8 behavior? The >>> relevant change here seems to be the one where 3.8 prevents executors from >>> finishing already-finished (or canceled) futures; why does your executor >>> need that? >>> >>> Finally, why do you need a wrapper class that constructs one of the >>> three types at initialization and then just delegates all methods to it? >>> Why not just use a factory function that constructs and returns an instance >>> of one of the three types directly? And, given how trivial that factory >>> function is, does it even need to be in the stdlib? >>> >>> I may well be missing something that makes some of these choices >>> necessary or desirable. But otherwise, I think we’d be better off adding a >>> SerialExecutor (that works with the existing Future type as-is) but not >>> adding or changing anything else. >>> >>> >>> >> >> -- >> -Jon >> _______________________________________________ >> Python-ideas mailing list -- python-ideas@python.org >> To unsubscribe send an email to python-ideas-le...@python.org >> https://mail.python.org/mailman3/lists/python-ideas.python.org/ >> Message archived at >> https://mail.python.org/archives/list/python-ideas@python.org/message/AG3AXJFU4R2CU6JPWCQ2BYHUPH75MKUM/ >> Code of Conduct: http://python.org/psf/codeofconduct/ >> > > > -- > --Guido van Rossum (python.org/~guido) > *Pronouns: he/him **(why is my pronoun here?)* > <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/> > _______________________________________________ > Python-ideas mailing list -- python-ideas@python.org > To unsubscribe send an email to python-ideas-le...@python.org > https://mail.python.org/mailman3/lists/python-ideas.python.org/ > Message archived at > https://mail.python.org/archives/list/python-ideas@python.org/message/ICJKHZ4BPIUMOPIT2TDTBIW2EH4CPNCP/ > Code of Conduct: http://python.org/psf/codeofconduct/ >
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6GQZJWKHLAJ5P7ZJCASP6IFV6H55OOK5/ Code of Conduct: http://python.org/psf/codeofconduct/