fix: use ResultsCondition in BackendsCall instead of hack in Weboob (refs #372)

This commit is contained in:
Romain Bignon 2010-11-14 21:46:48 +01:00
commit c644aacd29
4 changed files with 38 additions and 25 deletions

View file

@ -26,7 +26,7 @@ from weboob.tools.misc import get_backtrace
from weboob.tools.log import getLogger from weboob.tools.log import getLogger
__all__ = ['BackendsCall', 'CallErrors'] __all__ = ['BackendsCall', 'CallErrors', 'IResultsCondition', 'ResultsConditionError']
class CallErrors(Exception): class CallErrors(Exception):
@ -37,18 +37,28 @@ class CallErrors(Exception):
def __iter__(self): def __iter__(self):
return self.errors.__iter__() return self.errors.__iter__()
class IResultsCondition(object):
def is_valid(self, obj):
raise NotImplementedError()
class ResultsConditionError(Exception):
pass
class BackendsCall(object): 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 backends list of backends to call.
@param function backends' method name, or callable object @param condition a IResultsCondition object. Can be None.
@param args, kwargs arguments given to called functions @param function backends' method name, or callable object.
@param args, kwargs arguments given to called functions.
""" """
self.logger = getLogger('bcall') self.logger = getLogger('bcall')
# Store if a backend is finished # Store if a backend is finished
self.backends = {} self.backends = {}
for backend in backends: for backend in backends:
self.backends[backend.name] = False self.backends[backend.name] = False
# Condition
self.condition = condition
# Global mutex on object # Global mutex on object
self.mutex = RLock() self.mutex = RLock()
# Event set when every backends have give their data # Event set when every backends have give their data
@ -78,6 +88,8 @@ class BackendsCall(object):
def _store_result(self, backend, result): def _store_result(self, backend, result):
with self.mutex: with self.mutex:
if isinstance(result, CapBaseObject): if isinstance(result, CapBaseObject):
if self.condition and not self.condition.is_valid(result):
return
result.backend = backend.name result.backend = backend.name
self.responses.append((backend, result)) self.responses.append((backend, result))
self.response_event.set() self.response_event.set()

View file

@ -180,7 +180,7 @@ class Weboob(object):
@param backends list of backends to iterate on @param backends list of backends to iterate on
@param caps iterate on backends with this caps @param caps iterate on backends with this caps
@param condition a condition to validate to keep the result @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 = self.backend_instances.values()
_backends = kwargs.pop('backends', None) _backends = kwargs.pop('backends', None)
@ -206,13 +206,12 @@ class Weboob(object):
caps = kwargs.pop('caps') caps = kwargs.pop('caps')
backends = [backend for backend in backends if backend.has_caps(caps)] backends = [backend for backend in backends if backend.has_caps(caps)]
condition = kwargs.pop('condition', None) condition = kwargs.pop('condition', None)
for backend, result in BackendsCall(backends, function, *args, **kwargs):
d = dict(result.iter_fields()) # The return value MUST BE the BackendsCall instance. Please never iterate
if condition: # here on this object, because caller might want to use other methods, like
if condition.is_valid(d): # wait() on callback_thread().
yield backend, result # Thanks a lot.
else: return BackendsCall(backends, condition, function, *args, **kwargs)
yield backend, result
def schedule(self, interval, function, *args): def schedule(self, interval, function, *args):
return self.scheduler.schedule(interval, function, *args) return self.scheduler.schedule(interval, function, *args)

View file

@ -34,7 +34,7 @@ from weboob.tools.value import Value, ValueBool, ValueFloat, ValueInt
from .base import BackendNotFound, BaseApplication from .base import BackendNotFound, BaseApplication
from .formatters.load import FormattersLoader, FormatterLoadError from .formatters.load import FormattersLoader, FormatterLoadError
from .results import ResultsCondition, ResultsConditionException from .results import ResultsCondition, ResultsConditionError
__all__ = ['ReplApplication', 'NotEnoughArguments'] __all__ = ['ReplApplication', 'NotEnoughArguments']
@ -375,6 +375,8 @@ class ReplApplication(Cmd, BaseApplication):
if not msg: if not msg:
msg = 'website is unavailable.' msg = 'website is unavailable.'
print >>sys.stderr, u'Error(%s): %s' % (backend.name, msg) 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): 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'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)) 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: else:
try: try:
self.condition = ResultsCondition(line) self.condition = ResultsCondition(line)
except ResultsConditionException, e: except ResultsConditionError, e:
print e print >>sys.stderr, '%s' % e
else: else:
if self.condition is None: if self.condition is None:
print 'No condition is set.' print 'No condition is set.'

View file

@ -16,10 +16,13 @@
# 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__ = ['ResultsCondition', 'ResultsConditionException'] from weboob.core.bcall import IResultsCondition, ResultsConditionError
class ResultsCondition(object): __all__ = ['ResultsCondition', 'ResultsConditionError']
class ResultsCondition(IResultsCondition):
condition_str = None condition_str = None
def __init__(self, condition_str): def __init__(self, condition_str):
@ -36,13 +39,14 @@ class ResultsCondition(object):
elif '=' in _and: elif '=' in _and:
k, v = _and.split('=') k, v = _and.split('=')
else: 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() and_dict[k.strip()] = v.strip()
or_list.append(and_dict) or_list.append(and_dict)
self.condition = or_list self.condition = or_list
self.condition_str = condition_str 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 _or in self.condition:
for k, v in _or.iteritems(): for k, v in _or.iteritems():
if k.endswith('!'): if k.endswith('!'):
@ -58,7 +62,7 @@ class ResultsCondition(object):
if d[k] != v: if d[k] != v:
return False return False
else: else:
raise ResultsConditionException(u'Field "%s" is not valid.' % k) raise ResultsConditionError(u'Field "%s" is not valid.' % k)
return True return True
def __str__(self): def __str__(self):
@ -66,7 +70,3 @@ class ResultsCondition(object):
def __unicode__(self): def __unicode__(self):
return self.condition_str return self.condition_str
class ResultsConditionException(Exception):
pass