Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jc2k/cringe

Greenlets for GObject Introspection - making non-blocking GNOME code a pleasure to write and read
https://github.com/jc2k/cringe

Last synced: about 1 month ago
JSON representation

Greenlets for GObject Introspection - making non-blocking GNOME code a pleasure to write and read

Awesome Lists containing this project

README

        

=====================================
CoRoutines In Nice Gnome Environment
=====================================

This is basically gevent, but using the GNOME platform libraries.

GLib based API's often have asynchronous versions. These tend to use the Gio
pattern which looks something like this::

from gi.repository import GLib, Gio

def _loaded(f, result, user_data):
success, contents, etag = f.load_contents_finish(result)
print len(contents)

def _load_stuff(...):
f = Gio.File.new_for_uri(uri)
f.load_contents_async(None, _loaded, None)

GLib.idle_add(_load_stuff)
loop = GLib.MainLoop()
loop.run()

But with a sprinkling of greenlet it looks like this::

from cringe import patch_all()
patch_all()
from gi.repository import GLib, Gio

def _load_stuff(...):
f = Gio.File.new_for_uri(uri)
contents = f.load_contents()
print len(contents)

GLib.idle_add(_load_stuff)
GLib.main()

This is still asynchronous! The cringe patch modifies the gi generated bindings
to pause the currently running greenlet when it is waiting for an async
operation to complete. Control can then return to the mainloop, which can
process other incoming events.

Implementation
==============

All Gio-style async API's have an ``_async`` and ``_finish`` method. The
``_async`` function takes a ``callback`` and ``user_data`` parameter, and the
callback gets passed a ``AsyncResult`` which can be used to collect the actual
return value.

Wrapping this in Greenlet magic gives us::

def function(*args, **kwargs):
current = greenlet.getcurrent()

# Call the function and pass it current.switch as the callback - this
# is what allows the current coroutine to be resumed
args = args + (current.switch, None)
some_api_async(*args, **kwargs)

# Pause the current coroutine. It will be resumed here when the
# callback calls current.switch()
obj, result, _ = current.parent.switch()

# Actually return the expected value
return some_api_finish(result)

The GLib API mostly followed the same pattern for all of its asynchronous code.
This means we can easily apply this pattern to all bindings automagically by
hooking in to GObjectMeta (in ``gi.types``).

We monkey-patch in our GreenletMeta which uses heuristics to patch the async
API's it can recognise.