From 2ddc249b2bd99164cb1c3038aa2aede0d1f00867 Mon Sep 17 00:00:00 2001 From: Christophe Benz Date: Fri, 21 May 2010 19:19:28 +0200 Subject: [PATCH] enhance formatters --- weboob/tools/application/console.py | 14 ++-- .../tools/application/formatters/__init__.py | 8 ++- .../application/formatters/iformatter.py | 70 +++++++++++++++++++ .../tools/application/formatters/multiline.py | 34 +++++++++ weboob/tools/application/formatters/simple.py | 43 ++++-------- weboob/tools/application/results.py | 24 +++---- 6 files changed, 141 insertions(+), 52 deletions(-) create mode 100644 weboob/tools/application/formatters/iformatter.py create mode 100644 weboob/tools/application/formatters/multiline.py diff --git a/weboob/tools/application/console.py b/weboob/tools/application/console.py index 9c40dffd..ce1cea11 100644 --- a/weboob/tools/application/console.py +++ b/weboob/tools/application/console.py @@ -67,18 +67,20 @@ class ConsoleApplication(BaseApplication): def add_application_options(self, group): pass + def _handle_app_options(self): - self._formatter = formatters_classes[self.options.formatter] + self._formatter = formatters[self.options.formatter] if self.options.select: + self._formatter.display_keys = False self.selected_fields = self.options.select.split(',') else: self.selected_fields = None - if self.options.where: - self.where_condition = WhereCondition(self.options.where) + if self.options.condition: + self.condition = ResultsCondition(self.options.condition) else: - self.where_condition = None + self.condition = None def _get_completions(self): return set(name for name, arguments, doc_string in self._commands) @@ -195,10 +197,10 @@ class ConsoleApplication(BaseApplication): def format(self, result): try: - result = self._formatter.format(result, selected_fields=self.selected_fields, where_condition=self.where_condition) + result = self._formatter.format(result, selected_fields=self.selected_fields, condition=self.condition) if result is not None: print result - except WhereConditionException, e: + except ResultsConditionException, e: logging.error(e) register_command = staticmethod(register_command) diff --git a/weboob/tools/application/formatters/__init__.py b/weboob/tools/application/formatters/__init__.py index abcc67c9..572e43ac 100644 --- a/weboob/tools/application/formatters/__init__.py +++ b/weboob/tools/application/formatters/__init__.py @@ -15,12 +15,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +from .multiline import MultilineFormatter from .simple import SimpleFormatter -__all__ = ['formatters_classes'] +__all__ = ['formatters'] -formatters_classes = dict( - simple=SimpleFormatter, +formatters = dict( + multiline=MultilineFormatter(), + simple=SimpleFormatter(), ) diff --git a/weboob/tools/application/formatters/iformatter.py b/weboob/tools/application/formatters/iformatter.py new file mode 100644 index 00000000..52a1d11d --- /dev/null +++ b/weboob/tools/application/formatters/iformatter.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2010 Christophe Benz +# +# 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 +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +import types + + +__all__ = ['IFormatter'] + + +class IFormatter(object): + def format(self, obj, selected_fields=None, condition=None): + """ + Format an object to be human-readable. + An object has fields which can be selected, and the objects + can be filtered using a condition (like SELECT and WHERE in SQL). + If the object provides an iter_fields() method, the formatter will + call it. It can be used to specify the fields order. + + @param obj [object] object to format + @param selected_fields [list] fields to display + @param condition [Condition] condition to objects to display + @return a string of the formatted object + """ + item = self.to_dict(obj, condition) + if selected_fields is None: + selected_fields = sorted(item) + return self.format_dict(item=item, selected_fields=selected_fields) + + def format_dict(self, item, selected_fields): + """ + Format an dict to be human-readable. + Called by format(). + This method has to be overridden in child classes. + + @param item [dict] item to format + @param selected_fields [list] fields to display + @return a string of the formatted dict + """ + raise NotImplementedError() + + def iter_fields(self, obj): + for attribute_name in dir(obj): + if attribute_name.startswith('_'): + continue + attribute = getattr(obj, attribute_name) + if not isinstance(attribute, types.MethodType): + yield attribute_name, attribute + + def to_dict(self, obj, condition=None): + fields_iterator = obj.iter_fields() if hasattr(obj, 'iter_fields') else self.iter_fields(obj) + d = dict((k, v) for k, v in fields_iterator) + if condition is not None: + if not condition.is_valid(d): + d = None + return d diff --git a/weboob/tools/application/formatters/multiline.py b/weboob/tools/application/formatters/multiline.py new file mode 100644 index 00000000..ec86232c --- /dev/null +++ b/weboob/tools/application/formatters/multiline.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2010 Christophe Benz +# +# 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 +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +from .iformatter import IFormatter + + +__all__ = ['MultilineFormatter'] + + +class MultilineFormatter(IFormatter): + def __init__(self, key_value_separator=u': ', after_item=u'\n', display_keys=True): + self.key_value_separator = key_value_separator + self.after_item = after_item + self.display_keys = display_keys + + def format_dict(self, item, selected_fields): + result_str = u'\n'.join(u'%s%s' % ((u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), + unicode(item[k])) for k in selected_fields) + self.after_item + return result_str diff --git a/weboob/tools/application/formatters/simple.py b/weboob/tools/application/formatters/simple.py index 806ec2cb..8a033b13 100644 --- a/weboob/tools/application/formatters/simple.py +++ b/weboob/tools/application/formatters/simple.py @@ -16,38 +16,19 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +from .iformatter import IFormatter + + __all__ = ['SimpleFormatter'] -class SimpleFormatter(object): - @classmethod - def format(cls, obj, selected_fields=None, where_condition=None): - def iter_fields(obj): - import types - for attribute_name in dir(obj): - if attribute_name.startswith('_'): - continue - attribute = getattr(obj, attribute_name) - if not isinstance(attribute, types.MethodType): - yield attribute_name, attribute +class SimpleFormatter(IFormatter): + def __init__(self, field_separator=u'\t', key_value_separator=u'=', display_keys=True): + self.field_separator = field_separator + self.key_value_separator = key_value_separator + self.display_keys = display_keys - if hasattr(obj, 'iter_fields'): - fields_iterator = obj.iter_fields() - else: - fields_iterator = iter_fields(obj) - - d = dict((k, v) for k, v in fields_iterator) - - if where_condition is not None: - if not where_condition.is_valid(d): - d = None - - if not d: - return None - - if selected_fields is None: - result = u'\n'.join(u'%s: %s' % (k, unicode(d[k])) for k in sorted(d)) + u'\n' - else: - result = u'\t'.join(unicode(d[k]) for k in selected_fields if d[k] is not None) - - return result + def format_dict(self, item, selected_fields): + result_str = self.field_separator.join(u'%s%s' % ((u'%s%s' % ( + k, self.key_value_separator) if self.display_keys else ''), unicode(item[k])) for k in selected_fields) + return result_str diff --git a/weboob/tools/application/results.py b/weboob/tools/application/results.py index 367bfe2d..893d9d76 100644 --- a/weboob/tools/application/results.py +++ b/weboob/tools/application/results.py @@ -16,7 +16,7 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -__all__ = ['Results', 'WhereCondition', 'WhereConditionException'] +__all__ = ['Results', 'ResultsCondition', 'ResultsConditionException'] class Results(object): @@ -67,13 +67,13 @@ class Results(object): return iter(self._groups) -class WhereCondition(object): - def __init__(self, where_condition_str): - where_condition_str.replace('OR', 'or') - where_condition_str.replace('AND', 'and') - where_condition_str.replace('NOT', 'not') +class ResultsCondition(object): + def __init__(self, condition_str): + condition_str.replace('OR', 'or') + condition_str.replace('AND', 'and') + condition_str.replace('NOT', 'not') or_list = [] - for _or in where_condition_str.split('or'): + for _or in condition_str.split('or'): and_dict = {} for _and in _or.split('and'): if '!=' in _and: @@ -82,13 +82,13 @@ class WhereCondition(object): elif '=' in _and: k, v = _and.split('=') else: - raise WhereConditionException(u'Could not find = or != operator in sub-expression "%s"' % _and) + raise ResultsConditionException(u'Could not find = or != operator in sub-expression "%s"' % _and) and_dict[k] = v or_list.append(and_dict) - self.where_condition = or_list + self.condition = or_list def is_valid(self, d): - for _or in self.where_condition: + for _or in self.condition: for k, v in _or.iteritems(): if k.endswith('!'): k = k[:-1] @@ -103,9 +103,9 @@ class WhereCondition(object): if d[k] != v: return False else: - raise WhereConditionException(u'Field "%s" is not valid.' % k) + raise ResultsConditionException(u'Field "%s" is not valid.' % k) return True -class WhereConditionException(Exception): +class ResultsConditionException(Exception): pass