rewrite formatters management in repl
Commands can use specific default formatters, user can redefine formatters per commands or globally.
This commit is contained in:
parent
775ec96b22
commit
a86a6b2669
3 changed files with 119 additions and 55 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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
|
# 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
|
# 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.
|
# 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':
|
if name == 'htmltable':
|
||||||
return TableFormatter(result_funcname='get_html_string')
|
from .table import HTMLTableFormatter
|
||||||
|
return HTMLTableFormatter
|
||||||
elif name == 'table':
|
elif name == 'table':
|
||||||
return TableFormatter()
|
from .table import TableFormatter
|
||||||
elif name == 'simple':
|
return TableFormatter
|
||||||
from .simple import SimpleFormatter
|
elif name == 'simple':
|
||||||
return SimpleFormatter()
|
from .simple import SimpleFormatter
|
||||||
elif name == 'multiline':
|
return SimpleFormatter
|
||||||
from .multiline import MultilineFormatter
|
elif name == 'multiline':
|
||||||
return MultilineFormatter()
|
from .multiline import MultilineFormatter
|
||||||
elif name == 'webkit':
|
return MultilineFormatter
|
||||||
from .webkit import WebkitGtkFormatter
|
elif name == 'webkit':
|
||||||
return WebkitGtkFormatter()
|
from .webkit import WebkitGtkFormatter
|
||||||
|
return WebkitGtkFormatter
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,17 @@ from prettytable import PrettyTable
|
||||||
from .iformatter import IFormatter
|
from .iformatter import IFormatter
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TableFormatter']
|
__all__ = ['TableFormatter', 'HTMLTableFormatter']
|
||||||
|
|
||||||
|
|
||||||
class TableFormatter(IFormatter):
|
class TableFormatter(IFormatter):
|
||||||
column_headers = None
|
column_headers = None
|
||||||
queue = []
|
queue = []
|
||||||
header = None
|
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)
|
IFormatter.__init__(self, display_keys=display_keys, return_only=return_only)
|
||||||
self.result_funcname = result_funcname
|
|
||||||
|
|
||||||
def after_format(self, formatted):
|
def after_format(self, formatted):
|
||||||
if self.column_headers is None:
|
if self.column_headers is None:
|
||||||
|
|
@ -43,17 +43,21 @@ class TableFormatter(IFormatter):
|
||||||
return None
|
return None
|
||||||
s = ''
|
s = ''
|
||||||
if self.display_header and self.header:
|
if self.display_header and self.header:
|
||||||
if self.result_funcname == 'get_string':
|
if self.HTML:
|
||||||
s += self.header
|
|
||||||
elif self.result_funcname == 'get_html_string':
|
|
||||||
s+= '<p>%s</p>' % self.header
|
s+= '<p>%s</p>' % self.header
|
||||||
|
else:
|
||||||
|
s += self.header
|
||||||
s += "\n"
|
s += "\n"
|
||||||
table = PrettyTable(list(self.column_headers))
|
table = PrettyTable(list(self.column_headers))
|
||||||
for column_header in self.column_headers:
|
for column_header in self.column_headers:
|
||||||
table.set_field_align(column_header, 'l')
|
table.set_field_align(column_header, 'l')
|
||||||
for line in self.queue:
|
for line in self.queue:
|
||||||
table.add_row(line)
|
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 = []
|
self.queue = []
|
||||||
if self.return_only:
|
if self.return_only:
|
||||||
return s
|
return s
|
||||||
|
|
@ -66,3 +70,6 @@ class TableFormatter(IFormatter):
|
||||||
|
|
||||||
def set_header(self, string):
|
def set_header(self, string):
|
||||||
self.header = string
|
self.header = string
|
||||||
|
|
||||||
|
class HTMLTableFormatter(TableFormatter):
|
||||||
|
HTML = True
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ from weboob.tools.misc import iter_fields
|
||||||
from weboob.tools.browser import BrowserUnavailable, BrowserIncorrectPassword
|
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 FormattersLoader, FormatterLoadError
|
||||||
from .results import ResultsCondition, ResultsConditionException
|
from .results import ResultsCondition, ResultsConditionException
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,6 +58,10 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
BOLD = '[37;1m'
|
BOLD = '[37;1m'
|
||||||
NC = '[37;0m' # no color
|
NC = '[37;0m' # no color
|
||||||
|
|
||||||
|
EXTRA_FORMATTERS = {}
|
||||||
|
DEFAULT_FORMATTER = 'multiline'
|
||||||
|
COMMANDS_FORMATTERS = {}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Cmd.__init__(self)
|
Cmd.__init__(self)
|
||||||
self.prompt = self.BOLD + '%s> ' % self.APPNAME + self.NC
|
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.weboob_commands = ['backends', 'condition', 'count', 'formatter', 'logging', 'select', 'quit']
|
||||||
self.hidden_commands = set(['EOF'])
|
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:
|
try:
|
||||||
BaseApplication.__init__(self)
|
BaseApplication.__init__(self)
|
||||||
except BackendsConfig.WrongPermissions, e:
|
except BackendsConfig.WrongPermissions, e:
|
||||||
|
|
@ -102,6 +112,7 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
self._parser.add_option_group(results_options)
|
self._parser.add_option_group(results_options)
|
||||||
|
|
||||||
formatting_options = OptionGroup(self._parser, 'Formatting 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,
|
formatting_options.add_option('-f', '--formatter', choices=available_formatters,
|
||||||
help='select output formatter (%s)' % u','.join(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')
|
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.
|
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:
|
try:
|
||||||
return super(ReplApplication, self).onecmd(_cmd)
|
return super(ReplApplication, self).onecmd(_cmd)
|
||||||
except CallErrors, e:
|
except CallErrors, e:
|
||||||
|
|
@ -313,9 +329,9 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
msg = unicode(error)
|
msg = unicode(error)
|
||||||
if not msg:
|
if not msg:
|
||||||
msg = 'website is unavailable.'
|
msg = 'website is unavailable.'
|
||||||
print >>sys.stderr, 'Error(%s): %s' % (backend.name, msg)
|
print >>sys.stderr, u'Error(%s): %s' % (backend.name, msg)
|
||||||
else:
|
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:
|
if logging.root.level == logging.DEBUG:
|
||||||
print >>sys.stderr, backtrace
|
print >>sys.stderr, backtrace
|
||||||
else:
|
else:
|
||||||
|
|
@ -369,14 +385,9 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
# options related methods
|
# options related methods
|
||||||
|
|
||||||
def _handle_options(self):
|
def _handle_options(self):
|
||||||
self.formatter_name = self.options.formatter if self.options.formatter else 'multiline'
|
if self.options.formatter:
|
||||||
self.formatter = load_formatter(self.formatter_name)
|
self.commands_formatters = {}
|
||||||
|
self.DEFAULT_FORMATTER = self.options.formatter
|
||||||
if self.options.no_header:
|
|
||||||
self.formatter.display_header = False
|
|
||||||
|
|
||||||
if self.options.no_keys:
|
|
||||||
self.formatter.display_keys = False
|
|
||||||
|
|
||||||
if self.options.select:
|
if self.options.select:
|
||||||
self.selected_fields = self.options.select.split(',')
|
self.selected_fields = self.options.select.split(',')
|
||||||
|
|
@ -709,7 +720,8 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
print self.options.count
|
print self.options.count
|
||||||
|
|
||||||
def complete_formatter(self, text, line, *ignored):
|
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']
|
options = ['header', 'keys']
|
||||||
option_values = ['on', 'off']
|
option_values = ['on', 'off']
|
||||||
|
|
||||||
|
|
@ -721,12 +733,16 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
return options
|
return options
|
||||||
if len(args) == 4:
|
if len(args) == 4:
|
||||||
return option_values
|
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):
|
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.
|
If the argument is "list", print the available formatters.
|
||||||
|
|
||||||
|
|
@ -740,7 +756,7 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
args = line.strip().split()
|
args = line.strip().split()
|
||||||
if args:
|
if args:
|
||||||
if args[0] == 'list':
|
if args[0] == 'list':
|
||||||
print ', '.join(available_formatters)
|
print ', '.join(self.formatters_loader.get_available_formatters())
|
||||||
elif args[0] == 'option':
|
elif args[0] == 'option':
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
|
|
@ -759,14 +775,19 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
else:
|
else:
|
||||||
print 'Don\'t know which option to set. Available options: header, keys.'
|
print 'Don\'t know which option to set. Available options: header, keys.'
|
||||||
else:
|
else:
|
||||||
if args[0] in available_formatters:
|
if args[0] in self.formatters_loader.get_available_formatters():
|
||||||
self.formatter = load_formatter(args[0])
|
if len(args) > 1:
|
||||||
self.formatter_name = args[0]
|
self.commands_formatters[args[1]] = self.set_formatter(args[0])
|
||||||
|
else:
|
||||||
|
self.commands_formatters = {}
|
||||||
|
self.DEFAULT_FORMATTER = self.set_formatter(args[0])
|
||||||
else:
|
else:
|
||||||
print 'Formatter "%s" is not available.\n' \
|
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:
|
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):
|
def do_select(self, line):
|
||||||
"""
|
"""
|
||||||
|
|
@ -842,15 +863,24 @@ class ReplApplication(Cmd, BaseApplication):
|
||||||
|
|
||||||
# formatting related methods
|
# formatting related methods
|
||||||
|
|
||||||
def set_default_formatter(self, name):
|
def set_formatter(self, name):
|
||||||
if not self.options.formatter:
|
"""
|
||||||
try:
|
Set the current formatter from name.
|
||||||
self.formatter = load_formatter(name)
|
|
||||||
except ImportError:
|
It returns the name of the formatter which has been really set.
|
||||||
default_name = 'multiline'
|
"""
|
||||||
print 'Could not load default formatter "%s" for this command. Falling back to "%s".' % (
|
try:
|
||||||
name, default_name)
|
self.formatter = self.formatters_loader.build_formatter(name)
|
||||||
self.formatter = load_formatter(default_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):
|
def set_formatter_header(self, string):
|
||||||
self.formatter.set_header(string)
|
self.formatter.set_header(string)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue