Mercurial > libervia-backend
annotate frontends/src/jp/base.py @ 819:9e3641ea648f
core: fixed UnknownGroupError exception in getJidsFromGroup
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 13 Feb 2014 18:50:41 +0100 |
parents | c39117d00f35 |
children | 300b4de701a6 |
rev | line source |
---|---|
0 | 1 #! /usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
609
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
4 # jp: a SAT command line tool |
811 | 5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org) |
0 | 6 |
609
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
7 # This program is free software: you can redistribute it and/or modify |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
8 # it under the terms of the GNU Affero General Public License as published by |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
9 # the Free Software Foundation, either version 3 of the License, or |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
10 # (at your option) any later version. |
0 | 11 |
609
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
12 # This program is distributed in the hope that it will be useful, |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
15 # GNU Affero General Public License for more details. |
0 | 16 |
609
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
17 # You should have received a copy of the GNU Affero General Public License |
84a6e83157c2
fixed licences in docstrings (they are now in comments)
Goffi <goffi@goffi.org>
parents:
601
diff
changeset
|
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
0 | 19 |
771 | 20 from sat.core.i18n import _ |
402
f03688bdb858
jp: use with statement to open fifo
Goffi <goffi@goffi.org>
parents:
401
diff
changeset
|
21 |
0 | 22 global pbar_available |
23 pbar_available = True #checked before using ProgressBar | |
24 | |
25 ### logging ### | |
26 import logging | |
27 from logging import debug, info, error, warning | |
28 logging.basicConfig(level=logging.DEBUG, | |
29 format='%(message)s') | |
30 ### | |
31 | |
32 import sys | |
817 | 33 import locale |
34 import os.path | |
35 import argparse | |
36 import gobject | |
37 from glob import iglob | |
38 from importlib import import_module | |
236 | 39 from sat.tools.jid import JID |
627
d207c2186519
core, bridge, jp, quick_frontend: SàT stop more gracefully if bridge can't be initialised:
Goffi <goffi@goffi.org>
parents:
613
diff
changeset
|
40 from sat_frontends.bridge.DBus import DBusBridgeFrontend |
817 | 41 from sat.core import exceptions |
42 import sat_frontends.jp | |
0 | 43 try: |
817 | 44 import progressbar |
45 except ImportError: | |
46 info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar')) | |
47 info (_('Progress bar deactivated\n--\n')) | |
48 progressbar=None | |
49 | |
50 #consts | |
51 prog_name = u"jp" | |
52 description = """This software is a command line tool for XMPP. | |
53 Get the latest version at http://sat.goffi.org""" | |
54 | |
55 copyleft = u"""Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (aka Goffi) | |
56 This program comes with ABSOLUTELY NO WARRANTY; | |
57 This is free software, and you are welcome to redistribute it under certain conditions. | |
58 """ | |
0 | 59 |
60 | |
817 | 61 def unicode_decoder(arg): |
62 # Needed to have unicode strings from arguments | |
63 return arg.decode(locale.getpreferredencoding()) | |
814 | 64 |
0 | 65 |
817 | 66 class Jp(object): |
814 | 67 """ |
68 This class can be use to establish a connection with the | |
69 bridge. Moreover, it should manage a main loop. | |
70 | |
71 To use it, you mainly have to redefine the method run to perform | |
72 specify what kind of operation you want to perform. | |
73 | |
74 """ | |
817 | 75 def __init__(self): |
165
8a2053de6f8c
Frontends: management of unlaunched SàT Backend (information message and exit)
Goffi <goffi@goffi.org>
parents:
156
diff
changeset
|
76 try: |
817 | 77 self.bridge = DBusBridgeFrontend() |
78 except exceptions.BridgeExceptionNoService: | |
165
8a2053de6f8c
Frontends: management of unlaunched SàT Backend (information message and exit)
Goffi <goffi@goffi.org>
parents:
156
diff
changeset
|
79 print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) |
627
d207c2186519
core, bridge, jp, quick_frontend: SàT stop more gracefully if bridge can't be initialised:
Goffi <goffi@goffi.org>
parents:
613
diff
changeset
|
80 sys.exit(1) |
817 | 81 except excpetions.BridgeInitError: |
627
d207c2186519
core, bridge, jp, quick_frontend: SàT stop more gracefully if bridge can't be initialised:
Goffi <goffi@goffi.org>
parents:
613
diff
changeset
|
82 print(_(u"Can't init bridge")) |
165
8a2053de6f8c
Frontends: management of unlaunched SàT Backend (information message and exit)
Goffi <goffi@goffi.org>
parents:
156
diff
changeset
|
83 sys.exit(1) |
814 | 84 |
817 | 85 self.parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, |
86 description=description) | |
87 | |
88 self._make_parents() | |
89 self.add_parser_options() | |
90 self.subparsers = self.parser.add_subparsers(title=_('Available commands'), dest='subparser_name') | |
91 self._auto_loop = False # when loop is used for internal reasons | |
92 self.need_loop = False # to set by commands when loop is needed | |
93 self._progress_id = None # TODO: manage several progress ids | |
94 self.quit_on_progress_end = True # set to False if you manage yourself exiting, or if you want the user to stop by himself | |
95 | |
96 @property | |
97 def version(self): | |
98 return self.bridge.getVersion() | |
814 | 99 |
817 | 100 @property |
101 def progress_id(self): | |
102 return self._progress_id | |
103 | |
104 @progress_id.setter | |
105 def progress_id(self, value): | |
106 self._progress_id = value | |
107 | |
108 def _make_parents(self): | |
109 self.parents = {} | |
110 | |
111 profile_parent = self.parents['profile'] = argparse.ArgumentParser(add_help=False) | |
112 profile_parent.add_argument("-p", "--profile", action="store", type=str, default='@DEFAULT@', help=_("Use PROFILE profile key (default: %(default)s)")) | |
113 profile_parent.add_argument("-c", "--connect", action="store_true", help=_("Connect the profile before doing anything else")) | |
114 | |
115 progress_parent = self.parents['progress'] = argparse.ArgumentParser(add_help=False) | |
116 if progressbar: | |
117 progress_parent.add_argument("-g", "--progress", action="store_true", help=_("Show progress bar")) | |
0 | 118 |
817 | 119 def add_parser_options(self): |
120 self.parser.add_argument('--version', action='version', version=("%(name)s %(version)s %(copyleft)s" % {'name': prog_name, 'version': self.version, 'copyleft': copyleft})) | |
121 | |
122 def import_commands(self): | |
123 """ Automaticaly import commands to jp | |
124 looks from modules names cmd_*.py in jp path and import them | |
125 | |
126 """ | |
127 path = os.path.dirname(sat_frontends.jp.__file__) | |
128 modules = (os.path.splitext(module)[0] for module in map(os.path.basename, iglob(os.path.join(path, "cmd_*.py")))) | |
129 for module_name in modules: | |
130 module = import_module("sat_frontends.jp."+module_name) | |
131 try: | |
132 self.import_command_module(module) | |
133 except ImportError: | |
134 continue | |
0 | 135 |
817 | 136 def import_command_module(self, module): |
137 """ Add commands from a module to jp | |
138 @param module: module containing commands | |
139 | |
140 """ | |
141 try: | |
142 for classname in module.__commands__: | |
143 cls = getattr(module, classname) | |
144 except AttributeError: | |
145 warning(_("Invalid module %s") % module) | |
146 raise ImportError | |
147 cls(self) | |
148 | |
149 | |
150 def run(self, args=None): | |
151 self.args = self.parser.parse_args(args) | |
152 self.args.func() | |
153 if self.need_loop or self._auto_loop: | |
154 self._start_loop() | |
155 | |
156 def _start_loop(self): | |
814 | 157 self.loop = gobject.MainLoop() |
158 try: | |
159 self.loop.run() | |
160 except KeyboardInterrupt: | |
161 info(_("User interruption: good bye")) | |
0 | 162 |
817 | 163 def stop_loop(self): |
164 try: | |
165 self.loop.quit() | |
166 except AttributeError: | |
167 pass | |
587
952322b1d490
Remove trailing whitespaces.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
572
diff
changeset
|
168 |
817 | 169 def quit(self, errcode=0): |
170 self.stop_loop() | |
171 if errcode: | |
172 sys.exit(errcode) | |
587
952322b1d490
Remove trailing whitespaces.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
572
diff
changeset
|
173 |
814 | 174 def check_jids(self, jids): |
175 """Check jids validity, transform roster name to corresponding jids | |
110
cb904fa7de3c
jp: profile management (new option: --profile)
Goffi <goffi@goffi.org>
parents:
70
diff
changeset
|
176 |
817 | 177 @param profile: profile name |
178 @param jids: list of jids | |
179 @return: List of jids | |
180 | |
814 | 181 """ |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
182 names2jid = {} |
493
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
183 nodes2jid = {} |
393 | 184 |
814 | 185 for contact in self.bridge.getContacts(self.profile): |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
186 _jid, attr, groups = contact |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
187 if attr.has_key("name"): |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
188 names2jid[attr["name"].lower()] = _jid |
493
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
189 nodes2jid[JID(_jid).node.lower()] = _jid |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
190 |
817 | 191 def expand_jid(jid): |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
192 _jid = jid.lower() |
493
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
193 if _jid in names2jid: |
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
194 expanded = names2jid[_jid] |
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
195 elif _jid in nodes2jid: |
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
196 expanded = nodes2jid[_jid] |
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
197 else: |
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
198 expanded = jid |
b7c4bb2c0668
jp: - better expandJid: roster's jids' nodes are used after names to expand jid
Goffi <goffi@goffi.org>
parents:
480
diff
changeset
|
199 return unicode(expanded) |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
200 |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
201 def check(jid): |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
202 if not jid.is_valid: |
814 | 203 error (_("%s is not a valid JID !"), jid) |
817 | 204 self.quit(1) |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
205 |
814 | 206 dest_jids=[] |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
207 try: |
814 | 208 for i in range(len(jids)): |
817 | 209 dest_jids.append(expand_jid(jids[i])) |
814 | 210 check(dest_jids[i]) |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
211 except AttributeError: |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
212 pass |
0 | 213 |
814 | 214 return dest_jids |
0 | 215 |
817 | 216 def connect_profile(self, callback): |
217 """ Check if the profile is connected | |
218 @param callback: method to call when profile is connected | |
219 @exit: - 1 when profile is not connected and --connect is not set | |
220 - 1 when the profile doesn't exists | |
221 - 1 when there is a connection error | |
222 """ | |
223 # FIXME: need better exit codes | |
224 | |
225 def cant_connect(): | |
814 | 226 error(_(u"Can't connect profile")) |
817 | 227 self.quit(1) |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
228 |
817 | 229 self.profile = self.bridge.getProfileName(self.args.profile) |
0 | 230 |
817 | 231 if not self.profile: |
232 error(_("The profile [%s] doesn't exist") % self.args.profile) | |
233 self.quit(1) | |
234 | |
235 if self.args.connect: #if connection is asked, we connect the profile | |
236 self.bridge.asyncConnect(self.profile, callback, cant_connect) | |
237 self._auto_loop = True | |
814 | 238 return |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
239 |
817 | 240 elif not self.bridge.isConnected(self.profile): |
241 error(_(u"Profile [%(profile)s] is not connected, please connect it before using jp, or use --connect option") % { "profile": self.profile }) | |
242 self.quit(1) | |
0 | 243 |
817 | 244 callback() |
245 | |
246 def get_full_jid(self, param_jid): | |
247 """Return the full jid if possible (add last resource when find a bare jid)""" | |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
248 _jid = JID(param_jid) |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
249 if not _jid.resource: |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
250 #if the resource is not given, we try to add the last known resource |
817 | 251 last_resource = self.bridge.getLastResource(param_jid, self.profile) |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
252 if last_resource: |
688
f7878ad3c846
tools: renamed tools.jid.JID attribute "short" to "bare"
souliane <souliane@mailoo.org>
parents:
657
diff
changeset
|
253 return "%s/%s" % (_jid.bare, last_resource) |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
254 return param_jid |
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
255 |
817 | 256 def watch_progress(self): |
257 self.pbar = None | |
258 gobject.timeout_add(10, self._progress_cb) | |
0 | 259 |
817 | 260 def _progress_cb(self): |
261 if self.progress_id: | |
262 data = self.bridge.getProgress(self.progress_id, self.profile) | |
263 if data: | |
264 if not data['position']: | |
265 data['position'] = '0' | |
266 if not self.pbar: | |
267 #first answer, we must construct the bar | |
268 self.pbar = progressbar.ProgressBar(int(data['size']), | |
269 [_("Progress: "),progressbar.Percentage(), | |
270 " ", | |
271 progressbar.Bar(), | |
272 " ", | |
273 progressbar.FileTransferSpeed(), | |
274 " ", | |
275 progressbar.ETA()]) | |
276 self.pbar.start() | |
277 | |
278 self.pbar.update(int(data['position'])) | |
279 | |
280 elif self.pbar: | |
281 self.pbar.finish() | |
282 if self.quit_on_progress_end: | |
283 self.quit() | |
284 return False | |
285 | |
286 return True | |
814 | 287 |
288 | |
817 | 289 class CommandBase(object): |
814 | 290 |
817 | 291 def __init__(self, host, name, use_profile=True, use_progress=False, help=None, **kwargs): |
292 """ Initialise CommandBase | |
293 @param host: Jp instance | |
294 @param name: name of the new command | |
295 @param use_profile: if True, add profile selection/connection commands | |
296 @param use_progress: if True, add progress bar activation commands | |
297 @param help: help message to display | |
298 @param **kwargs: args passed to ArgumentParser | |
299 | |
814 | 300 """ |
817 | 301 try: # If we have subcommands, host is a CommandBase and we need to use host.host |
302 self.host = host.host | |
303 except AttributeError: | |
304 self.host = host | |
305 | |
306 parents = kwargs.setdefault('parents', set()) | |
307 if use_profile: | |
308 #self.host.parents['profile'] is an ArgumentParser with profile connection arguments | |
309 parents.add(self.host.parents['profile']) | |
310 if use_progress: | |
311 parents.add(self.host.parents['progress']) | |
312 | |
313 self.parser = host.subparsers.add_parser(name, help=help, **kwargs) | |
314 if hasattr(self, "subcommands"): | |
315 self.subparsers = self.parser.add_subparsers() | |
316 else: | |
317 self.parser.set_defaults(func=self.run) | |
318 self.add_parser_options() | |
319 | |
320 @property | |
321 def args(self): | |
322 return self.host.args | |
323 | |
324 @property | |
325 def need_loop(self): | |
326 return self.host.need_loop | |
327 | |
328 @need_loop.setter | |
329 def need_loop(self, value): | |
330 self.host.need_loop = value | |
331 | |
332 @property | |
333 def profile(self): | |
334 return self.host.profile | |
335 | |
336 @property | |
337 def progress_id(self): | |
338 return self.host.progress_id | |
814 | 339 |
817 | 340 @progress_id.setter |
341 def progress_id(self, value): | |
342 self.host.progress_id = value | |
343 | |
344 def add_parser_options(self): | |
345 try: | |
346 subcommands = self.subcommands | |
347 except AttributeError: | |
348 # We don't have subcommands, the class need to implements add_parser_options | |
349 raise NotImplementedError | |
350 | |
351 # now we add subcommands to ourself | |
352 for cls in subcommands: | |
353 cls(self) | |
814 | 354 |
817 | 355 def run(self): |
356 try: | |
357 if self.args.profile: | |
358 self.host.connect_profile(self.connected) | |
359 except AttributeError: | |
360 # the command doesn't need to connect profile | |
361 pass | |
362 try: | |
363 if self.args.progress: | |
364 self.host.watch_progress() | |
365 except AttributeError: | |
366 # the command doesn't use progress bar | |
367 pass | |
368 | |
369 def connected(self): | |
370 if not self.need_loop: | |
371 self.host.stop_loop() | |
372 | |
373 | |
374 class CommandAnswering(CommandBase): | |
375 #FIXME: temp, will be refactored when progress_bar/confirmations will be refactored | |
376 | |
377 def _ask_confirmation(self, confirm_id, confirm_type, data, profile): | |
378 """ Callback used for file transfer, accept files depending on parameters""" | |
538
2c4016921403
core, frontends, bridgen plugins: fixed methods which were unproperly managing multi-profiles
Goffi <goffi@goffi.org>
parents:
493
diff
changeset
|
379 if profile != self.profile: |
2c4016921403
core, frontends, bridgen plugins: fixed methods which were unproperly managing multi-profiles
Goffi <goffi@goffi.org>
parents:
493
diff
changeset
|
380 debug("Ask confirmation ignored: not our profile") |
2c4016921403
core, frontends, bridgen plugins: fixed methods which were unproperly managing multi-profiles
Goffi <goffi@goffi.org>
parents:
493
diff
changeset
|
381 return |
817 | 382 if confirm_type == self.confirm_type: |
383 if self.dest_jids and not JID(data['from']).bare in [JID(_jid).bare for _jid in self.dest_jids()]: | |
0 | 384 return #file is not sent by a filtered jid |
385 else: | |
817 | 386 self.ask(data, confirm_id) |
0 | 387 |
814 | 388 def ask(self): |
389 """ | |
390 The return value is used to answer to the bridge. | |
817 | 391 @return: bool or dict |
814 | 392 """ |
393 raise NotImplementedError | |
587
952322b1d490
Remove trailing whitespaces.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
572
diff
changeset
|
394 |
817 | 395 def connected(self): |
401
b2caa2615c4c
jp roster name manegement + Pipe transfer
Goffi <goffi@goffi.org>
parents:
393
diff
changeset
|
396 """Auto reply to confirmations requests""" |
817 | 397 self.need_loop = True |
398 super(CommandAnswering, self).connected() | |
399 # we watch confirmation signals | |
400 self.host.bridge.register("ask_confirmation", self._ask_confirmation) | |
587
952322b1d490
Remove trailing whitespaces.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
572
diff
changeset
|
401 |
542
3eeb6c865e4d
frontends: incoming files transfer management:
Goffi <goffi@goffi.org>
parents:
538
diff
changeset
|
402 #and we ask those we have missed |
817 | 403 for confirm_id, confirm_type, data in self.host.bridge.getWaitingConf(self.profile): |
404 self._ask_confirmation(confirm_id, confirm_type, data, self.profile) |