set up Configurator stuff
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1858 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
87d60fd415
commit
7ea411b923
4 changed files with 156 additions and 26 deletions
|
@ -26,8 +26,92 @@ This means that all calls to services (like crawler, transporter, ...)
|
||||||
return a deferred that must be supplied with a callback method (and in
|
return a deferred that must be supplied with a callback method (and in
|
||||||
most cases also an errback method).
|
most cases also an errback method).
|
||||||
|
|
||||||
>>> from loops.agent.core import Agent
|
>>> from loops.agent import core
|
||||||
>>> agent = Agent()
|
>>> agent = core.Agent()
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Management
|
||||||
|
========================
|
||||||
|
|
||||||
|
Functionality
|
||||||
|
|
||||||
|
- Storage of configuration parameters
|
||||||
|
- Interface to the browser-based user interface that allows the
|
||||||
|
editing of configuration parameters
|
||||||
|
|
||||||
|
All configuration parameters are always accessible via the ``config``
|
||||||
|
attribute of the agent object.
|
||||||
|
|
||||||
|
>>> config = agent.config
|
||||||
|
|
||||||
|
This already provides all needed sections (transport, crawl, ui), so
|
||||||
|
we can directly put information into these sections by loading a
|
||||||
|
string with the corresponding assignment.
|
||||||
|
|
||||||
|
>>> config.load('transport.url = "http://loops.cy55.de"')
|
||||||
|
>>> config.transport.url
|
||||||
|
'http://loops.cy55.de'
|
||||||
|
|
||||||
|
This setting may also contain indexed access; thus we can model
|
||||||
|
configuration parameters with multiple instances (like crawling
|
||||||
|
jobs).
|
||||||
|
|
||||||
|
>>> config.load('''
|
||||||
|
... crawl[0].type = "filesystem"
|
||||||
|
... crawl[0].directory = "documents/projects"
|
||||||
|
... ''')
|
||||||
|
>>> config.crawl[0].type
|
||||||
|
'filesystem'
|
||||||
|
>>> config.crawl[0].directory
|
||||||
|
'documents/projects'
|
||||||
|
|
||||||
|
Subsections are created automatically when they are first accessed.
|
||||||
|
|
||||||
|
>>> config.load('ui.web.port = 8081')
|
||||||
|
>>> config.ui.web.port
|
||||||
|
8081
|
||||||
|
|
||||||
|
The ``setdefault()`` method allows to retrieve a value and set
|
||||||
|
it with a default if not found, in one statement.
|
||||||
|
|
||||||
|
>>> config.ui.web.setdefault('port', 8080)
|
||||||
|
8081
|
||||||
|
>>> config.transport.setdefault('user', 'loops')
|
||||||
|
'loops'
|
||||||
|
|
||||||
|
We can output a configuration in a form that is ready for loading
|
||||||
|
just by converting it to a string representation.
|
||||||
|
|
||||||
|
>>> print config
|
||||||
|
crawl[0].directory = 'documents/projects'
|
||||||
|
crawl[0].type = 'filesystem'
|
||||||
|
transport.url = 'http://loops.cy55.de'
|
||||||
|
transport.user = 'loops'
|
||||||
|
ui.web.port = 8081
|
||||||
|
|
||||||
|
The configuration may also be saved to a file -
|
||||||
|
for testing purposes let's use the loops.agent package directory
|
||||||
|
for storage; normally it would be stored in the users home directory.
|
||||||
|
|
||||||
|
>>> import os
|
||||||
|
>>> os.environ['HOME'] = os.path.dirname(core.__file__)
|
||||||
|
|
||||||
|
>>> config.save()
|
||||||
|
|
||||||
|
>>> fn = config.getDefaultConfigFile()
|
||||||
|
>>> fn
|
||||||
|
'....loops.agent.cfg'
|
||||||
|
|
||||||
|
>>> print open(fn).read()
|
||||||
|
crawl[0].directory = 'documents/projects'
|
||||||
|
crawl[0].type = 'filesystem'
|
||||||
|
transport.url = 'http://loops.cy55.de'
|
||||||
|
transport.user = 'loops'
|
||||||
|
ui.web.port = 8081
|
||||||
|
|
||||||
|
Cleaning up up...
|
||||||
|
|
||||||
|
>>> os.unlink(fn)
|
||||||
|
|
||||||
|
|
||||||
Scheduling
|
Scheduling
|
||||||
|
@ -185,16 +269,6 @@ Configuration (per install/update job)
|
||||||
- package names
|
- package names
|
||||||
|
|
||||||
|
|
||||||
Configuration Management
|
|
||||||
========================
|
|
||||||
|
|
||||||
Functionality
|
|
||||||
|
|
||||||
- Storage of configuration parameters
|
|
||||||
- Interface to the browser-based user interface that allows the
|
|
||||||
editing of configuration parameters
|
|
||||||
|
|
||||||
|
|
||||||
Browser-based User Interface
|
Browser-based User Interface
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ Management of agent configuration.
|
||||||
$Id$
|
$Id$
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from loops.agent.interfaces import IConfigurator
|
from loops.agent.interfaces import IConfigurator
|
||||||
|
|
||||||
|
@ -30,21 +31,75 @@ class Configurator(object):
|
||||||
|
|
||||||
implements(IConfigurator)
|
implements(IConfigurator)
|
||||||
|
|
||||||
def loadConfiguration(self):
|
def __init__(self, *sections, **kw):
|
||||||
pass
|
for s in sections:
|
||||||
|
setattr(self, s, ConfigSection())
|
||||||
|
self.filename = kw.get('filename')
|
||||||
|
|
||||||
def addConfigOption(self, key, value):
|
def load(self, p=None, filename=None):
|
||||||
setattr(self, key, value)
|
if p is None:
|
||||||
|
fn = self.getConfigFile(filename)
|
||||||
|
if fn is not None:
|
||||||
|
f = open(fn, 'r')
|
||||||
|
p = f.read()
|
||||||
|
f.close()
|
||||||
|
if p is None:
|
||||||
|
return
|
||||||
|
exec p in self.__dict__
|
||||||
|
|
||||||
def getConfigOption(self, key, value):
|
def save(self, filename=None):
|
||||||
return getattr(self, key, None)
|
fn = self.getConfigFile(filename)
|
||||||
|
if fn is None:
|
||||||
|
fn = self.getDefaultConfigFile()
|
||||||
|
if fn is not None:
|
||||||
|
f = open(fn, 'w')
|
||||||
|
f.write(repr(self))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
result = []
|
||||||
|
for name, value in self.__dict__.items():
|
||||||
|
if isinstance(value, ConfigSection):
|
||||||
|
value.collect(name, result)
|
||||||
|
return '\n'.join(sorted(result))
|
||||||
|
|
||||||
|
def getConfigFile(self, filename=None):
|
||||||
|
if filename is not None:
|
||||||
|
self.filename = filename
|
||||||
|
if self.filename is None:
|
||||||
|
fn = self.getDefaultConfigFile()
|
||||||
|
if os.path.isfile(fn):
|
||||||
|
self.filename = fn
|
||||||
|
return self.filename
|
||||||
|
|
||||||
|
def getDefaultConfigFile(self):
|
||||||
|
return os.path.join(os.path.expanduser('~'), '.loops.agent.cfg')
|
||||||
|
|
||||||
|
|
||||||
conf = Configurator()
|
class ConfigSection(list):
|
||||||
|
|
||||||
# this is just for convenience during the development phase,
|
def __getattr__(self, attr):
|
||||||
# thus we can retrieve the port easily via ``conf.ui.web.port``
|
value = ConfigSection()
|
||||||
conf.addConfigOption('ui', Configurator())
|
setattr(self, attr, value)
|
||||||
conf.ui.addConfigOption('web', Configurator())
|
return value
|
||||||
conf.ui.web.addConfigOption('port', 10095)
|
|
||||||
|
def __getitem__(self, idx):
|
||||||
|
while idx >= len(self):
|
||||||
|
self.append(ConfigSection())
|
||||||
|
return list.__getitem__(self, idx)
|
||||||
|
|
||||||
|
def setdefault(self, attr, value):
|
||||||
|
if attr not in self.__dict__:
|
||||||
|
setattr(self, attr, value)
|
||||||
|
return value
|
||||||
|
return getattr(self, attr)
|
||||||
|
|
||||||
|
def collect(self, ident, result):
|
||||||
|
for idx, element in enumerate(self):
|
||||||
|
element.collect('%s[%i]' % (ident, idx), result)
|
||||||
|
for name, value in self.__dict__.items():
|
||||||
|
if isinstance(value, ConfigSection):
|
||||||
|
value.collect('%s.%s' % (ident, name), result)
|
||||||
|
elif isinstance(value, (str, int)):
|
||||||
|
result.append('%s.%s = %s' % (ident, name, repr(value)))
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Agent(object):
|
||||||
implements(IAgent)
|
implements(IAgent)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
configurator = self.configurator = Configurator()
|
config = self.config = Configurator('ui', 'crawl', 'transport')
|
||||||
configurator.loadConfiguration()
|
config.load()
|
||||||
self.scheduler = Scheduler()
|
self.scheduler = Scheduler()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
#
|
#
|
||||||
# Run with ``trial2.4 tests.py`` to execute the twisted unit tests.
|
# Run with ``trial2.4 tests.py`` to execute the twisted unit tests.
|
||||||
# Run with ``python tests.py`` to execute the doctests.
|
# Run with ``python tests.py`` to execute the doctests.
|
||||||
|
|
Loading…
Add table
Reference in a new issue