[Python-ideas] How the heck does async/await work in Python 3.5

Maxime S maxischmeii at gmail.com
Fri Feb 26 17:12:16 EST 2016


2016-02-25 17:47 GMT+01:00 Guido van Rossum <guido at python.org>:

> When you're implementing this API on top of tkinter, you'll probably
> find that you'll have to use tkinter's way of sleeping anyways, so the
> implementation of waiting in BaseEventLoop using a selector is not
> useful for this scenario.
>
> There are probably some possible refactorings in the asyncio package
> to help you reuse a little more code, but all in all I still think it
> would be very useful to have an asyncio loop integrated with Tkinter.
> (Of course Tkinter does support network I/O, so it would be possible
> to integrate with that, too. Or some hybrid where you somehow figure
> out how to wait using a Selector *or* tkinter events in the same
> loop.)
>

It is actually quite easy to implement an asyncio loop over tkinter once
you realise that tkapp.dooneevent() is very similar to poll(), and
tkapp.createfilehandler() is very similar to register() (which is not that
surprising since tcl use poll() internally if available).

Thus it is possible to create a tk selector and reuse all the code in
SelectorEventLoop. Unfortunately createfilehandler() is only available on
UNIX, so some form of threading is probably innevitable on Windows.

class TkSelector(selectors._BaseSelectorImpl):
    """Selector based on the Tk event loop."""

    def __init__(self, app):
        super().__init__()
        self.app = app
        self.ready = []
        self.is_timeout = False
        self.after_key = None

    def _file_cb(self, fileobj, mask):
        fd = self._fileobj_lookup(fileobj)
        self.ready.append((fd, mask))

    def _timeout_cb(self):
        self.is_timeout = True

    def _reset_state(self):
        del self.ready[:]
        self.is_timeout = False
        if self.after_key:
            self.app.after_cancel(self.after_key)
            self.after_key = None

    def register(self, fileobj, events, data=None):
        key = super().register(fileobj, events, data)
        flags = 0
        if events & EVENT_READ:
            flags |= tkinter.READABLE
        if events & EVENT_WRITE:
            flags |= tkinter.WRITABLE
        self.app.createfilehandler(fileobj, flags, self._file_cb)
        return key

    def unregister(self, fileobj):
        key = super().unregister(fileobj)
        self.app.deletefilehandler(fileobj)
        return key

    def select(self, timeout=None):
        if timeout is None:
            pass
        elif timeout <= 0:
            while self.app.dooneevent(_tkinter.DONT_WAIT):
                pass
            self.is_timeout = True
        else:
            self.after_key = self.app.after(math.ceil(timeout*1000),
self._timeout_cb)

        while not (self.is_timeout or self.ready):
            self.app.dooneevent()

        ret = []
        for fd, mask in self.ready:
            events = 0
            if mask & tkinter.WRITABLE:
                events |= EVENT_WRITE
            if mask & tkinter.READABLE:
                events |= EVENT_READ

            key = self._key_from_fd(fd)
            if key:
                ret.append((key, events & key.events))

        self._reset_state()

        return ret

class TkEventLoop(asyncio.SelectorEventLoop):
    """Asyncio-compatible tkinter event loop."""

    def __init__(self, app):
        selector = TkSelector(app)
        super().__init__(selector=selector)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160226/03822b9d/attachment-0001.html>


More information about the Python-ideas mailing list