diff --git a/weboob/tools/application/formatters/load.py b/weboob/tools/application/formatters/load.py index 2f789538..51e7e7b2 100644 --- a/weboob/tools/application/formatters/load.py +++ b/weboob/tools/application/formatters/load.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright(C) 2010 Christophe Benz +# Copyright(C) 2010 Christophe Benz, Romain Bignon # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,26 +16,53 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -__all__ = ['load_formatter'] +__all__ = ['FormattersLoader', 'FormatterLoadError'] -formatters = ('htmltable', 'multiline', 'simple', 'table', 'webkit') - +class FormatterLoadError(Exception): + pass + +class FormattersLoader(object): + BUILTINS = ['htmltable', 'multiline', 'simple', 'table', 'webkit'] + + def __init__(self): + self.formatters = {} + + def register_formatter(self, name, klass): + self.formatters[name] = klass + + def get_available_formatters(self): + l = set(self.formatters.iterkeys()) + l = l.union(self.BUILTINS) + l = list(l) + l.sort() + return l + + def build_formatter(self, name): + if not name in self.formatters: + try: + self.formatters[name] = self.load_builtin_formatter(name) + except ImportError, e: + FormattersLoader.BUILTINS.remove(name) + raise FormatterLoadError('Unable to load formatter "%s": %s' % (name, e)) + return self.formatters[name]() + + def load_builtin_formatter(self, name): + if not name in self.BUILTINS: + raise FormatterLoadError('Formatter "%s" does not exist' % name) -def load_formatter(name): - assert name in formatters - if name in ('htmltable', 'table'): - from .table import TableFormatter if name == 'htmltable': - return TableFormatter(result_funcname='get_html_string') + from .table import HTMLTableFormatter + return HTMLTableFormatter elif name == 'table': - return TableFormatter() - elif name == 'simple': - from .simple import SimpleFormatter - return SimpleFormatter() - elif name == 'multiline': - from .multiline import MultilineFormatter - return MultilineFormatter() - elif name == 'webkit': - from .webkit import WebkitGtkFormatter - return WebkitGtkFormatter() + from .table import TableFormatter + return TableFormatter + elif name == 'simple': + from .simple import SimpleFormatter + return SimpleFormatter + elif name == 'multiline': + from .multiline import MultilineFormatter + return MultilineFormatter + elif name == 'webkit': + from .webkit import WebkitGtkFormatter + return WebkitGtkFormatter diff --git a/weboob/tools/application/formatters/table.py b/weboob/tools/application/formatters/table.py index 01a86ce4..c29d89dd 100644 --- a/weboob/tools/application/formatters/table.py +++ b/weboob/tools/application/formatters/table.py @@ -21,17 +21,17 @@ from prettytable import PrettyTable from .iformatter import IFormatter -__all__ = ['TableFormatter'] +__all__ = ['TableFormatter', 'HTMLTableFormatter'] class TableFormatter(IFormatter): column_headers = None queue = [] header = None + HTML = False - def __init__(self, display_keys=True, return_only=False, result_funcname='get_string'): + def __init__(self, display_keys=True, return_only=False): IFormatter.__init__(self, display_keys=display_keys, return_only=return_only) - self.result_funcname = result_funcname def after_format(self, formatted): if self.column_headers is None: @@ -43,17 +43,21 @@ class TableFormatter(IFormatter): return None s = '' if self.display_header and self.header: - if self.result_funcname == 'get_string': - s += self.header - elif self.result_funcname == 'get_html_string': + if self.HTML: s+= '
%s
' % self.header + else: + s += self.header s += "\n" table = PrettyTable(list(self.column_headers)) for column_header in self.column_headers: table.set_field_align(column_header, 'l') for line in self.queue: table.add_row(line) - s += getattr(table, self.result_funcname)() + + if self.HTML: + s += table.get_html_string() + else: + s += table.get_string() self.queue = [] if self.return_only: return s @@ -66,3 +70,6 @@ class TableFormatter(IFormatter): def set_header(self, string): self.header = string + +class HTMLTableFormatter(TableFormatter): + HTML = True diff --git a/weboob/tools/application/repl.py b/weboob/tools/application/repl.py index 23f89fae..9e924e5b 100644 --- a/weboob/tools/application/repl.py +++ b/weboob/tools/application/repl.py @@ -34,7 +34,7 @@ from weboob.tools.misc import iter_fields from weboob.tools.browser import BrowserUnavailable, BrowserIncorrectPassword from .base import BackendNotFound, BaseApplication -from .formatters.load import formatters as available_formatters, load_formatter +from .formatters.load import FormattersLoader, FormatterLoadError from .results import ResultsCondition, ResultsConditionException @@ -58,6 +58,10 @@ class ReplApplication(Cmd, BaseApplication): BOLD = '[37;1m' NC = '[37;0m' # no color + EXTRA_FORMATTERS = {} + DEFAULT_FORMATTER = 'multiline' + COMMANDS_FORMATTERS = {} + def __init__(self): Cmd.__init__(self) self.prompt = self.BOLD + '%s> ' % self.APPNAME + self.NC @@ -74,6 +78,12 @@ class ReplApplication(Cmd, BaseApplication): self.weboob_commands = ['backends', 'condition', 'count', 'formatter', 'logging', 'select', 'quit'] self.hidden_commands = set(['EOF']) + self.formatters_loader = FormattersLoader() + for key, klass in self.EXTRA_FORMATTERS.iteritems(): + self.formatters_loader.register_formatter(key, klass) + self.formatter = None + self.commands_formatters = self.COMMANDS_FORMATTERS.copy() + try: BaseApplication.__init__(self) except BackendsConfig.WrongPermissions, e: @@ -102,6 +112,7 @@ class ReplApplication(Cmd, BaseApplication): self._parser.add_option_group(results_options) formatting_options = OptionGroup(self._parser, 'Formatting Options') + available_formatters = self.formatters_loader.get_available_formatters() formatting_options.add_option('-f', '--formatter', choices=available_formatters, help='select output formatter (%s)' % u','.join(available_formatters)) formatting_options.add_option('--no-header', dest='no_header', action='store_true', help='do not display header') @@ -295,6 +306,11 @@ class ReplApplication(Cmd, BaseApplication): """ This REPL method is overrided to catch some particular exceptions. """ + cmd_name = _cmd.split()[0] + if cmd_name in self.commands_formatters: + self.set_formatter(self.commands_formatters[cmd_name]) + else: + self.set_formatter(self.DEFAULT_FORMATTER) try: return super(ReplApplication, self).onecmd(_cmd) except CallErrors, e: @@ -313,9 +329,9 @@ class ReplApplication(Cmd, BaseApplication): msg = unicode(error) if not msg: msg = 'website is unavailable.' - print >>sys.stderr, 'Error(%s): %s' % (backend.name, msg) + print >>sys.stderr, u'Error(%s): %s' % (backend.name, msg) else: - print >>sys.stderr, 'Error(%s): %s' % (backend.name, error) + print >>sys.stderr, u'Error(%s): %s' % (backend.name, error) if logging.root.level == logging.DEBUG: print >>sys.stderr, backtrace else: @@ -369,14 +385,9 @@ class ReplApplication(Cmd, BaseApplication): # options related methods def _handle_options(self): - self.formatter_name = self.options.formatter if self.options.formatter else 'multiline' - self.formatter = load_formatter(self.formatter_name) - - if self.options.no_header: - self.formatter.display_header = False - - if self.options.no_keys: - self.formatter.display_keys = False + if self.options.formatter: + self.commands_formatters = {} + self.DEFAULT_FORMATTER = self.options.formatter if self.options.select: self.selected_fields = self.options.select.split(',') @@ -709,7 +720,8 @@ class ReplApplication(Cmd, BaseApplication): print self.options.count def complete_formatter(self, text, line, *ignored): - commands = list(available_formatters) + ['list', 'option'] + formatters = self.formatters_loader.get_available_formatters() + commands = ['list', 'option'] + formatters options = ['header', 'keys'] option_values = ['on', 'off'] @@ -721,12 +733,16 @@ class ReplApplication(Cmd, BaseApplication): return options if len(args) == 4: return option_values + elif args[1] in formatters: + return list(set(name[3:] for name in self.get_names() if name.startswith('do_'))) def do_formatter(self, line): """ - formatter [FORMATTER_NAME | list | option OPTION_NAME [on | off]] + formatter [list | FORMATTER [COMMAND] | option OPTION_NAME [on | off]] - If a FORMATTER_NAME is given, set the formatter to use. + If a FORMATTER is given, set the formatter to use. + You can add a COMMAND to apply the formatter change only to + a given command. If the argument is "list", print the available formatters. @@ -740,7 +756,7 @@ class ReplApplication(Cmd, BaseApplication): args = line.strip().split() if args: if args[0] == 'list': - print ', '.join(available_formatters) + print ', '.join(self.formatters_loader.get_available_formatters()) elif args[0] == 'option': if len(args) > 1: if len(args) == 2: @@ -759,14 +775,19 @@ class ReplApplication(Cmd, BaseApplication): else: print 'Don\'t know which option to set. Available options: header, keys.' else: - if args[0] in available_formatters: - self.formatter = load_formatter(args[0]) - self.formatter_name = args[0] + if args[0] in self.formatters_loader.get_available_formatters(): + if len(args) > 1: + self.commands_formatters[args[1]] = self.set_formatter(args[0]) + else: + self.commands_formatters = {} + self.DEFAULT_FORMATTER = self.set_formatter(args[0]) else: print 'Formatter "%s" is not available.\n' \ - 'Available formatters: %s.' % (args[0], ', '.join(available_formatters)) + 'Available formatters: %s.' % (args[0], ', '.join(self.formatters_loader.get_available_formatters())) else: - print self.formatter_name + print 'Default formatter: %s' % self.DEFAULT_FORMATTER + for key, klass in self.commands_formatters.iteritems(): + print 'Command "%s": %s' % (key, klass) def do_select(self, line): """ @@ -842,15 +863,24 @@ class ReplApplication(Cmd, BaseApplication): # formatting related methods - def set_default_formatter(self, name): - if not self.options.formatter: - try: - self.formatter = load_formatter(name) - except ImportError: - default_name = 'multiline' - print 'Could not load default formatter "%s" for this command. Falling back to "%s".' % ( - name, default_name) - self.formatter = load_formatter(default_name) + def set_formatter(self, name): + """ + Set the current formatter from name. + + It returns the name of the formatter which has been really set. + """ + try: + self.formatter = self.formatters_loader.build_formatter(name) + except FormatterLoadError, e: + print '%s' % e + print 'Falling back to "%s".' % (name, self.DEFAULT_FORMATTER) + self.formatter = self.formatters_loader.build_formatter(self.DEFAULT_FORMATTER) + name = self.DEFAULT_FORMATTER + if self.options.no_header: + self.formatter.display_header = False + if self.options.no_keys: + self.formatter.display_keys = False + return name def set_formatter_header(self, string): self.formatter.set_header(string)