From c644aacd29f28e4ba9b662cac257e7de6b085fbe Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Sun, 14 Nov 2010 21:46:48 +0100 Subject: [PATCH] fix: use ResultsCondition in BackendsCall instead of hack in Weboob (refs #372) --- weboob/core/bcall.py | 22 +++++++++++++++++----- weboob/core/ouiboube.py | 15 +++++++-------- weboob/tools/application/repl.py | 8 +++++--- weboob/tools/application/results.py | 18 +++++++++--------- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/weboob/core/bcall.py b/weboob/core/bcall.py index 6cf70040..a534524a 100644 --- a/weboob/core/bcall.py +++ b/weboob/core/bcall.py @@ -26,7 +26,7 @@ from weboob.tools.misc import get_backtrace from weboob.tools.log import getLogger -__all__ = ['BackendsCall', 'CallErrors'] +__all__ = ['BackendsCall', 'CallErrors', 'IResultsCondition', 'ResultsConditionError'] class CallErrors(Exception): @@ -37,18 +37,28 @@ class CallErrors(Exception): def __iter__(self): return self.errors.__iter__() +class IResultsCondition(object): + def is_valid(self, obj): + raise NotImplementedError() + +class ResultsConditionError(Exception): + pass + class BackendsCall(object): - def __init__(self, backends, function, *args, **kwargs): + def __init__(self, backends, condition, function, *args, **kwargs): """ - @param backends list of backends to call - @param function backends' method name, or callable object - @param args, kwargs arguments given to called functions + @param backends list of backends to call. + @param condition a IResultsCondition object. Can be None. + @param function backends' method name, or callable object. + @param args, kwargs arguments given to called functions. """ self.logger = getLogger('bcall') # Store if a backend is finished self.backends = {} for backend in backends: self.backends[backend.name] = False + # Condition + self.condition = condition # Global mutex on object self.mutex = RLock() # Event set when every backends have give their data @@ -78,6 +88,8 @@ class BackendsCall(object): def _store_result(self, backend, result): with self.mutex: if isinstance(result, CapBaseObject): + if self.condition and not self.condition.is_valid(result): + return result.backend = backend.name self.responses.append((backend, result)) self.response_event.set() diff --git a/weboob/core/ouiboube.py b/weboob/core/ouiboube.py index 858f2a6d..5d5b6c33 100644 --- a/weboob/core/ouiboube.py +++ b/weboob/core/ouiboube.py @@ -180,7 +180,7 @@ class Weboob(object): @param backends list of backends to iterate on @param caps iterate on backends with this caps @param condition a condition to validate to keep the result - @return an iterator of results + @return the BackendsCall object (iteratable) """ backends = self.backend_instances.values() _backends = kwargs.pop('backends', None) @@ -206,13 +206,12 @@ class Weboob(object): caps = kwargs.pop('caps') backends = [backend for backend in backends if backend.has_caps(caps)] condition = kwargs.pop('condition', None) - for backend, result in BackendsCall(backends, function, *args, **kwargs): - d = dict(result.iter_fields()) - if condition: - if condition.is_valid(d): - yield backend, result - else: - yield backend, result + + # The return value MUST BE the BackendsCall instance. Please never iterate + # here on this object, because caller might want to use other methods, like + # wait() on callback_thread(). + # Thanks a lot. + return BackendsCall(backends, condition, function, *args, **kwargs) def schedule(self, interval, function, *args): return self.scheduler.schedule(interval, function, *args) diff --git a/weboob/tools/application/repl.py b/weboob/tools/application/repl.py index 7faf825f..e0632d87 100644 --- a/weboob/tools/application/repl.py +++ b/weboob/tools/application/repl.py @@ -34,7 +34,7 @@ from weboob.tools.value import Value, ValueBool, ValueFloat, ValueInt from .base import BackendNotFound, BaseApplication from .formatters.load import FormattersLoader, FormatterLoadError -from .results import ResultsCondition, ResultsConditionException +from .results import ResultsCondition, ResultsConditionError __all__ = ['ReplApplication', 'NotEnoughArguments'] @@ -375,6 +375,8 @@ class ReplApplication(Cmd, BaseApplication): if not msg: msg = 'website is unavailable.' print >>sys.stderr, u'Error(%s): %s' % (backend.name, msg) + elif isinstance(error, ResultsConditionError): + print >>sys.stderr, u'Error(%s): condition error: %s' % (backend.name, error) elif isinstance(error, NotImplementedError): print >>sys.stderr, u'Error(%s): this feature is not supported yet by this backend.' % backend.name print >>sys.stderr, u' %s To help the maintainer of this backend implement this feature,' % (' ' * len(backend.name)) @@ -787,8 +789,8 @@ class ReplApplication(Cmd, BaseApplication): else: try: self.condition = ResultsCondition(line) - except ResultsConditionException, e: - print e + except ResultsConditionError, e: + print >>sys.stderr, '%s' % e else: if self.condition is None: print 'No condition is set.' diff --git a/weboob/tools/application/results.py b/weboob/tools/application/results.py index a5987a25..572f3874 100644 --- a/weboob/tools/application/results.py +++ b/weboob/tools/application/results.py @@ -16,10 +16,13 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -__all__ = ['ResultsCondition', 'ResultsConditionException'] +from weboob.core.bcall import IResultsCondition, ResultsConditionError -class ResultsCondition(object): +__all__ = ['ResultsCondition', 'ResultsConditionError'] + + +class ResultsCondition(IResultsCondition): condition_str = None def __init__(self, condition_str): @@ -36,13 +39,14 @@ class ResultsCondition(object): elif '=' in _and: k, v = _and.split('=') else: - raise ResultsConditionException(u'Could not find = or != operator in sub-expression "%s"' % _and) + raise ResultsConditionError(u'Could not find = or != operator in sub-expression "%s"' % _and) and_dict[k.strip()] = v.strip() or_list.append(and_dict) self.condition = or_list self.condition_str = condition_str - def is_valid(self, d): + def is_valid(self, obj): + d = dict(obj.iter_fields()) for _or in self.condition: for k, v in _or.iteritems(): if k.endswith('!'): @@ -58,7 +62,7 @@ class ResultsCondition(object): if d[k] != v: return False else: - raise ResultsConditionException(u'Field "%s" is not valid.' % k) + raise ResultsConditionError(u'Field "%s" is not valid.' % k) return True def __str__(self): @@ -66,7 +70,3 @@ class ResultsCondition(object): def __unicode__(self): return self.condition_str - - -class ResultsConditionException(Exception): - pass