Skip to content

Commit

Permalink
Experimental async implementation (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
mufeedali authored Oct 9, 2024
2 parents 4a69633 + 6761195 commit f80500c
Show file tree
Hide file tree
Showing 20 changed files with 1,577 additions and 1,547 deletions.
50 changes: 50 additions & 0 deletions dialect/asyncio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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


def background_task(f: Callable[..., Coroutine]):
"""
Wraps an async function to be run using ``create_background_task``.
Useful to use async functions like signal handlers or GTK template callbacks.
Note: The return value will be lost, so this is not suitable when you need to
return something from the coroutine, what might be needed in some signal handlers.
"""

@functools.wraps(f)
def decor(*args, **kwargs):
create_background_task(f(*args, **kwargs))

return decor
13 changes: 8 additions & 5 deletions dialect/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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 @@ -168,10 +169,7 @@ def _on_pronunciation(self, action: Gio.SimpleAction, value: GLib.Variant):

# Update UI
if self.window:
if self.window.trans_src_pron is not None:
self.window.src_pron_revealer.props.reveal_child = value # type: ignore
if self.window.trans_dest_pron is not None:
self.window.dest_pron_revealer.props.reveal_child = value # type: ignore
self.window._check_pronunciation()

def _on_preferences(self, _action, _param):
"""Show preferences window"""
Expand All @@ -198,4 +196,9 @@ def _on_quit(self, _action, _param):
def main():
# Run the Application
app = Dialect()
return app.run(sys.argv)
exit_code = 0

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

return exit_code
1 change: 1 addition & 0 deletions dialect/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ subdir('search_provider')
# Python sources
sources = [
'__init__.py',
'asyncio.py',
'languages.py',
'main.py',
'preferences.py',
Expand Down
17 changes: 15 additions & 2 deletions dialect/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,22 @@
from dialect.providers.base import ( # noqa
BaseProvider,
ProviderCapability,
ProviderError,
ProviderErrorCode,
ProviderFeature,
Translation,
TranslationMistake,
TranslationPronunciation,
TranslationRequest,
)
from dialect.providers.errors import ( # noqa
APIKeyInvalid,
APIKeyRequired,
BatchSizeExceeded,
CharactersLimitExceeded,
InvalidLangCode,
ProviderError,
RequestError,
ServiceLimitReached,
UnexpectedError,
)

MODULES: dict[str, type[BaseProvider]] = {}
Expand Down
Loading

0 comments on commit f80500c

Please sign in to comment.