Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 4 additions & 32 deletions dialect/asyncio.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,12 @@
import asyncio
import contextlib
import functools
from typing import Callable, Coroutine

from gi.events import GLibEventLoopPolicy


@contextlib.contextmanager
def glib_event_loop_policy():
original = asyncio.get_event_loop_policy()
policy = GLibEventLoopPolicy()
asyncio.set_event_loop_policy(policy)
try:
yield policy
finally:
asyncio.set_event_loop_policy(original)


_background_tasks: set[asyncio.Task] = set()


def create_background_task(coro: Coroutine) -> asyncio.Task:
"""
Create and track a task.

Normally tasks are weak-referenced by asyncio.
We keep track of them, so they can be completed before GC kicks in.
"""
task = asyncio.create_task(coro)
_background_tasks.add(task)
task.add_done_callback(_background_tasks.discard)
return task
from gi.repository import Gio


def background_task(f: Callable[..., Coroutine]):
"""
Wraps an async function to be run using ``create_background_task``.
Wraps an async function to be run using ``Gio.Application.create_asyncio_task``.

Useful to use async functions like signal handlers or GTK template callbacks.

Expand All @@ -45,6 +16,7 @@ def background_task(f: Callable[..., Coroutine]):

@functools.wraps(f)
def decor(*args, **kwargs):
create_background_task(f(*args, **kwargs))
app = Gio.Application.get_default()
app.create_asyncio_task(f(*args, **kwargs))

return decor
12 changes: 5 additions & 7 deletions dialect/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later

# Initial setup
import asyncio
import logging
import sys

import gi
from gi.events import GLibEventLoopPolicy

try:
gi.require_version("Gdk", "4.0")
Expand All @@ -23,7 +25,6 @@
except ImportError or ValueError:
logging.error("Error: GObject dependencies not met.")

from dialect.asyncio import glib_event_loop_policy
from dialect.define import APP_ID, RES_PATH, VERSION
from dialect.preferences import DialectPreferencesDialog
from dialect.settings import Settings
Expand Down Expand Up @@ -196,11 +197,8 @@ def _on_quit(self, _action, _param):


def main():
# Set the asyncio event loop policy from PyGObject
asyncio.set_event_loop_policy(GLibEventLoopPolicy())
# Run the Application
app = Dialect()
exit_code = 0

with glib_event_loop_policy():
exit_code = app.run(sys.argv)

return exit_code
return app.run(sys.argv)
17 changes: 7 additions & 10 deletions dialect/widgets/provider_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from gi.repository import Adw, GObject, Gtk

from dialect.asyncio import create_background_task
from dialect.asyncio import background_task
from dialect.define import RES_PATH
from dialect.providers import ProviderCapability, RequestError

Expand Down Expand Up @@ -74,8 +74,9 @@ def _check_settings(self):

self.api_usage_group.props.visible = False
if self.provider.supports_api_usage:
create_background_task(self._load_api_usage())
self._load_api_usage()

@background_task
async def _load_api_usage(self):
if not self.provider:
return
Expand All @@ -92,11 +93,9 @@ async def _load_api_usage(self):
logging.error(exc)

@Gtk.Template.Callback()
def _on_instance_apply(self, _row):
@background_task
async def _on_instance_apply(self, _row):
"""Called on self.instance_entry::apply signal"""
create_background_task(self._instance_apply())

async def _instance_apply(self):
if not self.provider:
return

Expand Down Expand Up @@ -160,11 +159,9 @@ def _on_reset_instance(self, _button):
self.instance_entry.props.text = self.provider.instance_url

@Gtk.Template.Callback()
def _on_api_key_apply(self, _row):
@background_task
async def _on_api_key_apply(self, _row):
"""Called on self.api_key_entry::apply signal"""
create_background_task(self._api_key_apply())

async def _api_key_apply(self):
if not self.provider:
return

Expand Down