annotate libervia/cli/loops.py @ 4306:94e0968987cd

plugin XEP-0033: code modernisation, improve delivery, data validation: - Code has been rewritten using Pydantic models and `async` coroutines for data validation and cleaner element parsing/generation. - Delivery has been completely rewritten. It now works even if server doesn't support multicast, and send to local multicast service first. Delivering to local multicast service first is due to bad support of XEP-0033 in server (notably Prosody which has an incomplete implementation), and the current impossibility to detect if a sub-domain service handles fully multicast or only for local domains. This is a workaround to have a good balance between backward compatilibity and use of bandwith, and to make it work with the incoming email gateway implementation (the gateway will only deliver to entities of its own domain). - disco feature checking now uses `async` corountines. `host` implementation still use Deferred return values for compatibility with legacy code. rel 450
author Goffi <goffi@goffi.org>
date Thu, 26 Sep 2024 16:12:01 +0200
parents 0d7bb4df2343
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/env python3
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
2
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
3 # Libervia CLI
3479
be6d91572633 date update
Goffi <goffi@goffi.org>
parents: 3136
diff changeset
4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
5
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 # This program is free software: you can redistribute it and/or modify
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # it under the terms of the GNU Affero General Public License as published by
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # the Free Software Foundation, either version 3 of the License, or
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # (at your option) any later version.
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 # This program is distributed in the hope that it will be useful,
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # GNU Affero General Public License for more details.
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
15
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 # You should have received a copy of the GNU Affero General Public License
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
18
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
19 import sys
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
20 import asyncio
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 import logging as log
4071
4b842c1fb686 refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents: 4037
diff changeset
22 from libervia.backend.core.i18n import _
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
23 from libervia.cli.constants import Const as C
4141
ba8ddfdd334f cli (loops): run GLib loop in same thread as asyncio:
Goffi <goffi@goffi.org>
parents: 4075
diff changeset
24 from libervia.frontends.tools import aio
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
25
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4206
diff changeset
26 log.basicConfig(level=log.WARNING, format="[%(name)s] %(message)s")
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
27
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
28 USER_INTER_MSG = _("User interruption: good bye")
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
29
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
30
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 class QuitException(BaseException):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 """Quitting is requested
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
33
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
34 This is used to stop execution when host.quit() is called
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
35 """
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
36
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
37
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
38 def get_libervia_cli_loop(bridge_name):
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4206
diff changeset
39 if "dbus" in bridge_name:
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
40 import signal
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
41
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
42 class LiberviaCLILoop:
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
43
4141
ba8ddfdd334f cli (loops): run GLib loop in same thread as asyncio:
Goffi <goffi@goffi.org>
parents: 4075
diff changeset
44 def __init__(self):
4206
0f8ea0768a3b cli (call): implement GUI output:
Goffi <goffi@goffi.org>
parents: 4141
diff changeset
45 self.loop = asyncio.get_event_loop()
4141
ba8ddfdd334f cli (loops): run GLib loop in same thread as asyncio:
Goffi <goffi@goffi.org>
parents: 4075
diff changeset
46
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
47 def run(self, libervia_cli, args, namespace):
4141
ba8ddfdd334f cli (loops): run GLib loop in same thread as asyncio:
Goffi <goffi@goffi.org>
parents: 4075
diff changeset
48 aio.install_glib_asyncio_iteration()
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
49 signal.signal(signal.SIGINT, self._on_sigint)
4141
ba8ddfdd334f cli (loops): run GLib loop in same thread as asyncio:
Goffi <goffi@goffi.org>
parents: 4075
diff changeset
50 loop = self.loop
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
51 loop.run_until_complete(libervia_cli.main(args=args, namespace=namespace))
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 loop.run_forever()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
53
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 def quit(self, exit_code):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
55 loop = asyncio.get_event_loop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 loop.stop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 sys.exit(exit_code)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
58
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 def call_later(self, delay, callback, *args):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 """call a callback repeatedly
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
61
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 @param delay(int): delay between calls in s
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 @param callback(callable): method to call
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 if the callback return True, the call will continue
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 else the calls will stop
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 @param *args: args of the callbac
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 """
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
68 loop = asyncio.get_event_loop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 loop.call_later(delay, callback, *args)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
70
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
71 def _on_sigint(self, sig_number, stack_frame):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 """Called on keyboard interruption
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
73
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
74 Print user interruption message, set exit code and stop reactor
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
75 """
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
76 print("\r" + USER_INTER_MSG)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 self.quit(C.EXIT_USER_CANCELLED)
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4206
diff changeset
78
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
79 else:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
80 import signal
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 from twisted.internet import asyncioreactor
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4206
diff changeset
82
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 asyncioreactor.install()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 from twisted.internet import reactor, defer
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
85
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
86 class LiberviaCLILoop:
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
87
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 def __init__(self):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 # exit code must be set when using quit, so if it's not set
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 # something got wrong and we must report it
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
91 self._exit_code = C.EXIT_INTERNAL_ERROR
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
92
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
93 def run(self, libervia_cli, *args):
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
94 self.libervia_cli = libervia_cli
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 signal.signal(signal.SIGINT, self._on_sigint)
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
96 defer.ensureDeferred(self._start(libervia_cli, *args))
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
97 try:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
98 reactor.run(installSignalHandlers=False)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 except SystemExit as e:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
100 self._exit_code = e.code
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 sys.exit(self._exit_code)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
102
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
103 async def _start(self, libervia_cli, *args):
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
104 fut = asyncio.ensure_future(libervia_cli.main(*args))
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 try:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 await defer.Deferred.fromFuture(fut)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 except BaseException:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 import traceback
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4206
diff changeset
109
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
110 traceback.print_exc()
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
111 libervia_cli.quit(1)
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
112
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 def quit(self, exit_code):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
114 self._exit_code = exit_code
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
115 reactor.stop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
116
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
117 def _timeout_cb(self, args, callback, delay):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
118 try:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
119 ret = callback(*args)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
120 # FIXME: temporary hack to avoid traceback when using XMLUI
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
121 # to be removed once create_task is not used anymore in
4074
26b7ed2817da refactoring: rename `sat_frontends` to `libervia.frontends`
Goffi <goffi@goffi.org>
parents: 4071
diff changeset
122 # xmlui_manager (i.e. once libervia.frontends.tools.xmlui fully supports
3043
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
123 # async syntax)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
124 except QuitException:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
125 return
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
126 if ret:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
127 reactor.callLater(delay, self._timeout_cb, args, callback, delay)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
128
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
129 def call_later(self, delay, callback, *args):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
130 reactor.callLater(delay, self._timeout_cb, args, callback, delay)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
131
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
132 def _on_sigint(self, sig_number, stack_frame):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 """Called on keyboard interruption
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
134
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
135 Print user interruption message, set exit code and stop reactor
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 """
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 print("\r" + USER_INTER_MSG)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 self._exit_code = C.EXIT_USER_CANCELLED
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 reactor.callFromThread(reactor.stop)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
140
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 if bridge_name == "embedded":
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 raise NotImplementedError
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
143 # from sat.core import sat_main
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
144 # sat = sat_main.SAT()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
145
4075
47401850dec6 refactoring: rename `libervia.frontends.jp` to `libervia.cli`
Goffi <goffi@goffi.org>
parents: 4074
diff changeset
146 return LiberviaCLILoop