repl: interactive add of backends when no one is loaded at startup

This commit is contained in:
Romain Bignon 2010-09-25 00:57:54 -04:00
commit 04037e7893
13 changed files with 144 additions and 106 deletions

View file

@ -31,9 +31,7 @@ class Boobank(ReplApplication):
APPNAME = 'boobank' APPNAME = 'boobank'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon, Christophe Benz' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon, Christophe Benz'
CAPS = ICapBank
def load_default_backends(self):
self.load_backends(ICapBank)
def do_list(self, line): def do_list(self, line):
""" """

View file

@ -30,9 +30,7 @@ class Chatoob(ReplApplication):
APPNAME = 'chatoob' APPNAME = 'chatoob'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz' COPYRIGHT = 'Copyright(C) 2010 Christophe Benz'
CAPS = ICapChat
def load_default_backends(self):
self.load_backends(ICapChat)
def on_new_chat_message(self, message): def on_new_chat_message(self, message):
print 'on_new_chat_message: %s' % message print 'on_new_chat_message: %s' % message

View file

@ -29,9 +29,7 @@ class Geolooc(ReplApplication):
APPNAME = 'geolooc' APPNAME = 'geolooc'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CAPS = ICapGeolocIp
def load_default_backends(self):
self.load_backends(ICapGeolocIp)
def main(self, argv): def main(self, argv):
if len(argv) < 2: if len(argv) < 2:

View file

@ -35,6 +35,7 @@ class HaveSex(ReplApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
STORAGE_FILENAME = 'dating.storage' STORAGE_FILENAME = 'dating.storage'
CONFIG = {'optimizations': ''} CONFIG = {'optimizations': ''}
CAPS = ICapDating
def load_default_backends(self): def load_default_backends(self):
self.load_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME)) self.load_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME))

View file

@ -84,6 +84,7 @@ class Monboob(ReplApplication):
'recipient': 'weboob@example.org', 'recipient': 'weboob@example.org',
'smtp': 'localhost', 'smtp': 'localhost',
'html': 0} 'html': 0}
CAPS = ICapMessages
def add_application_options(self, group): def add_application_options(self, group):
group.add_option('-S', '--smtpd', help='run a fake smtpd server and set the port') group.add_option('-S', '--smtpd', help='run a fake smtpd server and set the port')

View file

@ -29,9 +29,7 @@ class Traveloob(ReplApplication):
APPNAME = 'traveloob' APPNAME = 'traveloob'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CAPS = ICapTravel
def load_default_backends(self):
self.load_backends(ICapTravel)
def do_stations(self, pattern): def do_stations(self, pattern):
""" """

View file

@ -120,6 +120,7 @@ class Videoob(ReplApplication):
APPNAME = 'videoob' APPNAME = 'videoob'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, Romain Bignon, John Obbele' COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, Romain Bignon, John Obbele'
CAPS = ICapVideo
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
ReplApplication.__init__(self, *args, **kwargs) ReplApplication.__init__(self, *args, **kwargs)
@ -131,9 +132,6 @@ class Videoob(ReplApplication):
self.videos = [] self.videos = []
def load_default_backends(self):
self.load_backends(caps=ICapVideo)
def add_application_options(self, group): def add_application_options(self, group):
group.add_option('--nsfw', action='store_true', help='enable non-suitable for work videos') group.add_option('--nsfw', action='store_true', help='enable non-suitable for work videos')

View file

