[Python-ideas] Async API: some code to review
Guido van Rossum
guido at python.org
Tue Oct 30 03:07:21 CET 2012
On Mon, Oct 29, 2012 at 5:43 PM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:
> Finally got some time to do a review & read what others posted.
Great!
> Some comments are more general, some are more implementation-specific
> (hopefully you want to hear latter ones as well)
Yes!
> And I'm still in the process of digesting your approach & code (as
> I've spent too much time with my implementation)...
Heh. :-)
> On 2012-10-28, at 7:52 PM, Guido van Rossum <guido at python.org> wrote:
> [...]
>> polling.py: http://code.google.com/p/tulip/source/browse/polling.py
> [...]
>
> 1. I'd make EventLoopMixin a separate entity from pollsters. So that you'd
> be able to add many different pollsters to one EventLoop. This way
> you can have specialized pollster for different types of IO, including
> UI etc.
I came to the same conclusion, so I fixed this. See the latest version.
(BTW, I also renamed add_reader() etc. on the Pollster class to
register_reader() etc. -- I dislike similar APIs on different classes
to have the same name if there's not a strict super class override
involved.)
> 2. Sometimes, there is a need to run a coroutine in a threadpool. I know it
> sounds weird, but it's probably worth exploring.
I think that can be done quite simply. Since each thread has its own
eventloop (via the magic of TLS), it's as simple as writing a function
that creates a task, starts it, and then runs the eventloop. There's
nothing else running in that particular thread, and its eventloop will
terminate when there's nothing left to do there -- i.e. when the task
is done. Sketch:
def some_generator(arg):
...stuff using yield from...
return 42
def run_it_in_the_threadpool(arg):
t = Task(some_generator(arg))
t.start()
scheduling.run()
return t.result
# And in your code:
result = yield from scheduling.call_in_thread(run_it_in_the_threadpool, arg)
# Now result == 42.
> 3. In my framework each threadpool worker has its own local context, with
> various information like what Task run the operation etc.
I think I have this too -- Thread-Local Storage!
> And few small things:
>
> 4. epoll.poll and other syscalls need to be wrapped in try..except to catch
> and ignore (and log?) EINTR type of exceptions.
Good point.
> 5. For epoll you probably want to check/(log?) EPOLLHUP and EPOLLERR errors
> too.
Do you have a code sample? I haven't found a need yet.
>> scheduling.py: http://code.google.com/p/tulip/source/browse/scheduling.py
> [...]
>
>> In the docstrings I use the prefix "COROUTINE:" to indicate public
>> APIs that should be invoked using yield from.
> [...]
>
> As others, I would definitely suggest adding a decorator to make
> coroutines more distinguishable.
That's definitely on my TODO list.
> It would be even better if we can return
> a tiny wrapper, that lets you to simply write 'doit.run().with_timeout(2.1)',
> instead of:
>
> task = scheduling.Task(doit(), timeout=2.1)
> task.start()
> scheduling.run()
The run() call shouldn't be necessary unless you are at the toplevel.
> And avoid manual Task instantiation at all.
Hm. I want the generator function to return just a generator object,
and I can't add methods to that. But we can come up with a decent API.
> I also liked the simplicity of the Task class. I think it'd be easy
> to mix greenlets in it by switching in a new greenlet on each 'step'.
> That will give you 'yield_()' function, which you can use in the same
> way you use 'yield' statement now (I'm not proposing to incorporate
> greenlets in the lib itself, but rather to provide an option to do so)
> Hence there should be a way to plug your own Task (sub-)class in.
Hm. Someone else will have to give that a try.
Thanks for your feedback!!
--
--Guido van Rossum (python.org/~guido)
More information about the Python-ideas
mailing list