Tuesday, January 24, 2012

A simple event loop with PEP 380

On January 13, Nick Coghlan pushed up the C implementation of PEP 380, which officially supports yield from syntax, to Python 3. I have toyed around with yield from idea since P. J. Eby cooked up his fiendishly clever trampoline in pure Python. The official C implementation is a welcomed news because it alleviates the need for such a trampoline from app to app.

Here's a simple event loop that makes use of PEP 380.

import heapq
import inspect
import time

_processes = []

def sleep(delay):
    yield 'sleep', delay

def task(name, delay):
    for i in range(10):
        print('hello {0} {1} {2}'.format(time.time(), name, i))
        yield from sleep(delay)

def spawn(proc, *args, **kw_args):
    def generatorize(f, *args, **kw_args):
        yield 'ignore', f(*args, **kw_args)
    global _processes
    if not inspect.isgenerator(proc):
        if inspect.isgeneratorfunction(proc):
            proc = proc(*args, **kw_args)
        else:
            proc = generatorize(proc, *args, **kw_args)
    heapq.heappush(_processes, (time.time(), proc))

def run():
    global _processes
    while _processes:
        (start_time, proc) = heapq.heappop(_processes)
        if start_time > time.time():
            heapq.heappush(_processes, (start_time, proc))
            continue
        try:
            command, arg = next(proc)
        except StopIteration:
            pass
        else:
            if command == 'sleep':
                heapq.heappush(_processes, (time.time() + arg, proc))

def not_a_task():
    print('not a task')

spawn(task, 'task 0.5', 0.5)
spawn(task, 'task 1.0', 1.0)
spawn(not_a_task)

run()

The main functions are spawn and run. In spawn, a function object is converted into a generator if needed and then pushed into a heap queue of scheduled processes. These processes are given a chance to run when they are up in schedule. They can voluntarily yield execution to other processes by calling yield from sleep, or some other utility functions such as read or write.

Overall, I think the code is quite compact and readable.

No comments:

Post a Comment