added util.config (experimental); clean-up of doctests
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2103 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
e8db37544f
commit
caf8b60b83
9 changed files with 255 additions and 7 deletions
|
@ -171,8 +171,8 @@ The default configurator uses attribute annotations for retrieving view
|
|||
properties; that means that there could be a form somewhere to edit those
|
||||
properties and store them in the content object's annotations.
|
||||
|
||||
>>> from zope.app.annotation.interfaces import IAttributeAnnotatable, IAnnotations
|
||||
>>> from zope.app.annotation.attribute import AttributeAnnotations
|
||||
>>> from zope.annotation.interfaces import IAttributeAnnotatable, IAnnotations
|
||||
>>> from zope.annotation.attribute import AttributeAnnotations
|
||||
>>> component.provideAdapter(AttributeAnnotations, (SomeObject,), IAnnotations)
|
||||
|
||||
The configurator is called automatically from the controller if there is
|
||||
|
|
|
@ -166,5 +166,5 @@ Macros / renderers
|
|||
|
||||
>>> fieldRenderers = form.fieldRenderers
|
||||
>>> sorted(fieldRenderers.keys())
|
||||
[u'field', u'field_spacer', u'fields', u'form', u'input_dropdown',
|
||||
[u'field', u'field_spacer', u'fields', u'form', u'input_date', u'input_dropdown',
|
||||
u'input_fileupload', u'input_password', u'input_textarea', u'input_textline']
|
||||
|
|
|
@ -33,6 +33,9 @@ from cybertools.composer.schema.schema import Schema
|
|||
|
||||
|
||||
class SchemaFactory(object):
|
||||
""" Creates a cybertools.composer schema from an
|
||||
interface (a zope.schema schema).
|
||||
"""
|
||||
|
||||
implements(ISchemaFactory)
|
||||
adapts(Interface)
|
||||
|
|
|
@ -95,6 +95,7 @@ fieldTypes = SimpleVocabulary((
|
|||
fieldRenderer='field_spacer', storeData=False),
|
||||
))
|
||||
|
||||
# TODO: move this to organize.service...
|
||||
standardFieldNames = SimpleVocabulary((
|
||||
SimpleTerm('', '', 'Not selected'),
|
||||
SimpleTerm('lastName', 'lastName', 'Last name'),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import unittest, doctest
|
||||
from zope.app.testing.functional import FunctionalTestCase
|
||||
from zope.app.testing import setup
|
||||
from zope.testbrowser import Browser
|
||||
from zope.testbrowser.testing import Browser
|
||||
|
||||
from zope.app import component, intid, zapi
|
||||
|
||||
|
@ -23,7 +23,7 @@ class BrowserTest(FunctionalTestCase):
|
|||
key = default.registrationManager.addRegistration(reg)
|
||||
default.registrationManager[key].status = component.interfaces.registration.ActiveStatus
|
||||
|
||||
def test(self):
|
||||
def test(self):
|
||||
browser = Browser()
|
||||
browser.handleErrors = False
|
||||
browser.addHeader('Authorization', 'Basic mgr:mgrpw')
|
||||
|
@ -38,7 +38,7 @@ class BrowserTest(FunctionalTestCase):
|
|||
button = browser.getControl('Apply')
|
||||
button.click()
|
||||
self.assert_(browser.isHtml)
|
||||
|
||||
|
||||
|
||||
def test_suite():
|
||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
|
|
143
util/config.py
Normal file
143
util/config.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
#
|
||||
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
Formulating configuration options.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
from zope.interface import implements
|
||||
|
||||
|
||||
_not_found = object()
|
||||
|
||||
|
||||
class Configurator(dict):
|
||||
|
||||
filenamePrefix = 'cybertools'
|
||||
|
||||
def __init__(self, *sections, **kw):
|
||||
for s in sections:
|
||||
setattr(self, s, ConfigSection(s))
|
||||
self.filename = kw.get('filename')
|
||||
|
||||
def __getitem__(self, key):
|
||||
item = getattr(self, key, _not_found)
|
||||
if item is _not_found:
|
||||
item = ConfigSection(key)
|
||||
setattr(self, key, item)
|
||||
return item
|
||||
|
||||
def load(self, p=None, filename=None):
|
||||
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
|
||||
|
||||
def save(self, filename=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('~'),
|
||||
'.%s.cfg' % self.filenamePrefix)
|
||||
|
||||
|
||||
class ConfigSection(list):
|
||||
|
||||
__name__ = '???'
|
||||
|
||||
def __init__(self, name=None):
|
||||
if name is not None:
|
||||
self.__name__ = name
|
||||
|
||||
def __getattr__(self, attr):
|
||||
value = ConfigSection(attr)
|
||||
setattr(self, attr, value)
|
||||
return value
|
||||
|
||||
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 items(self):
|
||||
for name, value in self.__dict__.items():
|
||||
if isinstance(value, (str, int)):
|
||||
yield name, value
|
||||
|
||||
def __call__(self, *args, **kw):
|
||||
for s in args:
|
||||
if isinstance(s, ConfigSection):
|
||||
# should we update an existing entry?
|
||||
#old = getattr(self, s.__name__, None)
|
||||
#if old is not None: # this would have to be done recursively
|
||||
# old.__dict__.update(s.__dict__)
|
||||
# for elem in s:
|
||||
# old.append(elem)
|
||||
#else:
|
||||
# or just keep the new one?
|
||||
setattr(self, s.__name__, s)
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
return self
|
||||
|
||||
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 name != '__name__' and isinstance(value, (str, int)):
|
||||
result.append('%s.%s = %s' % (ident, name, repr(value)))
|
||||
|
100
util/config.txt
Normal file
100
util/config.txt
Normal file
|
@ -0,0 +1,100 @@
|
|||
=============================
|
||||
Setting Configuration Options
|
||||
=============================
|
||||
|
||||
$Id$
|
||||
|
||||
>>> from cybertools import util
|
||||
>>> from cybertools.util.config import Configurator
|
||||
>>> config = Configurator()
|
||||
|
||||
>>> config.load('transport.serverURL = "http://demo.cy55.de"')
|
||||
>>> config.transport.serverURL
|
||||
'http://demo.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('userName', 'demo')
|
||||
'demo'
|
||||
|
||||
>>> sorted(config.transport.items())
|
||||
[('__name__', 'transport'), ('serverURL', 'http://demo.cy55.de'),
|
||||
('userName', 'demo')]
|
||||
|
||||
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.serverURL = 'http://demo.cy55.de'
|
||||
transport.userName = 'demo'
|
||||
ui.web.port = 8081
|
||||
|
||||
The configuration may also be saved to a file -
|
||||
for testing purposes let's use the cybertools.util package directory
|
||||
for storage; normally it would be stored in the users home directory.
|
||||
|
||||
>>> import os
|
||||
>>> os.environ['HOME'] = os.path.dirname(util.__file__)
|
||||
|
||||
>>> config.save()
|
||||
|
||||
>>> fn = config.getDefaultConfigFile()
|
||||
>>> fn
|
||||
'....cybertools.cfg'
|
||||
|
||||
>>> print open(fn).read()
|
||||
crawl[0].directory = 'documents/projects'
|
||||
crawl[0].type = 'filesystem'
|
||||
transport.serverURL = 'http://demo.cy55.de'
|
||||
transport.userName = 'demo'
|
||||
ui.web.port = 8081
|
||||
|
||||
The simplified syntax
|
||||
---------------------
|
||||
|
||||
>>> config.load('''
|
||||
... ui(
|
||||
... web(
|
||||
... port=11080,
|
||||
... ))
|
||||
... crawl[1](
|
||||
... type='outlook',
|
||||
... folder='inbox',
|
||||
... )
|
||||
... ''')
|
||||
>>> config.ui.web.port
|
||||
11080
|
||||
>>> config.crawl[1].type
|
||||
'outlook'
|
||||
|
||||
>>> #print config
|
||||
|
||||
Cleaning up
|
||||
-----------
|
||||
|
||||
>>> os.unlink(fn)
|
||||
|
|
@ -20,6 +20,7 @@ def test_suite():
|
|||
#doctest.DocTestSuite(cybertools.util.property, optionflags=flags),
|
||||
doctest.DocFileSuite('adapter.txt', optionflags=flags),
|
||||
doctest.DocFileSuite('aop.txt', optionflags=flags),
|
||||
doctest.DocFileSuite('config.txt', optionflags=flags),
|
||||
doctest.DocFileSuite('defer.txt', optionflags=flags),
|
||||
doctest.DocFileSuite('format.txt', optionflags=flags),
|
||||
doctest.DocFileSuite('property.txt', optionflags=flags),
|
||||
|
|
|
@ -22,7 +22,7 @@ Generic template base class.
|
|||
$Id$
|
||||
"""
|
||||
|
||||
from zope.app.traversing.adapters import DefaultTraversable
|
||||
from zope.traversing.adapters import DefaultTraversable
|
||||
from zope import component
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue