diff --git a/weboob/applications/boobank/boobank.py b/weboob/applications/boobank/boobank.py index 1086c478..3ee9892d 100644 --- a/weboob/applications/boobank/boobank.py +++ b/weboob/applications/boobank/boobank.py @@ -31,9 +31,7 @@ class Boobank(ReplApplication): APPNAME = 'boobank' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon, Christophe Benz' - - def load_default_backends(self): - self.load_backends(ICapBank) + CAPS = ICapBank def do_list(self, line): """ diff --git a/weboob/applications/chatoob/chatoob.py b/weboob/applications/chatoob/chatoob.py index 7685c16b..fe9ca657 100644 --- a/weboob/applications/chatoob/chatoob.py +++ b/weboob/applications/chatoob/chatoob.py @@ -30,9 +30,7 @@ class Chatoob(ReplApplication): APPNAME = 'chatoob' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Christophe Benz' - - def load_default_backends(self): - self.load_backends(ICapChat) + CAPS = ICapChat def on_new_chat_message(self, message): print 'on_new_chat_message: %s' % message diff --git a/weboob/applications/geolooc/geolooc.py b/weboob/applications/geolooc/geolooc.py index e1130dd5..09ea3a0c 100644 --- a/weboob/applications/geolooc/geolooc.py +++ b/weboob/applications/geolooc/geolooc.py @@ -29,9 +29,7 @@ class Geolooc(ReplApplication): APPNAME = 'geolooc' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' - - def load_default_backends(self): - self.load_backends(ICapGeolocIp) + CAPS = ICapGeolocIp def main(self, argv): if len(argv) < 2: diff --git a/weboob/applications/havesex/havesex.py b/weboob/applications/havesex/havesex.py index 2d9c5860..1ba59eb1 100644 --- a/weboob/applications/havesex/havesex.py +++ b/weboob/applications/havesex/havesex.py @@ -35,6 +35,7 @@ class HaveSex(ReplApplication): COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' STORAGE_FILENAME = 'dating.storage' CONFIG = {'optimizations': ''} + CAPS = ICapDating def load_default_backends(self): self.load_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME)) diff --git a/weboob/applications/monboob/monboob.py b/weboob/applications/monboob/monboob.py index 4e276a00..5da3bf56 100644 --- a/weboob/applications/monboob/monboob.py +++ b/weboob/applications/monboob/monboob.py @@ -84,6 +84,7 @@ class Monboob(ReplApplication): 'recipient': 'weboob@example.org', 'smtp': 'localhost', 'html': 0} + CAPS = ICapMessages def add_application_options(self, group): group.add_option('-S', '--smtpd', help='run a fake smtpd server and set the port') diff --git a/weboob/applications/traveloob/traveloob.py b/weboob/applications/traveloob/traveloob.py index ffaeafd1..fa953695 100644 --- a/weboob/applications/traveloob/traveloob.py +++ b/weboob/applications/traveloob/traveloob.py @@ -29,9 +29,7 @@ class Traveloob(ReplApplication): APPNAME = 'traveloob' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' - - def load_default_backends(self): - self.load_backends(ICapTravel) + CAPS = ICapTravel def do_stations(self, pattern): """ diff --git a/weboob/applications/videoob/videoob.py b/weboob/applications/videoob/videoob.py index b1314944..cfa100f8 100644 --- a/weboob/applications/videoob/videoob.py +++ b/weboob/applications/videoob/videoob.py @@ -120,6 +120,7 @@ class Videoob(ReplApplication): APPNAME = 'videoob' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, Romain Bignon, John Obbele' + CAPS = ICapVideo def __init__(self, *args, **kwargs): ReplApplication.__init__(self, *args, **kwargs) @@ -131,9 +132,6 @@ class Videoob(ReplApplication): self.videos = [] - def load_default_backends(self): - self.load_backends(caps=ICapVideo) - def add_application_options(self, group): group.add_option('--nsfw', action='store_true', help='enable non-suitable for work videos') diff --git a/weboob/applications/weboobcfg/weboobcfg.py b/weboob/applications/weboobcfg/weboobcfg.py index 41025135..42e439e4 100644 --- a/weboob/applications/weboobcfg/weboobcfg.py +++ b/weboob/applications/weboobcfg/weboobcfg.py @@ -16,8 +16,6 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -import ConfigParser -import logging import os import subprocess import re @@ -37,13 +35,6 @@ class WeboobCfg(ReplApplication): def load_default_backends(self): pass - def caps_included(self, modcaps, caps): - modcaps = [x.__name__ for x in modcaps] - for cap in caps: - if not cap in modcaps: - return False - return True - def do_add(self, line): """ add NAME [OPTIONS ...] @@ -56,60 +47,17 @@ class WeboobCfg(ReplApplication): else: options = () - self.weboob.modules_loader.load_all() - if name not in [_name for _name, backend in self.weboob.modules_loader.loaded.iteritems()]: - logging.error(u'Backend "%s" does not exist.' % name) - return - params = {} # set backend params from command-line arguments for option in options: try: key, value = option.split('=', 1) except ValueError: - logging.error(u'Parameters have to be formatted "key=value"') + print 'Parameters have to be formatted "key=value"' return params[key] = value - # ask for params non-specified on command-line arguments - backend = self.weboob.modules_loader.get_or_load_module(name) - asked_config = False - for key, value in backend.config.iteritems(): - if not asked_config: - asked_config = True - print 'Configuration of backend' - print '------------------------' - if key not in params: - params[key] = self.ask(' [%s] %s' % (key, value.description), - default=value.default, - masked=value.is_masked, - choices=value.choices, - regexp=value.regexp) - else: - print ' [%s] %s: %s' % (key, value.description, '(masked)' if value.is_masked else params[key]) - if asked_config: - print '------------------------' - try: - self.weboob.backends_config.add_backend(name, name, params) - print 'Backend "%s" successfully added to file "%s".\n'\ - 'Please check configuration parameters values with "weboob-config edit".' % ( - name, self.weboob.backends_config.confpath) - except ConfigParser.DuplicateSectionError: - print 'Backend "%s" is already configured in file "%s"' % (name, self.weboob.backends_config.confpath) - response = raw_input(u'Add new instance of "%s" backend? [yN] ' % name) - if response.lower() == 'y': - while True: - new_name = raw_input(u'Please give new instance name (could be "%s_1"): ' % name) - if not new_name: - continue - try: - self.weboob.backends_config.add_backend(new_name, name, params) - print 'Backend "%s" successfully added to file "%s".\n'\ - 'Please check configuration parameters values with "weboob-config edit".' % ( - name, self.weboob.backends_config.confpath) - break - except ConfigParser.DuplicateSectionError: - print 'Instance "%s" already exists for backend "%s".' % (new_name, name) + self.add_backend(name, params) def do_list(self, line): """ @@ -133,11 +81,10 @@ class WeboobCfg(ReplApplication): Remove a configured backend. """ - try: - self.weboob.backends_config.remove_backend(instance_name) - except ConfigParser.NoSectionError: - logging.error('Backend instance "%s" does not exist' % instance_name) + if not self.weboob.backends_config.remove_backend(instance_name): + print 'Backend instance "%s" does not exist' % instance_name return 1 + return 0 def do_edit(self): """ @@ -174,7 +121,7 @@ class WeboobCfg(ReplApplication): try: backend = self.weboob.modules_loader.get_or_load_module(name) except KeyError: - logging.error('No such backend: "%s"' % name) + print 'No such backend: "%s"' % name return 1 print '.------------------------------------------------------------------------------.' @@ -213,5 +160,3 @@ class WeboobCfg(ReplApplication): if m and '__init__.py' in files: applications.add(m.group(1)) print ' '.join(sorted(applications)).encode('utf-8') - - diff --git a/weboob/applications/weboorrents/weboorrents.py b/weboob/applications/weboorrents/weboorrents.py index 4bfa5ba2..98ef5fa3 100644 --- a/weboob/applications/weboorrents/weboorrents.py +++ b/weboob/applications/weboorrents/weboorrents.py @@ -30,9 +30,7 @@ class Weboorrents(ReplApplication): APPNAME = 'weboorrents' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' - - def load_default_backends(self): - self.load_backends(ICapTorrent) + CAPS = ICapTorrent def do_info(self, id): """ diff --git a/weboob/applications/wetboobs/wetboobs.py b/weboob/applications/wetboobs/wetboobs.py index df56b472..de55db50 100644 --- a/weboob/applications/wetboobs/wetboobs.py +++ b/weboob/applications/wetboobs/wetboobs.py @@ -30,9 +30,7 @@ class WetBoobs(ReplApplication): APPNAME = 'wetboobs' VERSION = '0.1' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' - - def load_default_backends(self): - self.load_backends(ICapWeather) + CAPS = ICapWeather def do_search(self, pattern): """ diff --git a/weboob/core/backendscfg.py b/weboob/core/backendscfg.py index 5be415fa..b089bc17 100644 --- a/weboob/core/backendscfg.py +++ b/weboob/core/backendscfg.py @@ -20,11 +20,13 @@ from __future__ import with_statement import stat import os -from ConfigParser import RawConfigParser +from ConfigParser import RawConfigParser, DuplicateSectionError from logging import warning -__all__ = ['BackendsConfig'] +__all__ = ['BackendsConfig', 'BackendAlreadyExists'] +class BackendAlreadyExists(Exception): + pass class BackendsConfig(object): class WrongPermissions(Exception): @@ -64,7 +66,10 @@ class BackendsConfig(object): config = RawConfigParser() config.read(self.confpath) if not edit: - config.add_section(instance_name) + try: + config.add_section(instance_name) + except DuplicateSectionError: + raise BackendAlreadyExists(instance_name) config.set(instance_name, '_backend', backend_name) for key, value in params.iteritems(): config.set(instance_name, key, value) @@ -96,8 +101,10 @@ class BackendsConfig(object): def remove_backend(self, instance_name): config = RawConfigParser() config.read(self.confpath) - config.remove_section(instance_name) + if not config.remove_section(instance_name): + return False with open(self.confpath, 'w') as f: config.write(f) + return True diff --git a/weboob/tools/application/base.py b/weboob/tools/application/base.py index 01255f3a..02b41a65 100644 --- a/weboob/tools/application/base.py +++ b/weboob/tools/application/base.py @@ -306,7 +306,9 @@ class BaseApplication(object): sys.exit(app.main(args)) except KeyboardInterrupt: print 'Program killed by SIGINT' - sys.exit(0) # XXX is it really the right exit code? -romain + sys.exit(0) + except EOFError: + sys.exit(0) except ConfigError, e: print 'Configuration error: %s' % e sys.exit(1) diff --git a/weboob/tools/application/repl.py b/weboob/tools/application/repl.py index fbadb2bd..9ab6b89e 100644 --- a/weboob/tools/application/repl.py +++ b/weboob/tools/application/repl.py @@ -29,7 +29,7 @@ import sys from weboob.capabilities.base import FieldNotFound from weboob.core import CallErrors -from weboob.core.backendscfg import BackendsConfig +from weboob.core.backendscfg import BackendsConfig, BackendAlreadyExists from weboob.tools.misc import iter_fields from .base import BackendNotFound, BaseApplication @@ -50,6 +50,7 @@ class ReplApplication(Cmd, BaseApplication): SYNOPSIS = 'Usage: %prog [-dqv] [-b backends] [-cnfs] [command [arguments..]]\n' SYNOPSIS += ' %prog [--help] [--version]' + CAPS = None # shell escape strings BOLD = '' @@ -124,9 +125,100 @@ class ReplApplication(Cmd, BaseApplication): def interactive(self): return self._interactive + def caps_included(self, modcaps, caps): + modcaps = [x.__name__ for x in modcaps] + if not isinstance(caps, (list,set,tuple)): + caps = (caps,) + for cap in caps: + if not cap in modcaps: + return False + return True + + def add_backend(self, name, params={}): + backend = self.weboob.modules_loader.get_or_load_module(name) + if not backend: + print 'Backend "%s" does not exist.' % name + return None + + # ask for params non-specified on command-line arguments + asked_config = False + for key, value in backend.config.iteritems(): + if not asked_config: + asked_config = True + print 'Configuration of backend' + print '------------------------' + if key not in params: + params[key] = self.ask(' [%s] %s' % (key, value.description), + default=value.default, + masked=value.is_masked, + choices=value.choices, + regexp=value.regexp) + else: + print ' [%s] %s: %s' % (key, value.description, '(masked)' if value.is_masked else params[key]) + if asked_config: + print '------------------------' + + try: + self.weboob.backends_config.add_backend(name, name, params) + print 'Backend "%s" successfully added.' % name + return name + except BackendAlreadyExists:#ConfigParser.DuplicateSectionError: + 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): + new_name = self.ask('Please give new instance name (could be "%s_1")' % name, regexp=u'^[\d\w_-]+$') + try: + self.weboob.backends_config.add_backend(new_name, name, params) + print 'Backend "%s" successfully added.' % new_name + return new_name + except BackendAlreadyExists: + print 'Instance "%s" already exists for backend "%s".' % (new_name, name) + def load_backends(self, *args, **kwargs): ret = super(ReplApplication, self).load_backends(*args, **kwargs) self.enabled_backends = list(self.weboob.iter_backends()) + while len(self.enabled_backends) == 0: + print 'Warning: there is currently no configured backend for %s' % self.APPNAME + if not self.ask('Do you want to configure backends?', default=True): + break + + self.weboob.modules_loader.load_all() + r = '' + while r != 'q': + backends = [] + print '\nAvailable backends:' + for name, backend in sorted(self.weboob.modules_loader.loaded.iteritems()): + if self.CAPS and not self.caps_included(backend.iter_caps(), self.CAPS.__name__): + continue + backends.append(name) + loaded = ' ' + for bi in self.weboob.iter_backends(): + if bi.NAME == name: + if loaded == ' ': + loaded = 'X' + elif loaded == 'X': + loaded = 2 + else: + loaded += 1 + print '%s%d)%s [%s] %s%-15s%s (%s)' % (self.BOLD, len(backends), self.NC, loaded, + self.BOLD, name, self.NC, backend.description) + print '%sq)%s --stop--\n' % (self.BOLD, self.NC) + r = self.ask('Select a backend to add (q to stop)', regexp='^(\d+|q)$') + + if r.isdigit(): + i = int(r) - 1 + if i < 0 or i >= len(backends): + print 'Error: %s is not a valid choice' % r + continue + name = backends[i] + try: + inst = self.add_backend(name) + if inst: + self.load_backends(names=inst) + except (KeyboardInterrupt,EOFError): + print '\nAborted.' + + print 'Right right!' + return ret def load_default_backends(self): @@ -135,7 +227,7 @@ class ReplApplication(Cmd, BaseApplication): Applications can overload this method to restrict backends loaded. """ - self.load_backends() + self.load_backends(self.CAPS) @classmethod def run(klass, args=None): @@ -153,6 +245,15 @@ class ReplApplication(Cmd, BaseApplication): args += tuple([None for i in xrange(nb - len(args))]) return args + def postcmd(self, stop, line): + """ + This REPL method is overrided to return None instead of integers + to prevent stopping cmdloop(). + """ + if not isinstance(stop, bool): + stop = None + return stop + def onecmd(self, _cmd): """ This REPL method is overrided to catch some particular exceptions. @@ -180,7 +281,9 @@ class ReplApplication(Cmd, BaseApplication): cmd_line = ' '.join(cmd_args) cmds = cmd_line.split(';') for cmd in cmds: - self.onecmd(cmd) + ret = self.onecmd(cmd) + if ret: + return ret else: self.intro += '\nLoaded backends: %s\n' % ', '.join(sorted(backend.name for backend in self.weboob.iter_backends())) self._interactive = True @@ -374,27 +477,18 @@ class ReplApplication(Cmd, BaseApplication): given_backends = set(backend for backend in self.weboob.iter_backends() if backend.name in given_backend_names) if action == 'enable': - action_func = self.enabled_backends.add for backend in given_backends: - try: - action_func(backend) - except KeyError, e: - print e + self.enabled_backends.add(backend) elif action == 'disable': - action_func = self.enabled_backends.remove for backend in given_backends: try: - action_func(backend) - except KeyError, e: - logging.info('%s is not enabled' % e) + self.enabled_backends.remove(backend) + except KeyError: + print '%s is not enabled' % backend elif action == 'only': self.enabled_backends = set() - action_func = self.enabled_backends.add for backend in given_backends: - try: - action_func(backend) - except KeyError, e: - print e + self.enabled_backends.add(backend) elif action == 'list': print 'Available: %s' % ', '.join(sorted(backend.name for backend in self.weboob.iter_backends())) print 'Enabled: %s' % ', '.join(sorted(backend.name for backend in self.enabled_backends)) @@ -587,7 +681,7 @@ class ReplApplication(Cmd, BaseApplication): question = u'%s [%s]' % (question, default) if masked: - question = u'(input chars are hidden) %s' % question + question = u'%s (hidden input)' % question question += ': ' @@ -596,7 +690,9 @@ class ReplApplication(Cmd, BaseApplication): line = getpass.getpass(question) if masked else raw_input(question) if not line and default is not None: line = default - correct = (not regexp or re.match(regexp, unicode(line))) and \ + if isinstance(line, str): + line = line.decode('utf-8') + correct = (not regexp or re.match(unicode(regexp), unicode(line))) and \ (not choices or unicode(line) in [unicode(s) for s in (choices.iterkeys() if isinstance(choices, dict) else choices)])