@Guido As an aside, my understanding was libraries that fall back to c (Numpy as an example) release the GIL for load heavy operations. But I believe the explanation would hold in the general case if you replace thread with process using a ProcessPoolExecutor, that it would be good to be able to submit a callback function back to the executor.
Sent from my iPhone > On Jan 26, 2018, at 12:10 PM, Daniel Collins <dancollin...@gmail.com> wrote: > > @Guido: I agree, that’s a much cleaner solution to pass the executor. > However, I think the last line should be future.add_done_callback(callback) > return newf > > not executor.submit. > > I’ll rewrite it like this and resubmit tonight for discussion. > > Sent from my iPhone > >> On Jan 26, 2018, at 11:59 AM, Guido van Rossum <gu...@python.org> wrote: >> >> @Bar: I don't know about exposing _chain_future(). Certainly it's overkill >> for what the OP wants -- their PR only cares about chaining >> concurrent.future.Future. >> >> @Daniel: I present the following simpler solution -- it requires you to >> explicitly pass the executor, but since 'fn' is being submitted to an >> executor, EIBTI. >> >> def then(executor, future, fn): >> newf = concurrent.futures.Future() >> def callback(fut): >> f = executor.submit(fn, fut) >> try: >> newf.set_result(f.result()) >> except CancelledError: >> newf.cancel() >> except Exception as err: >> newf.set_exception(err) >> return executor.submit(callback) >> >> I can't quite follow your reasoning about worker threads (and did you >> realize that because of the GIL, Python doesn't actually use multiple >> cores?). But I suppose it doesn't matter whether I understand that -- your >> point is that you want the 'fn' function submitted to the executor, not run >> as a "done callback". And that's reasonable. But modifying so much code just >> so the Future can know which to executor it belongs so you can make then() a >> method seems overkill. >> >>> On Fri, Jan 26, 2018 at 8:54 AM, Daniel Collins <dancollin...@gmail.com> >>> wrote: >>> So, just going point by point: >>> >>> Yes, absolutely put this off for 3.8. I didn’t know the freeze was so close >>> or I would have put the 3.8 tag on originally. >>> >>> Yes, absolutely it is only meant for concurrent.futures futures, it only >>> changes async where async uses concurrent.futures futures. >>> >>> Here’s a more fleshed out description of the use case: >>> >>> Assume you have two functions. Function a(x: str)->AResult fetches an >>> AResult object from a web resource, function b(y: AResult) performs some >>> computationally heavy work on AResult. >>> >>> Assume you’re calling a 10 times with a threadpoolexecutor with 2 worker >>> theads. If you were to schedule a as future using submit, and b as a >>> callback, the executions would look like this: >>> >>> ExecutorThread: b*10 >>> Worker1: a*5 >>> Worker2: a*5 >>> >>> This only gets worse as more work (b) is scheduled as a callback for the >>> result from a. >>> >>> Now you could resolve this by, instead of submitting b as a callback, >>> submitting the following lambda: >>> >>> lambda x: executor.submit(b, x) >>> >>> But then you wouldn’t have easy access to this new future. You would have >>> to build a lot of boilerplate code to collect that future into some >>> external collection, and this would only get worse the deeper the nesting >>> goes. >>> >>> With this syntax on the other hand, if you run a 10 times using submit, but >>> then run a_fut.then(b) for each future, execution instead looks like this: >>> >>> ExecutorThread: >>> Worker1: a*5 b*5 >>> Worker2: a*5 b*5 >>> >>> You can also do additional depth easily. Suppose you want to run 3 c >>> operations (processes the output of b) for each b operation. Then you could >>> call this like >>> >>> b_fut = a_fut.then(b) >>> >>> for i in range(3): >>> b_fut.then(c) >>> >>> And the execution would look like this: >>> >>> ExecutorThread: >>> Worker1: a*5 b*5 c*15 >>> Worker2: a*5 b*5 c*15 >>> >>> Which would be very difficult to do otherwise, and distributes the load >>> across the workers, while having direct access to the outputs of the calls >>> to c. >>> >>> -dancollins34 >>> >>> Sent from my iPhone >>> >>>> On Jan 26, 2018, at 1:07 AM, Guido van Rossum <gu...@python.org> wrote: >>>> >>>> I really don't want to distract Yury with this. Let's consider this (or >>>> something that addresses the same need) for 3.8. >>>> >>>> To be clear this is meant as a feature for concurrent.futures.Future, not >>>> for asyncio.Future. (It's a bit confusing since you also change asyncio.) >>>> >>>> Also to be honest I don't understand the use case *or* the semantics very >>>> well. You have some explaining to do... >>>> >>>> (Also, full links: https://bugs.python.org/issue32672; >>>> https://github.com/python/cpython/pull/5335) >>>> >>>>> On Thu, Jan 25, 2018 at 8:38 PM, Daniel Collins <dancollin...@gmail.com> >>>>> wrote: >>>>> Hello all, >>>>> >>>>> So, first time posting here. I’ve been bothered for a while about the >>>>> lack of the ability to chain futures in python, such that the next future >>>>> will execute upon the first’s completion. So I submitted a pr to do >>>>> this. This would add the .then(self, fn) method to >>>>> concurrent.futures.Future. Thoughts? >>>>> >>>>> -dancollins34 >>>>> >>>>> Github PR #5335 >>>>> bugs.python.org issue #32672 >>>>> >>>>> _______________________________________________ >>>>> Python-ideas mailing list >>>>> Python-ideas@python.org >>>>> https://mail.python.org/mailman/listinfo/python-ideas >>>>> Code of Conduct: http://python.org/psf/codeofconduct/ >>>>> >>>> >>>> >>>> >>>> -- >>>> --Guido van Rossum (python.org/~guido) >> >> >> >> -- >> --Guido van Rossum (python.org/~guido)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/