annotate sat_frontends/jp/loops.py @ 3254:6cf4bd6972c2

core, frontends: avatar refactoring: /!\ huge commit Avatar logic has been reworked around the IDENTITY plugin: plugins able to handle avatar or other identity related metadata (like nicknames) register to IDENTITY plugin in the same way as for other features like download/upload. Once registered, IDENTITY plugin will call them when suitable in order of priority, and handle caching. Methods to manage those metadata from frontend now use serialised data. For now `avatar` and `nicknames` are handled: - `avatar` is now a dict with `path` + metadata like `media_type`, instead of just a string path - `nicknames` is now a list of nicknames in order of priority. This list is never empty, and `nicknames[0]` should be the preferred nickname to use by frontends in most cases. In addition to contact specified nicknames, user set nickname (the one set in roster) is used in priority when available. Among the side changes done with this commit, there are: - a new `contactGet` bridge method to get roster metadata for a single contact - SatPresenceProtocol.send returns a Deferred to check when it has actually been sent - memory's methods to handle entities data now use `client` as first argument - metadata filter can be specified with `getIdentity` - `getAvatar` and `setAvatar` are now part of the IDENTITY plugin instead of XEP-0054 (and there signature has changed) - `isRoom` and `getBareOrFull` are now part of XEP-0045 plugin - jp avatar/get command uses `xdg-open` first when available for `--show` flag - `--no-cache` has been added to jp avatar/get and identity/get - jp identity/set has been simplified, explicit options (`--nickname` only for now) are used instead of `--field`. `--field` may come back in the future if necessary for extra data. - QuickContactList `SetContact` now handle None as a value, and doesn't use it to delete the metadata anymore - improved cache handling for `metadata` and `nicknames` in quick frontend - new `default` argument in QuickContactList `getCache`
author Goffi <goffi@goffi.org>
date Tue, 14 Apr 2020 21:00:33 +0200
parents 9d0df638c8b4
children be6d91572633
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
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
3 # jp: a SAT command line tool
3136
9d0df638c8b4 dates update
Goffi <goffi@goffi.org>
parents: 3043
diff changeset
4 # Copyright (C) 2009-2020 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
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
22 from sat.core.i18n import _
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 from sat_frontends.jp.constants import Const as C
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
24
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 log.basicConfig(level=log.WARNING,
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 format='[%(name)s] %(message)s')
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
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
38 def getJPLoop(bridge_name):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 if 'dbus' in bridge_name:
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 import threading
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 from gi.repository import GLib
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
43
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
44 class JPLoop:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
45
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
46 def run(self, jp, args, namespace):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
47 signal.signal(signal.SIGINT, self._on_sigint)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
48 self._glib_loop = GLib.MainLoop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
49 threading.Thread(target=self._glib_loop.run).start()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
50 loop = asyncio.get_event_loop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 loop.run_until_complete(jp.main(args=args, namespace=namespace))
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 self._glib_loop.quit()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 sys.exit(exit_code)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
59
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 def call_later(self, delay, callback, *args):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 """call a callback repeatedly
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
62
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 @param delay(int): delay between calls in s
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 @param callback(callable): method to call
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 if the callback return True, the call will continue
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 else the calls will stop
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 @param *args: args of the callbac
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
68 """
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 loop = asyncio.get_event_loop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
70 loop.call_later(delay, callback, *args)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
71
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 def _on_sigint(self, sig_number, stack_frame):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 """Called on keyboard interruption
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
74
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
75 Print user interruption message, set exit code and stop reactor
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
76 """
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 print("\r" + USER_INTER_MSG)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
78 self.quit(C.EXIT_USER_CANCELLED)
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
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
82 asyncioreactor.install()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 from twisted.internet import reactor, defer
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
84
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 class JPLoop:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
86
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 def __init__(self):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 # 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
89 # something got wrong and we must report it
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 self._exit_code = C.EXIT_INTERNAL_ERROR
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
91
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
92 def run(self, jp, *args):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 self.jp = jp
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 signal.signal(signal.SIGINT, self._on_sigint)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 defer.ensureDeferred(self._start(jp, *args))
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 try:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
97 reactor.run(installSignalHandlers=False)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
98 except SystemExit as e:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 self._exit_code = e.code
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
100 sys.exit(self._exit_code)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
101
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
102 async def _start(self, jp, *args):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 fut = asyncio.ensure_future(jp.main(*args))
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 try:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 await defer.Deferred.fromFuture(fut)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 except BaseException:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 import traceback
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 traceback.print_exc()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
109 jp.quit(1)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
110
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
111 def quit(self, exit_code):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
112 self._exit_code = exit_code
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 reactor.stop()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
114
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
115 def _timeout_cb(self, args, callback, delay):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
116 try:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
117 ret = callback(*args)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
118 # FIXME: temporary hack to avoid traceback when using XMLUI
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
119 # to be removed once create_task is not used anymore in
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
120 # xmlui_manager (i.e. once sat_frontends.tools.xmlui fully supports
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
121 # async syntax)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
122 except QuitException:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
123 return
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
124 if ret:
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
125 reactor.callLater(delay, self._timeout_cb, args, callback, delay)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
126
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
127 def call_later(self, delay, callback, *args):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
128 reactor.callLater(delay, self._timeout_cb, args, callback, delay)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
129
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
130 def _on_sigint(self, sig_number, stack_frame):
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 """Called on keyboard interruption
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
132
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 Print user interruption message, set exit code and stop reactor
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("\r" + USER_INTER_MSG)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 self._exit_code = C.EXIT_USER_CANCELLED
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 reactor.callFromThread(reactor.stop)
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
138
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
139
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
140 if bridge_name == "embedded":
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 raise NotImplementedError
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 # from sat.core import sat_main
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
143 # sat = sat_main.SAT()
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
144
3df611adb598 jp: handle dbus bridge with asyncio:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 return JPLoop