split module loader in ModulesLoader and BackendsConfig classes

This commit is contained in:
Romain Bignon 2010-04-06 21:17:51 +02:00
commit 56fea28640
8 changed files with 141 additions and 49 deletions

View file

@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import re
class Backend:
class Backend(object):
# Module name.
NAME = None
# Name of the maintainer of this module.
@ -29,6 +29,8 @@ class Backend:
EMAIL = '<unspecified>'
# Version of module (for information only).
VERSION = '<unspecified>'
# Description
DESCRIPTION = '<unspecified>'
# License of this module.
LICENSE = '<unspecified>'
# Configuration required for this module. # Values must be ConfigField

View file

@ -18,6 +18,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""
from .cap import ICap
class AccountNotFound(Exception): pass
class Account(object):
@ -69,7 +71,7 @@ class Operation(object):
self.amount = amount
class ICapBank:
class ICapBank(ICap):
def iter_accounts(self):
raise NotImplementedError()

View file

@ -21,6 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import datetime
import time
from .cap import ICap
class Message:
def __init__(self, thread_id, _id, title, sender, date=None, reply_id=u'', content=u'', signature=u''):
self.thread_id = unicode(thread_id)
@ -80,7 +82,7 @@ class Message:
self.id, self.title, self.date, self.sender)
return result.encode('utf-8')
class ICapMessages:
class ICapMessages(ICap):
def iter_new_messages(self):
"""
Iterates on new messages from last time this function has been called.
@ -95,6 +97,6 @@ class ICapMessages:
"""
raise NotImplementedError()
class ICapMessagesReply:
class ICapMessagesReply(ICap):
def post_reply(self, to, message):
raise NotImplementedError()

View file

