split module loader in ModulesLoader and BackendsConfig classes
This commit is contained in:
parent
d03bd47981
commit
56fea28640
8 changed files with 141 additions and 49 deletions
|
|
@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
class Backend:
|
class Backend(object):
|
||||||
# Module name.
|
# Module name.
|
||||||
NAME = None
|
NAME = None
|
||||||
# Name of the maintainer of this module.
|
# Name of the maintainer of this module.
|
||||||
|
|
@ -29,6 +29,8 @@ class Backend:
|
||||||
EMAIL = '<unspecified>'
|
EMAIL = '<unspecified>'
|
||||||
# Version of module (for information only).
|
# Version of module (for information only).
|
||||||
VERSION = '<unspecified>'
|
VERSION = '<unspecified>'
|
||||||
|
# Description
|
||||||
|
DESCRIPTION = '<unspecified>'
|
||||||
# License of this module.
|
# License of this module.
|
||||||
LICENSE = '<unspecified>'
|
LICENSE = '<unspecified>'
|
||||||
# Configuration required for this module. # Values must be ConfigField
|
# Configuration required for this module. # Values must be ConfigField
|
||||||
|
|
|
||||||
|
|
@ -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 AccountNotFound(Exception): pass
|
||||||
|
|
||||||
class Account(object):
|
class Account(object):
|
||||||
|
|
@ -69,7 +71,7 @@ class Operation(object):
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
|
|
||||||
|
|
||||||
class ICapBank:
|
class ICapBank(ICap):
|
||||||
def iter_accounts(self):
|
def iter_accounts(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from .cap import ICap
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
def __init__(self, thread_id, _id, title, sender, date=None, reply_id=u'', content=u'', signature=u''):
|
def __init__(self, thread_id, _id, title, sender, date=None, reply_id=u'', content=u'', signature=u''):
|
||||||
self.thread_id = unicode(thread_id)
|
self.thread_id = unicode(thread_id)
|
||||||
|
|
@ -80,7 +82,7 @@ class Message:
|
||||||
self.id, self.title, self.date, self.sender)
|
self.id, self.title, self.date, self.sender)
|
||||||
return result.encode('utf-8')
|
return result.encode('utf-8')
|
||||||
|
|
||||||
class ICapMessages:
|
class ICapMessages(ICap):
|
||||||
def iter_new_messages(self):
|
def iter_new_messages(self):
|
||||||
"""
|
"""
|
||||||
Iterates on new messages from last time this function has been called.
|
Iterates on new messages from last time this function has been called.
|
||||||
|
|
@ -95,6 +97,6 @@ class ICapMessages:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
class ICapMessagesReply:
|
class ICapMessagesReply(ICap):
|
||||||
def post_reply(self, to, message):
|
def post_reply(self, to, message):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
from datetime import time
|
from datetime import time
|
||||||
|
|
||||||
class ICapTravel:
|
from .cap import ICap
|
||||||
|
|
||||||
|
class ICapTravel(ICap):
|
||||||
def iter_station_search(self, pattern):
|
def iter_station_search(self, pattern):
|
||||||
"""
|
"""
|
||||||
Iterates on search results of stations.
|
Iterates on search results of stations.
|
||||||
|
|
|
||||||
|
|
@ -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 = {}
|
snapshots = {}
|
||||||
|
|
||||||
def take_snapshot(self, name, collection):
|
def take_snapshot(self, name, collection):
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ import re
|
||||||
import os
|
import os
|
||||||
from ConfigParser import SafeConfigParser
|
from ConfigParser import SafeConfigParser
|
||||||
from logging import warning, debug
|
from logging import warning, debug
|
||||||
from types import ClassType
|
|
||||||
|
|
||||||
import weboob.backends as backends
|
import weboob.backends as backends
|
||||||
from weboob.backend import Backend
|
from weboob.backend import Backend
|
||||||
|
from weboob.capabilities.cap import ICap
|
||||||
|
from weboob.tools.misc import itersubclasses
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
def __init__(self, name, module):
|
def __init__(self, name, module):
|
||||||
|
|
@ -34,7 +35,7 @@ class Module:
|
||||||
self.klass = None
|
self.klass = None
|
||||||
for attrname in dir(self.module):
|
for attrname in dir(self.module):
|
||||||
attr = getattr(self.module, attrname)
|
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
|
self.klass = attr
|
||||||
|
|
||||||
if not self.klass:
|
if not self.klass:
|
||||||
|
|
@ -43,6 +44,26 @@ class Module:
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.klass.NAME
|
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):
|
def has_caps(self, *caps):
|
||||||
for c in caps:
|
for c in caps:
|
||||||
if issubclass(self.klass, c):
|
if issubclass(self.klass, c):
|
||||||
|
|
@ -52,6 +73,36 @@ class Module:
|
||||||
def create_backend(self, weboob, name, config):
|
def create_backend(self, weboob, name, config):
|
||||||
return self.klass(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:
|
class ModulesLoader:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.modules = {}
|
self.modules = {}
|
||||||
|
|
@ -75,38 +126,3 @@ class ModulesLoader:
|
||||||
return
|
return
|
||||||
self.modules[module.get_name()] = module
|
self.modules[module.get_name()] = module
|
||||||
debug('Loaded module %s (%s)' % (name, module.module.__name__))
|
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
|
|
||||||
|
|
|
||||||
|
|
@ -20,34 +20,62 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from weboob.modules import ModulesLoader
|
from weboob.modules import ModulesLoader, BackendsConfig
|
||||||
from weboob.scheduler import Scheduler
|
from weboob.scheduler import Scheduler
|
||||||
|
|
||||||
class Weboob:
|
class Weboob:
|
||||||
WORKDIR = os.path.join(os.path.expanduser('~'), '.weboob')
|
WORKDIR = os.path.join(os.path.expanduser('~'), '.weboob')
|
||||||
BACKENDS_FILENAME = 'backends'
|
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.app_name = app_name
|
||||||
self.workdir = workdir
|
self.workdir = workdir
|
||||||
self.backends = {}
|
self.backends = {}
|
||||||
|
|
||||||
|
# Scheduler
|
||||||
if scheduler is None:
|
if scheduler is None:
|
||||||
scheduler = Scheduler()
|
scheduler = Scheduler()
|
||||||
self.scheduler = scheduler
|
self.scheduler = scheduler
|
||||||
|
|
||||||
|
# Modules loader
|
||||||
self.modules_loader = ModulesLoader()
|
self.modules_loader = ModulesLoader()
|
||||||
self.modules_loader.load()
|
self.modules_loader.load()
|
||||||
|
|
||||||
def get_backends_filename(self):
|
# Backends config
|
||||||
return os.path.join(self.workdir, self.BACKENDS_FILENAME)
|
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):
|
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
|
return self.backends
|
||||||
|
|
||||||
def load_modules(self, caps=None, names=None):
|
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
|
return self.backends
|
||||||
|
|
||||||
def iter_backends(self, caps=None):
|
def iter_backends(self, caps=None):
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,42 @@ def local2utc(d):
|
||||||
d = d.astimezone(tz.tzutc())
|
d = d.astimezone(tz.tzutc())
|
||||||
return d
|
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue