curio v0.6 Release Notes

Release Date: 2017-02-15 // about 7 years ago
  • 02/13/2017 Added a withfd=True option to UniversalQueue. For example:

              q = UniversalQueue(withfd=True)
    
           If added, the queue internally sets up an I/O loopback
           where putting items on the queue write bytes to an I/O
           channel.  The queue then spouts a fileno() method and
           becomes pollable in other event loops.  This is potentially
           useful strategy for integrating Curio with GUIs and other
           kinds of foreign event loops.  
    

    02/11/2017 Added a guard for proper use of asynchronous generators involving asynchronous finalization. Must be wrapped by finalize(). For example:

           async def some_generator():
               ...
               try:
                   yield val
               finally:
                   await action()
    
           async def coro():
               ...
               async with finalize(some_generator()) as agen:
                   async for item in agen:
                       ...
    
           Failure to do this results in a RuntimeError if an
           asynchronous generator is iterated.   This is not needed for
           generators that don't perform finalization steps involving
           async code.
    

    02/08/2017 New Kernel.run() method implementation. It should be backwards compatible, but there are two new ways of using it:

               kernel = Kernel()
               ...
               # Run a coroutine with a timeout/deadline applied to it
               try:
                   result = kernel.run(coro, timeout=secs)
               except TaskTimeout:
                   print('Timed out')
    
               # Run all daemonic tasks through a single scheduling cycle
               # with no blocking
               kernel.run()
    
               # Run all daemonic tasks through a cycle, but specify a
               # timeout on internal blocking
               kernel.run(timeout=secs)
    

    02/06/2017 New aside() function for launching a Curio task in an independent process. For example:

           async def child(name, n):
               print('Hello from', name)
               for i in range(n):
                   print('name says', i)
                   await sleep(1)
    
           async def main():
               t = await aside(child, 'Spam', 10)   # Runs in subprocess
               await t.join()
    
           run(main())
    
           In a nutshell, aside(coro, *args, **kwargs) creates a clean
           Python interpreter and invokes curio.run(coro(*args,
           **kwargs)) on the supplied coroutine.  The return value of
           aside() is a Task object.  Joining with it returns the
           child exit code (normally 0).  Cancelling it causes a
           TaskCancelled exception to be raised in the child.
    
           aside() does not involve a process fork or pipe. There
           is no underlying communication between the child and parent
           process.  If you want communication, use a Channel object 
           or set up some other kind of networking.
    

    02/06/2017 Some improvements to message passing and launching tasks in subprocesses. A new Channel object makes it easy to establish message passing between two different interpreters. For example, here is a producer program:

           # producer.py
           from curio import Channel, run
    
           async def producer(ch):
               while True:
                   c = await ch.accept(authkey=b'peekaboo')
                   for i in range(10):
                       await c.send(i)
                   await c.send(None)   # Sentinel
    
           if __name__ == '__main__':
               ch = Channel(('localhost', 30000))
               run(producer(ch))
    
           Here is a consumer program::
    
           # consumer.py
           from curio import Channel, run
    
           async def consumer(ch):
               c = await ch.connect(authkey=b'peekaboo')
               while True:
                   msg = await c.recv()
                   if msg is None:
                      break
                   print('Got:', msg)
    
           if __name__ == '__main__':
              ch = Channel(('localhost', 30000))
              run(consumer(ch))
    
           A Channel is a lot like a socket except that it sends discrete
           messages.   Any picklable Python compatible object can be
           passed.
    

    🛠 02/03/2017 Fixed a few regressions in SSL sockets and the Kernel.run() method.