Skip to content

Commit f80500c

Browse files
authored
Experimental async implementation (#401)
2 parents 4a69633 + 6761195 commit f80500c

20 files changed

+1577
-1547
lines changed

dialect/asyncio.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import asyncio
2+
import contextlib
3+
import functools
4+
from typing import Callable, Coroutine
5+
6+
from gi.events import GLibEventLoopPolicy
7+
8+
9+
@contextlib.contextmanager
10+
def glib_event_loop_policy():
11+
original = asyncio.get_event_loop_policy()
12+
policy = GLibEventLoopPolicy()
13+
asyncio.set_event_loop_policy(policy)
14+
try:
15+
yield policy
16+
finally:
17+
asyncio.set_event_loop_policy(original)
18+
19+
20+
_background_tasks: set[asyncio.Task] = set()
21+
22+
23+
def create_background_task(coro: Coroutine) -> asyncio.Task:
24+
"""
25+
Create and track a task.
26+
27+
Normally tasks are weak-referenced by asyncio.
28+
We keep track of them, so they can be completed before GC kicks in.
29+
"""
30+
task = asyncio.create_task(coro)
31+
_background_tasks.add(task)
32+
task.add_done_callback(_background_tasks.discard)
33+
return task
34+
35+
36+
def background_task(f: Callable[..., Coroutine]):
37+
"""
38+
Wraps an async function to be run using ``create_background_task``.
39+
40+
Useful to use async functions like signal handlers or GTK template callbacks.
41+
42+
Note: The return value will be lost, so this is not suitable when you need to
43+
return something from the coroutine, what might be needed in some signal handlers.
44+
"""
45+
46+
@functools.wraps(f)
47+
def decor(*args, **kwargs):
48+
create_background_task(f(*args, **kwargs))
49+
50+
return decor

dialect/main.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
except ImportError or ValueError:
2222
logging.error("Error: GObject dependencies not met.")
2323

24+
from dialect.asyncio import glib_event_loop_policy
2425
from dialect.define import APP_ID, RES_PATH, VERSION
2526
from dialect.preferences import DialectPreferencesDialog
2627
from dialect.settings import Settings
@@ -168,10 +169,7 @@ def _on_pronunciation(self, action: Gio.SimpleAction, value: GLib.Variant):
168169

169170
# Update UI
170171
if self.window:
171-
if self.window.trans_src_pron is not None:
172-
self.window.src_pron_revealer.props.reveal_child = value # type: ignore
173-
if self.window.trans_dest_pron is not None:
174-
self.window.dest_pron_revealer.props.reveal_child = value # type: ignore
172+
self.window._check_pronunciation()
175173

176174
def _on_preferences(self, _action, _param):
177175
"""Show preferences window"""
@@ -198,4 +196,9 @@ def _on_quit(self, _action, _param):
198196
def main():
199197
# Run the Application
200198
app = Dialect()
201-
return app.run(sys.argv)
199+
exit_code = 0
200+
201+
with glib_event_loop_policy():
202+
exit_code = app.run(sys.argv)
203+
204+
return exit_code

dialect/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ subdir('search_provider')
5959
# Python sources
6060
sources = [
6161
'__init__.py',
62+
'asyncio.py',
6263
'languages.py',
6364
'main.py',
6465
'preferences.py',

dialect/providers/__init__.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,22 @@
1212
from dialect.providers.base import ( # noqa
1313
BaseProvider,
1414
ProviderCapability,
15-
ProviderError,
16-
ProviderErrorCode,
1715
ProviderFeature,
16+
Translation,
17+
TranslationMistake,
18+
TranslationPronunciation,
19+
TranslationRequest,
20+
)
21+
from dialect.providers.errors import ( # noqa
22+
APIKeyInvalid,
23+
APIKeyRequired,
24+
BatchSizeExceeded,
25+
CharactersLimitExceeded,
26+
InvalidLangCode,
27+
ProviderError,
28+
RequestError,
29+
ServiceLimitReached,
30+
UnexpectedError,
1831
)
1932

2033
MODULES: dict[str, type[BaseProvider]] = {}

0 commit comments

Comments
 (0)