@ -20,7 +20,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from datetime import time
class ICapTravel:
from .cap import ICap
class ICapTravel(ICap):
def iter_station_search(self, pattern):
"""
Iterates on search results of stations.

View file

@ -18,8 +18,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""
from .cap import ICap
class ICapUpdatable:
class ICapUpdatable(ICap):
snapshots = {}
def take_snapshot(self, name, collection):

View file

@ -22,10 +22,11 @@ import re
import os
from ConfigParser import SafeConfigParser
from logging import warning, debug
from types import ClassType
import weboob.backends as backends
from weboob.backend import Backend
from weboob.capabilities.cap import ICap
from weboob.tools.misc import itersubclasses
class Module:
def __init__(self, name, module):
@ -34,7 +35,7 @@ class Module:
self.klass = None
for attrname in dir(self.module):
attr = getattr(self.module, attrname)
if isinstance(attr, ClassType) and issubclass(attr, Backend) and attr != Backend:
if isinstance(attr, type) and issubclass(attr, Backend) and attr != Backend:
self.klass = attr
if not self.klass:
@ -43,6 +44,26 @@ class Module:
def get_name(self):
return self.klass.NAME
def get_maintainer(self):
return '%s <%s>' % (self.klass.MAINTAINER, self.klass.EMAIL)
def get_version(self):
return self.klass.VERSION
def get_description(self):
return self.klass.DESCRIPTION
def get_license(self):
return self.klass.LICENSE
def get_config(self):
return self.klass.CONFIG
def iter_caps(self):
for subclass in itersubclasses(self.klass):
if issubclass(subclass, ICap):
yield subclass
def has_caps(self, *caps):
for c in caps:
if issubclass(self.klass, c):
@ -52,6 +73,36 @@ class Module:
def create_backend(self, weboob, name, config):
return self.klass(weboob, name, config)
class BackendsConfig:
def __init__(self, confpath):
self.confpath = confpath
def iter_backends(self):
config = SafeConfigParser()
config.read(self.confpath)
for name in config.sections():
params = dict(config.items(name))
try:
yield name, params.pop('_type'), params
except KeyError:
warning("Missing field '_type' for backend '%s'", name)
continue
def add_backend(self, name, _type, params):
config = SafeConfigParser()
config.read(self.confpath)
config.add_section(name)
config.set(name, '_type', _type)
for key, value in params.iteritems():
config.set(name, key, value)
config.save(self.confpath)
def remove_backend(self, name):
config = SafeConfigParser()
config.read(self.confpath)
config.remove_section(name)
config.save(self.confpath)
class ModulesLoader:
def __init__(self):
self.modules = {}
@ -75,38 +126,3 @@ class ModulesLoader:
return
self.modules[module.get_name()] = module
debug('Loaded module %s (%s)' % (name, module.module.__name__))
def load_backends(self, confpath, caps, names):
config = SafeConfigParser()
config.read(confpath)
backends = {}
for name in config.sections():
params = dict(config.items(name))
try:
module = self.modules[params['_type']]
except KeyError:
warning('Unable to find module %s', name)
continue
# Check conditions
if (not caps is None and not module.has_caps(caps)) or \
(not names is None and not module.name in name):
continue
try:
backends[name] = module.create_backend(self, name, params)
except Exception, e:
warning('Unable to load %s backend: %s' % (name, e))
return backends
def load_modules_as_backends(self, caps, names):
backends = {}
for name, module in self.modules.iteritems():
if (caps is None or module.has_caps(caps)) and \
(names is None or module.name in names):
try:
backends[module.name] = module.create_backend(self, module.name, {})
except Exception, e:
warning('Unable to load %s backend: %s' % (name, e))
return backends

View file

@ -20,34 +20,62 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import os
from weboob.modules import ModulesLoader
from weboob.modules import ModulesLoader, BackendsConfig
from weboob.scheduler import Scheduler
class Weboob:
WORKDIR = os.path.join(os.path.expanduser('~'), '.weboob')
BACKENDS_FILENAME = 'backends'
def __init__(self, app_name, workdir=WORKDIR, scheduler=None):
def __init__(self, app_name, workdir=WORKDIR, backends_filename=None, scheduler=None):
self.app_name = app_name
self.workdir = workdir
self.backends = {}
# Scheduler
if scheduler is None:
scheduler = Scheduler()
self.scheduler = scheduler
# Modules loader
self.modules_loader = ModulesLoader()
self.modules_loader.load()
def get_backends_filename(self):
return os.path.join(self.workdir, self.BACKENDS_FILENAME)
# Backends config
if not backends_filename:
backends_filename = os.path.join(self.workdir, self.BACKENDS_FILENAME)
elif not backends_filename.startswith('/'):
backends_filename = os.path.join(self.workdir, backends_filename)
self.backends_config = BackendsConfig(backends_filename)
def load_backends(self, caps=None, names=None):
self.backends.update(self.modules_loader.load_backends(self.get_backends_filename(), caps, names))
for name, _type, params in self.backends_config.iter_backends():
try:
module = self.modules_loader.modules[_type]
except KeyError:
warning('Unable to find module %s', name)
continue
# Check conditions
if (not caps is None and not module.has_caps(caps)) or \
(not names is None and not module.name in name):
continue
try:
self.backends[name] = module.create_backend(self, name, params)
except Exception, e:
warning('Unable to load %s backend: %s' % (name, e))
return self.backends
def load_modules(self, caps=None, names=None):
self.backends.update(self.modules_loader.load_modules_as_backends(caps, names))
for name, module in self.modules_loader.modules.iteritems():
if (caps is None or module.has_caps(caps)) and \
(names is None or module.name in names):
try:
self.backends[module.name] = module.create_backend(self, module.name, {})
except Exception, e:
warning('Unable to load %s backend: %s' % (name, e))
return self.backends
def iter_backends(self, caps=None):

View file

@ -44,3 +44,42 @@ def local2utc(d):
d = d.astimezone(tz.tzutc())
return d
def itersubclasses(cls, _seen=None):
"""
itersubclasses(cls)
Generator over all subclasses of a given class, in depth first order.
>>> list(itersubclasses(int)) == [bool]
True
>>> class A(object): pass
>>> class B(A): pass
>>> class C(A): pass
>>> class D(B,C): pass
>>> class E(D): pass
>>>
>>> for cls in itersubclasses(A):
... print(cls.__name__)
B
D
E
C
>>> # get ALL (new-style) classes currently defined
>>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS
['type', ...'tuple', ...]
"""
if not isinstance(cls, type):
raise TypeError('itersubclasses must be called with '
'new-style classes, not %.100r' % cls)
if _seen is None: _seen = set()
try:
subs = cls.__subclasses__()
except TypeError: # fails only when cls is type
subs = cls.__subclasses__(cls)
for sub in subs:
if sub not in _seen:
_seen.add(sub)
yield sub
for sub in itersubclasses(sub, _seen):
yield sub