-
-
Notifications
You must be signed in to change notification settings - Fork 24
PyGObject and asyncio #32
Comments
It would be awesome! Now I have to use my "poor man integration": https://github.com/pychess/pychess/blob/master/pychess#L224 in PyChess because gbulb has unresolved issues. If gbulb can be integrated into PyGObject I hope it will get more developers/contributors and may boost it's development. |
As someone that has used both libraries: +1 one on this. I'd imagine there may be potential to make the integration better / easier. There aren't a huge amount of developers on both projects, joining forces probably makes sense. |
I'm certainly interested in this! It'd be good to get some more people in the code base. At the moment the code that's in master hasn't been released - it's essentially a reimplementation that gives a more full-fledged event loop that adds Windows support (instead of hacking support in on top of the Unix event loop), so I'm understandably paranoid about it. The end result is the same on Linux, however the biggest issue with it at the moment is that subprocesses don't work on Windows because non-blocking streams aren't supported by GObject's IOChannels (on Windows only, I think?). I don't know if this is something that better integration could help with, but more people working on the problem would help. I've contemplated just doing the release without subprocess support on Windows until I can figure out a workaround. Also @gbtami, which unresolved issues are you referencing? The subprocess one? Also also, if people want development to be a bit faster, they could help me out - I'm just one person working on this project for fun when I have some spare time, getting more people in who actually use it would be great. |
@nathan-hoad yes #24 |
That's great to hear! I'll try to add an asyncio page to the PyGObject website which points to gbulb One long term issue, if we ever try to move some of the code into PyGObject |
By the way, it will be great to add these two helper functions, though they are really simple. PyGObject lacks some sweet syntax sugars 😆 def connect_async(self, detailed_signal, handler_async, *args):
def handler(self, *args):
asyncio.ensure_future(handler_async(self, *args))
self.connect(detailed_signal, handler, *args)
GObject.GObject.connect_async = connect_async
def wrap_asyncio(target, method, *, priority=False):
async_begin = getattr(target, method + '_async')
async_finish = getattr(target, method + '_finish')
def wrapper(self, *args):
def callback(self, result, future):
future.set_result(async_finish(self, result))
future = asyncio.get_event_loop().create_future()
if priority:
async_begin(self, *args, GLib.PRIORITY_DEFAULT, None, callback, future)
else:
async_begin(self, *args, None, callback, future)
return future
setattr(target, method + '_asyncio', wrapper) An example usage will be #!/usr/bin/env python3
import gi # NOQA: E402
gi.require_versions({
'Gtk': '3.0',
'Soup': '2.4'
}) # NOQA: E402
import sys
import asyncio
import gbulb
from gi.repository import Gtk, Gio, Soup, GLib, GObject
def connect_async(self, detailed_signal, handler_async, *args):
def handler(self, *args):
asyncio.ensure_future(handler_async(self, *args))
self.connect(detailed_signal, handler, *args)
GObject.GObject.connect_async = connect_async
def wrap_asyncio(target, method, *, priority=False):
async_begin = getattr(target, method + '_async')
async_finish = getattr(target, method + '_finish')
def wrapper(self, *args):
def callback(self, result, future):
future.set_result(async_finish(self, result))
future = asyncio.get_event_loop().create_future()
if priority:
async_begin(self, *args, GLib.PRIORITY_DEFAULT, None, callback, future)
else:
async_begin(self, *args, None, callback, future)
return future
setattr(target, method + '_asyncio', wrapper)
wrap_asyncio(Soup.Request, 'send')
wrap_asyncio(Gio.InputStream, 'read_bytes', priority=True)
wrap_asyncio(Gio.InputStream, 'close', priority=True)
class Window(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, title='Async Window', **kwargs)
self.connect_async('realize', self.on_realize)
async def on_realize(self, *args, **kwargs):
button = Gtk.Button(label="Get")
button.connect_async("clicked", self.on_button_clicked)
entry = Gtk.Entry()
entry.set_text('https://httpbin.org/get')
text_view = Gtk.TextView()
grid = Gtk.Grid()
grid.attach(button, 1, 0, 1, 1)
grid.attach(entry, 0, 0, 1, 1)
grid.attach(text_view, 0, 1, 2, 1)
self.add(grid)
self.show_all()
self.entry = entry
self.text_view = text_view
async def on_button_clicked(self, widget):
session = Soup.Session()
uri = Soup.URI.new(self.entry.get_text())
request = session.request_http_uri('GET', uri)
stream = await request.send_asyncio()
data = await stream.read_bytes_asyncio(4096)
self.text_view.get_buffer().set_text(data.get_data().decode())
await stream.close_asyncio()
class Application(Gtk.Application):
window = None
def __init__(self, *args, **kwargs):
super().__init__(*args, application_id=None, **kwargs)
def do_activate(self):
if not self.window:
self.window = Window(application=self)
self.window.present()
if __name__ == '__main__':
gbulb.install(gtk=True)
asyncio.get_event_loop().run_forever(Application(), sys.argv) It is really fascinating to run every line of code inside just one thread, including networking and GUI 🎉 🎉 |
Hello, I wanted to update everyone who's been involved in gbulb and I figured this was the best place to do it. Due to personal reasons and goings on in my life, I'm not able to give gbulb the attention it deserves. If people are interested in taking ownership and maintaining it, please discuss on here so we can come to an agreement, and I'll transfer ownership. |
👍 I think that |
The SelectorEventLoop is by far the most common event loop. Nearly all of the documentation relates to it. |
Oh well, I meant that symbols starts with |
Soo, I randomly wondered about this again and played a bit with it over the weekend. I think one of the major things we want here, is to turn allow GLib async functions to be run using asyncio using a nice syntax, my idea for that is the following:
Also, I guess we need to get GBulb into a shape where it is mergeable into pygobject. For fun, I hacked up glib-asyncio to dispatch from the GLib mainloop. It is kind of neat as it is simple, but I suspect it is not portable (due to socket FDs not being pollable by GLib without wrapping them in a |
@lazka already asked earlier, a clarification of the gbulb license would be helpful. Without that one might need to start from scratch when trying to integrate it into pygobject. EDIT: Ohh, looks like there is an Apache license file now. But it looks to me like that is not compatible with LGPL-2.1+. |
Somewhat tangential: I used to use gbulb, but, well, unmaintained and all, so I set out to redo from scratch, with a lazy man's approach of modifying existing loop implementations as little as necessary. It's very basic, but works for my application (an MPD client, mixing Gtk user interface and async socket communication with the mpd server). And can theoretically be improved for other use cases. |
@freakboy3742, in case you have not seen it. Some time ago I worked on adding asyncio support for pygobject itself (i.e. Gio async routines). See https://gitlab.gnome.org/GNOME/pygobject/-/merge_requests/158 My plan was to hack up a thin That said, GBulb seem really neat feature wise in other regards. So maybe that is the better solution in the end, especially if someone is interested in maintaining it. Anyway, if you are interested, maybe we should sync up a bit on what we can do. I should be able to spend some time on it, feel free to ping me on IRC (my nick is benzea on various networks, best is probably on GIMPnet in #python). Note that I am not a maintainer though, and so far it seemed to me that the interest in merging all this is pretty low. |
After the originally referenced issue has been migrated to pygobject's new issue 146, with the pygobject activity focusing on MR 189. |
@chrysn Thanks for the heads up. FWIW, I'd vastly prefer to use functionality baked into PyGObject. I maintain this package out of necessity, not out of any deep interest in maintaining asyncio support in GTK. If PyGObject gains enough baked-in support for asyncio loop integration to meet my needs for Toga, I'd deprecate this project in a heartbeat. |
This issue should be obsolete with MR 189 merged as an experimental feature. Please note the following that this feature is experimental, so try it but don't rely on it just yet. More specifically:
|
Closing on the basis that PyGObject now has native asyncio support in a public release. |
On the PyGObject IRC channel there is regular talk on how to best integrate with asyncio and if PyGObject should provide better integration out of the box.
What are your thoughts on integrating gblub into PyGObject?
(related bug report: https://bugzilla.gnome.org/show_bug.cgi?id=791591)
The text was updated successfully, but these errors were encountered: