correctly handle BrowserUnavailable and BrowserIncorrectPassword exceptions in repl applications (closes #302)

It asks user to reconfigure backend when BrowserIncorrectPassword is
raised.
This commit is contained in:
Romain Bignon 2010-09-30 11:59:12 +02:00
commit 6dfbda042c
4 changed files with 67 additions and 31 deletions

View file

@ -19,7 +19,6 @@ from __future__ import with_statement
import logging import logging
import weboob
from weboob.capabilities.bank import ICapBank from weboob.capabilities.bank import ICapBank
from weboob.tools.application.repl import ReplApplication from weboob.tools.application.repl import ReplApplication
@ -44,18 +43,11 @@ class Boobank(ReplApplication):
tot_balance = 0.0 tot_balance = 0.0
tot_coming = 0.0 tot_coming = 0.0
self.accounts = [] self.accounts = []
try:
for backend, account in self.do('iter_accounts'): for backend, account in self.do('iter_accounts'):
self.format(account) self.format(account)
tot_balance += account.balance tot_balance += account.balance
tot_coming += account.coming tot_coming += account.coming
self.accounts.append(account) self.accounts.append(account)
except weboob.core.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, weboob.tools.browser.BrowserIncorrectPassword):
logging.error(u'Error: Incorrect password for backend %s' % backend.name)
else:
logging.error(u'Error[%s]: %s\n%s' % (backend.name, error, backtrace))
else: else:
self.format((('label', 'Total'), self.format((('label', 'Total'),
('balance', tot_balance), ('balance', tot_balance),

View file

@ -34,7 +34,7 @@ class CallErrors(Exception):
def __init__(self, errors): def __init__(self, errors):
Exception.__init__(self, u'These errors have been raised in backend threads '\ Exception.__init__(self, u'These errors have been raised in backend threads '\
'(use --debug option to print backtraces):\n%s' % ( '(use --debug option to print backtraces):\n%s' % (
u'\n'.join((u' * %s: %s%s' % (backend, repr(error), backtrace.decode('utf-8') + '\n' u'\n'.join((u' * %s: %s%s' % (backend.name, repr(error), backtrace.decode('utf-8') + '\n'
if logging.root.level == logging.DEBUG else '')) if logging.root.level == logging.DEBUG else ''))
for backend, error, backtrace in errors))) for backend, error, backtrace in errors)))
self.errors = copy(errors) self.errors = copy(errors)

View file

@ -97,6 +97,7 @@ class Weboob(object):
return loaded return loaded
def unload_backends(self, names=None): def unload_backends(self, names=None):
unloaded = {}
if isinstance(names, basestring): if isinstance(names, basestring):
names = [names] names = [names]
elif names is None: elif names is None:
@ -106,6 +107,9 @@ class Weboob(object):
backend = self.backend_instances.pop(name) backend = self.backend_instances.pop(name)
with backend: with backend:
backend.deinit() backend.deinit()
unloaded[backend.name] = backend
return unloaded
def get_backend(self, name): def get_backend(self, name):
return self.backend_instances[name] return self.backend_instances[name]

View file

@ -31,6 +31,7 @@ from weboob.capabilities.base import FieldNotFound
from weboob.core import CallErrors from weboob.core import CallErrors
from weboob.core.backendscfg import BackendsConfig, BackendAlreadyExists from weboob.core.backendscfg import BackendsConfig, BackendAlreadyExists
from weboob.tools.misc import iter_fields from weboob.tools.misc import iter_fields
from weboob.tools.browser import BrowserUnavailable, BrowserIncorrectPassword
from .base import BackendNotFound, BaseApplication from .base import BackendNotFound, BaseApplication
from .formatters.load import formatters as available_formatters, load_formatter from .formatters.load import formatters as available_formatters, load_formatter
@ -119,7 +120,7 @@ class ReplApplication(Cmd, BaseApplication):
atexit.register(savehist) atexit.register(savehist)
self._interactive = False self._interactive = False
self.enabled_backends = [] self.enabled_backends = set()
@property @property
def interactive(self): def interactive(self):
@ -134,10 +135,20 @@ class ReplApplication(Cmd, BaseApplication):
return False return False
return True return True
def add_backend(self, name, params=None): def edit_backend(self, name, params=None):
return self.add_backend(name, params, True)
def add_backend(self, name, params=None, edit=False):
if params is None: if params is None:
params = {} params = {}
if not edit:
backend = self.weboob.modules_loader.get_or_load_module(name) backend = self.weboob.modules_loader.get_or_load_module(name)
else:
bname, items = self.weboob.backends_config.get_backend(name)
backend = self.weboob.modules_loader.get_or_load_module(bname)
items.update(params)
params = items
if not backend: if not backend:
print 'Backend "%s" does not exist.' % name print 'Backend "%s" does not exist.' % name
return None return None
@ -149,9 +160,9 @@ class ReplApplication(Cmd, BaseApplication):
asked_config = True asked_config = True
print 'Configuration of backend' print 'Configuration of backend'
print '------------------------' print '------------------------'
if key not in params: if key not in params or edit:
params[key] = self.ask(' [%s] %s' % (key, value.description), params[key] = self.ask(' [%s] %s' % (key, value.description),
default=value.default, default=params[key] if (edit and key in params) else value.default,
masked=value.is_masked, masked=value.is_masked,
choices=value.choices, choices=value.choices,
regexp=value.regexp) regexp=value.regexp)
@ -161,10 +172,10 @@ class ReplApplication(Cmd, BaseApplication):
print '------------------------' print '------------------------'
try: try:
self.weboob.backends_config.add_backend(name, name, params) self.weboob.backends_config.add_backend(name, name, params, edit=edit)
print 'Backend "%s" successfully added.' % name print 'Backend "%s" successfully %s.' % (name, 'updated' if edit else 'added')
return name return name
except BackendAlreadyExists:#ConfigParser.DuplicateSectionError: except BackendAlreadyExists:
print 'Backend "%s" is already configured in file "%s"' % (name, self.weboob.backends_config.confpath) print 'Backend "%s" is already configured in file "%s"' % (name, self.weboob.backends_config.confpath)
while self.ask('Add new instance of "%s" backend?' % name, default=False): while self.ask('Add new instance of "%s" backend?' % name, default=False):
new_name = self.ask('Please give new instance name (could be "%s_1")' % name, regexp=u'^[\d\w_-]+$') new_name = self.ask('Please give new instance name (could be "%s_1")' % name, regexp=u'^[\d\w_-]+$')
@ -175,10 +186,19 @@ class ReplApplication(Cmd, BaseApplication):
except BackendAlreadyExists: except BackendAlreadyExists:
print 'Instance "%s" already exists for backend "%s".' % (new_name, name) print 'Instance "%s" already exists for backend "%s".' % (new_name, name)
def unload_backends(self, *args, **kwargs):
unloaded = self.weboob.unload_backends(*args, **kwargs)
for backend in unloaded.itervalues():
try:
self.enabled_backends.remove(backend)
except ValueError:
pass
return unloaded
def load_backends(self, *args, **kwargs): def load_backends(self, *args, **kwargs):
ret = super(ReplApplication, self).load_backends(*args, **kwargs) ret = super(ReplApplication, self).load_backends(*args, **kwargs)
for name, backend in ret.iteritems(): for name, backend in ret.iteritems():
self.enabled_backends.append(backend) self.enabled_backends.add(backend)
while len(self.enabled_backends) == 0: while len(self.enabled_backends) == 0:
print 'Warning: there is currently no configured backend for %s' % self.APPNAME print 'Warning: there is currently no configured backend for %s' % self.APPNAME
if not self.ask('Do you want to configure backends?', default=True): if not self.ask('Do you want to configure backends?', default=True):
@ -264,7 +284,31 @@ class ReplApplication(Cmd, BaseApplication):
try: try:
return super(ReplApplication, self).onecmd(_cmd) return super(ReplApplication, self).onecmd(_cmd)
except CallErrors, e: except CallErrors, e:
print >>sys.stderr, '%s' % e ask_debug_mode = False
for backend, error, backtrace in e.errors:
if isinstance(error, BrowserIncorrectPassword):
msg = unicode(error)
if not msg:
msg = 'invalid login/password.'
print >>sys.stderr, 'Error(%s): %s' % (backend.name, msg)
if self.ask('Do you want to reconfigure this backend?', default=True):
self.unload_backends(names=[backend.name])
self.edit_backend(backend.name)
self.load_backends(names=[backend.name])
elif isinstance(error, BrowserUnavailable):
msg = unicode(error)
if not msg:
msg = 'website is unavailable.'
print >>sys.stderr, 'Error(%s): %s' % (backend.name, msg)
else:
common_errors = True
print >>sys.stderr, 'Error(%s): %s' % (backend.name, error)
if logging.root.level == logging.DEBUG:
print >>sys.stderr, backtrace
else:
ask_debug_mode = True
if ask_debug_mode:
print >>sys.stderr, 'Use --debug option to print backtraces.'
except NotEnoughArguments, e: except NotEnoughArguments, e:
print >>sys.stderr, 'Error: no enough arguments.' print >>sys.stderr, 'Error: no enough arguments.'
except (KeyboardInterrupt,EOFError): except (KeyboardInterrupt,EOFError):
@ -507,11 +551,7 @@ class ReplApplication(Cmd, BaseApplication):
elif action == 'remove': elif action == 'remove':
for backend in given_backends: for backend in given_backends:
self.weboob.backends_config.remove_backend(backend.name) self.weboob.backends_config.remove_backend(backend.name)
self.weboob.unload_backends(backend.name) self.unload_backends(backend.name)
try:
self.enabled_backends.remove(backend)
except ValueError:
pass
else: else:
print 'Unknown action: "%s"' % action print 'Unknown action: "%s"' % action
return 1 return 1