@ -16,8 +16,6 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import ConfigParser
import logging
import os import os
import subprocess import subprocess
import re import re
@ -37,13 +35,6 @@ class WeboobCfg(ReplApplication):
def load_default_backends(self): def load_default_backends(self):
pass 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): def do_add(self, line):
""" """
add NAME [OPTIONS ...] add NAME [OPTIONS ...]
@ -56,60 +47,17 @@ class WeboobCfg(ReplApplication):
else: else:
options = () 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 = {} params = {}
# set backend params from command-line arguments # set backend params from command-line arguments
for option in options: for option in options:
try: try:
key, value = option.split('=', 1) key, value = option.split('=', 1)
except ValueError: except ValueError:
logging.error(u'Parameters have to be formatted "key=value"') print 'Parameters have to be formatted "key=value"'
return return
params[key] = value 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.add_backend(name, params)
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)
def do_list(self, line): def do_list(self, line):
""" """
@ -133,11 +81,10 @@ class WeboobCfg(ReplApplication):
Remove a configured backend. Remove a configured backend.
""" """
try: if not self.weboob.backends_config.remove_backend(instance_name):
self.weboob.backends_config.remove_backend(instance_name) print 'Backend instance "%s" does not exist' % instance_name
except ConfigParser.NoSectionError:
logging.error('Backend instance "%s" does not exist' % instance_name)
return 1 return 1
return 0
def do_edit(self): def do_edit(self):
""" """
@ -174,7 +121,7 @@ class WeboobCfg(ReplApplication):
try: try:
backend = self.weboob.modules_loader.get_or_load_module(name) backend = self.weboob.modules_loader.get_or_load_module(name)
except KeyError: except KeyError:
logging.error('No such backend: "%s"' % name) print 'No such backend: "%s"' % name
return 1 return 1
print '.------------------------------------------------------------------------------.' print '.------------------------------------------------------------------------------.'
@ -213,5 +160,3 @@ class WeboobCfg(ReplApplication):
if m and '__init__.py' in files: if m and '__init__.py' in files:
applications.add(m.group(1)) applications.add(m.group(1))
print ' '.join(sorted(applications)).encode('utf-8') print ' '.join(sorted(applications)).encode('utf-8')

View file

@ -30,9 +30,7 @@ class Weboorrents(ReplApplication):
APPNAME = 'weboorrents' APPNAME = 'weboorrents'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CAPS = ICapTorrent
def load_default_backends(self):
self.load_backends(ICapTorrent)
def do_info(self, id): def do_info(self, id):
""" """

View file

@ -30,9 +30,7 @@ class WetBoobs(ReplApplication):
APPNAME = 'wetboobs' APPNAME = 'wetboobs'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CAPS = ICapWeather
def load_default_backends(self):
self.load_backends(ICapWeather)
def do_search(self, pattern): def do_search(self, pattern):
""" """

View file

@ -20,11 +20,13 @@ from __future__ import with_statement
import stat import stat
import os import os
from ConfigParser import RawConfigParser from ConfigParser import RawConfigParser, DuplicateSectionError
from logging import warning from logging import warning
__all__ = ['BackendsConfig'] __all__ = ['BackendsConfig', 'BackendAlreadyExists']
class BackendAlreadyExists(Exception):
pass
class BackendsConfig(object): class BackendsConfig(object):
class WrongPermissions(Exception): class WrongPermissions(Exception):
@ -64,7 +66,10 @@ class BackendsConfig(object):
config = RawConfigParser() config = RawConfigParser()
config.read(self.confpath) config.read(self.confpath)
if not edit: 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) config.set(instance_name, '_backend', backend_name)
for key, value in params.iteritems(): for key, value in params.iteritems():
config.set(instance_name, key, value) config.set(instance_name, key, value)
@ -96,8 +101,10 @@ class BackendsConfig(object):
def remove_backend(self, instance_name): def remove_backend(self, instance_name):
config = RawConfigParser() config = RawConfigParser()
config.read(self.confpath) 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: with open(self.confpath, 'w') as f:
config.write(f) config.write(f)
return True

View file

@ -306,7 +306,9 @@ class BaseApplication(object):
sys.exit(app.main(args)) sys.exit(app.main(args))
except KeyboardInterrupt: except KeyboardInterrupt:
print 'Program killed by SIGINT' 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: except ConfigError, e:
print 'Configuration error: %s' % e print 'Configuration error: %s' % e
sys.exit(1) sys.exit(1)

View file

@ -29,7 +29,7 @@ import sys
from weboob.capabilities.base import FieldNotFound from weboob.capabilities.base import FieldNotFound
from weboob.core import CallErrors 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 weboob.tools.misc import iter_fields
from .base import BackendNotFound, BaseApplication from .base import BackendNotFound, BaseApplication
@ -50,6 +50,7 @@ class ReplApplication(Cmd, BaseApplication):
SYNOPSIS = 'Usage: %prog [-dqv] [-b backends] [-cnfs] [command [arguments..]]\n' SYNOPSIS = 'Usage: %prog [-dqv] [-b backends] [-cnfs] [command [arguments..]]\n'
SYNOPSIS += ' %prog [--help] [--version]' SYNOPSIS += ' %prog [--help] [--version]'
CAPS = None
# shell escape strings # shell escape strings
BOLD = '' BOLD = ''
@ -124,9 +125,100 @@ class ReplApplication(Cmd, BaseApplication):
def interactive(self): def interactive(self):
return self._interactive 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): def load_backends(self, *args, **kwargs):
ret = super(ReplApplication, self).load_backends(*args, **kwargs) ret = super(ReplApplication, self).load_backends(*args, **kwargs)
self.enabled_backends = list(self.weboob.iter_backends()) 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 return ret
def load_default_backends(self): def load_default_backends(self):
@ -135,7 +227,7 @@ class ReplApplication(Cmd, BaseApplication):
Applications can overload this method to restrict backends loaded. Applications can overload this method to restrict backends loaded.
""" """
self.load_backends() self.load_backends(self.CAPS)
@classmethod @classmethod
def run(klass, args=None): def run(klass, args=None):
@ -153,6 +245,15 @@ class ReplApplication(Cmd, BaseApplication):
args += tuple([None for i in xrange(nb - len(args))]) args += tuple([None for i in xrange(nb - len(args))])
return 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): def onecmd(self, _cmd):
""" """
This REPL method is overrided to catch some particular exceptions. This REPL method is overrided to catch some particular exceptions.
@ -180,7 +281,9 @@ class ReplApplication(Cmd, BaseApplication):
cmd_line = ' '.join(cmd_args) cmd_line = ' '.join(cmd_args)
cmds = cmd_line.split(';') cmds = cmd_line.split(';')
for cmd in cmds: for cmd in cmds:
self.onecmd(cmd) ret = self.onecmd(cmd)
if ret:
return ret
else: else:
self.intro += '\nLoaded backends: %s\n' % ', '.join(sorted(backend.name for backend in self.weboob.iter_backends())) self.intro += '\nLoaded backends: %s\n' % ', '.join(sorted(backend.name for backend in self.weboob.iter_backends()))
self._interactive = True 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) given_backends = set(backend for backend in self.weboob.iter_backends() if backend.name in given_backend_names)
if action == 'enable': if action == 'enable':
action_func = self.enabled_backends.add
for backend in given_backends: for backend in given_backends:
try: self.enabled_backends.add(backend)
action_func(backend)
except KeyError, e:
print e
elif action == 'disable': elif action == 'disable':
action_func = self.enabled_backends.remove
for backend in given_backends: for backend in given_backends:
try: try:
action_func(backend) self.enabled_backends.remove(backend)
except KeyError, e: except KeyError:
logging.info('%s is not enabled' % e) print '%s is not enabled' % backend
elif action == 'only': elif action == 'only':
self.enabled_backends = set() self.enabled_backends = set()
action_func = self.enabled_backends.add
for backend in given_backends: for backend in given_backends:
try: self.enabled_backends.add(backend)
action_func(backend)
except KeyError, e:
print e
elif action == 'list': elif action == 'list':
print 'Available: %s' % ', '.join(sorted(backend.name for backend in self.weboob.iter_backends())) 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)) 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) question = u'%s [%s]' % (question, default)
if masked: if masked:
question = u'(input chars are hidden) %s' % question question = u'%s (hidden input)' % question
question += ': ' question += ': '
@ -596,7 +690,9 @@ class ReplApplication(Cmd, BaseApplication):
line = getpass.getpass(question) if masked else raw_input(question) line = getpass.getpass(question) if masked else raw_input(question)
if not line and default is not None: if not line and default is not None:
line = default 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 (not choices or unicode(line) in
[unicode(s) for s in (choices.iterkeys() if isinstance(choices, dict) else choices)]) [unicode(s) for s in (choices.iterkeys() if isinstance(choices, dict) else choices)])