diff --git a/composer/base.py b/composer/base.py
index 21a8ae8..fd255f5 100644
--- a/composer/base.py
+++ b/composer/base.py
@@ -17,7 +17,7 @@
#
"""
-Basic classes for a complex template structures.
+Basic classes for complex template structures.
$Id$
"""
@@ -43,14 +43,18 @@ class Compound(Component):
implements(ICompound)
+ componentStorage = Jeep
+
def __init__(self):
- self.parts = Jeep()
+ self.parts = self.componentStorage()
class Template(object):
implements(ITemplate)
- def __init__(self):
- self.components = Jeep()
+ componentStorage = Jeep
+
+ def __init__(self):
+ self.components = self.componentStorage()
diff --git a/composer/instance.py b/composer/instance.py
index 8eebcab..4a39c04 100644
--- a/composer/instance.py
+++ b/composer/instance.py
@@ -31,6 +31,9 @@ class Instance(object):
implements(IInstance)
+ templateStorage = dict
+ templateAttributeName = '__ctc_templates__'
+
aspect = 'composer.default'
def __init__(self, context):
@@ -38,11 +41,12 @@ class Instance(object):
self.instances = []
def setTemplate(self, template):
- templates = getattr(self.context, '__templates__', {})
+ templates = getattr(self.context,
+ self.templateAttributeName, self.templateStorage())
templates.setdefault(self.aspect, template)
- self.context.__templates__ = templates
+ setattr(self.context, self.templateAttributeName, templates)
def getTemplate(self):
- templates = getattr(self.context, '__templates__', {})
+ templates = getattr(self.context, self.templateAttributeName, {})
return templates.get(self.aspect, None)
template = property(getTemplate, setTemplate)
diff --git a/organize/interfaces.py b/organize/interfaces.py
index 6bc6d99..048fb6f 100644
--- a/organize/interfaces.py
+++ b/organize/interfaces.py
@@ -122,6 +122,13 @@ class ITask(Interface):
# services
+class IServiceManager(Interface):
+ """ A manager or container for a set of services.
+ """
+
+ services = Attribute('A collection of services managed by this object.')
+
+
class IServiceGroup(Interface):
""" A group of related services or a general service definition,
e.g. a regular bus service or a series of trainings.
diff --git a/organize/service.py b/organize/service.py
index 7bafba7..e929937 100644
--- a/organize/service.py
+++ b/organize/service.py
@@ -17,21 +17,20 @@
#
"""
-Service instance classes.
+Service management classes.
$Id$
"""
from zope.interface import implements
-from cybertools.organize.interfaces import IService
-from cybertools.organize.interfaces import IScheduledService
+from cybertools.organize.interfaces import IServiceManager
+from cybertools.organize.interfaces import IService, IScheduledService
-class Registration(object):
+class ServiceManager(object):
- def __init__(self, client):
- self.client = client
+ implements(IServiceManager)
class Service(object):
@@ -60,3 +59,9 @@ class ScheduledService(Service):
implements(IScheduledService)
+
+class Registration(object):
+
+ def __init__(self, client):
+ self.client = client
+
diff --git a/stateful/definition.py b/stateful/definition.py
index e8dfa25..123411c 100644
--- a/stateful/definition.py
+++ b/stateful/definition.py
@@ -65,6 +65,9 @@ class StatesDefinition(object):
return [ self._transitions[t] for t in self._states[state].transitions ]
+def registerStatesDefinition(id, definition):
+ statesDefinitions[id] = definition
+
statesDefinitions = {
'default': StatesDefinition(),
}
diff --git a/xedit/configure.zcml b/xedit/configure.zcml
index 5490be7..e5cf3ad 100644
--- a/xedit/configure.zcml
+++ b/xedit/configure.zcml
@@ -16,17 +16,17 @@
-
-
+
-
+ -->
.py file with an implementation and a
+corresponding .txt file with a description that can be run as a
+doctest.
+
+The doctests can be run by issuing
+
+ python cybertools/zutil/tests.py
+
+in the directory above the cybertools directory or via
+
+ bin/test -vs cybertools.zutil
+
+from within a Zope 3 instance that contains the cybertools package in
+its python path.
diff --git a/zutil/__init__.py b/zutil/__init__.py
new file mode 100644
index 0000000..38314f3
--- /dev/null
+++ b/zutil/__init__.py
@@ -0,0 +1,3 @@
+"""
+$Id$
+"""
diff --git a/zutil/jeep.py b/zutil/jeep.py
new file mode 100644
index 0000000..c8b20cc
--- /dev/null
+++ b/zutil/jeep.py
@@ -0,0 +1,123 @@
+#
+# 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
+#
+
+"""
+A general purpose (thus 'Jeep') class that provides most of the interfaces
+of sequences and dictionaries and in addition allows attribute access to
+the dictionary entries.
+
+$Id$
+"""
+
+from persistent.list import PersistentList
+from BTrees.OOBTree import OOBTree
+
+_notfound = object()
+_nodefault = object()
+
+
+class Jeep(object):
+
+ _attributes = ('_sequence', '_mapping')
+
+ def __init__(self, seq=[]):
+ self._sequence = PersistentList()
+ self._mapping = OOBTree()
+ for item in seq:
+ attr, value = item
+ setattr(self, attr, value)
+
+ def __len__(self):
+ return len(self._sequence)
+
+ def __iter__(self):
+ for key in self._sequence:
+ yield self[key]
+
+ def __getattr__(self, attr, default=_nodefault):
+ value = self._mapping.get(attr, _notfound)
+ if value is _notfound:
+ if default is _nodefault:
+ raise AttributeError(attr)
+ else:
+ return default
+ return value
+
+ def __setattr__(self, attr, value):
+ if attr in self._attributes:
+ object.__setattr__(self, attr, value)
+ else:
+ if getattr(self, attr, _notfound) is _notfound:
+ self._sequence.append(attr)
+ self._mapping[attr] = value
+
+ def __delattr__(self, attr):
+ del self._sequence[self.index(attr)]
+ del self._mapping[attr]
+
+ def __getitem__(self, key):
+ if type(key) in (int, long):
+ return getattr(self, self._sequence[key])
+ value = getattr(self, key, _notfound)
+ if value is _notfound:
+ raise KeyError(key)
+ return value
+
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
+
+ def __delitem__(self, key):
+ delattr(self, key)
+
+ def __contains__(self, key):
+ return getattr(self, key, _notfound) is not _notfound
+
+ def keys(self):
+ return [key for key in self._sequence]
+
+ def values(self):
+ return list(self)
+
+ def items(self):
+ return [(k, self[k]) for k in self._sequence]
+
+ def get(self, key, default=None):
+ return getattr(self, key, default)
+
+ def index(self, key):
+ return self._sequence.index(key)
+
+ def append(self, obj):
+ self.insert(len(self), obj)
+
+ def insert(self, idx, obj):
+ key = getattr(obj, '__name__', getattr(obj, 'name', _notfound))
+ if key is _notfound:
+ raise AttributeError("No name attribute present")
+ if key in self:
+ raise ValueError("Object already present")
+ self._sequence.insert(idx, key)
+ self._mapping[key] = obj
+
+ def pop(self, key=-1):
+ value = self[key]
+ if type(key) in (int, long):
+ key = self._sequence[key]
+ delattr(self, key)
+ return value
+
diff --git a/zutil/jeep.txt b/zutil/jeep.txt
new file mode 100644
index 0000000..456780e
--- /dev/null
+++ b/zutil/jeep.txt
@@ -0,0 +1,141 @@
+==============================
+Jeep - a General Purpose Class
+==============================
+
+$Id$
+
+ >>> from cybertools.zutil.jeep import Jeep
+ >>> jeep = Jeep()
+
+ >>> jeep.first = 'first value'
+ >>> jeep.second = 'second value'
+
+ >>> jeep.first
+ 'first value'
+
+In addition to the usual access via dot notation all attributes can be
+accessed via dictionary notation:
+
+The third type of interface provided by Jeep objects is the sequence or
+iterator interface. Converting a jeep object to a list iterates over its
+values (that is different from the dictionary behaviour, but is what
+you want usually; use the ``.keys()`` method to get at the keys, see below):
+
+ >>> list(jeep)
+ ['first value', 'second value']
+
+Direct index access to certain entries gives the corresponding value,
+not the key:
+
+ >>> jeep[1]
+ 'second value'
+
+Changing Jeep Objects
+---------------------
+
+Assignment by dictionary or attribute access appends the newly assigned
+attribute:
+
+ >>> jeep['third'] = 'third value'
+ >>> jeep.third
+ 'third value'
+
+ >>> list(jeep)
+ ['first value', 'second value', 'third value']
+
+Assigning a new value to an already existing attribute does not change the
+order but only changes the attribute's value
+
+ >>> jeep.second = 'new second value'
+ >>> list(jeep)
+ ['first value', 'new second value', 'third value']
+ >>> jeep[1]
+ 'new second value'
+
+More Dictionary Methods
+-----------------------
+
+ >>> jeep.keys()
+ ['first', 'second', 'third']
+
+ >>> jeep.values()
+ ['first value', 'new second value', 'third value']
+
+ >>> jeep.items()
+ [('first', 'first value'), ('second', 'new second value'), ('third', 'third value')]
+
+ >>> jeep.get('second')
+ 'new second value'
+ >>> jeep.get('fourth', 'default')
+ 'default'
+ >>> jeep.get('fourth') is None
+ True
+ >>> jeep['fourth']
+ Traceback (most recent call last):
+ ...
+ KeyError: 'fourth'
+
+ >>> dict(jeep)
+ {'second': 'new second value', 'third': 'third value', 'first': 'first value'}
+
+More Methods and Operators
+--------------------------
+
+ >>> 'third' in jeep
+ True
+
+ >>> jeep.pop()
+ 'third value'
+ >>> len(jeep)
+ 2
+
+ >>> 'third' in jeep
+ False
+
+ >>> jeep.index('second')
+ 1
+ >>> jeep.index('third')
+ Traceback (most recent call last):
+ ...
+ ValueError: ...not in list
+
+Sequence Additions with Named Objects
+-------------------------------------
+
+Objects that have a ``__name__`` attribute can be appended
+to a Jeep object as the dictionary key can be obtained from these attribute.
+
+ >>> class Term(object):
+ ... def __init__(self, token, title=None, value=None):
+ ... self.__name__ = self.token = token
+ ... self.title = title or token
+ ... self.value = value or title or token
+
+ >>> t1 = Term('term1', 'title 1')
+ >>> jeep.append(t1)
+ >>> jeep.keys()
+ ['first', 'second', 'term1']
+ >>> jeep.term1.title
+ 'title 1'
+
+ >>> jeep.insert(1, Term('term2', 'title 2'))
+ >>> jeep.keys()
+ ['first', 'term2', 'second', 'term1']
+ >>> jeep[1].title
+ 'title 2'
+
+Inserting or appending an object with a name that's already present raises
+an exception:
+
+ >>> jeep.append(t1)
+ Traceback (most recent call last):
+ ...
+ ValueError: ...already present
+
+Constructors
+------------
+
+ >>> jeep2 = Jeep((('f', '1st'), ('s', '2nd'), ('t', '3rd')))
+ >>> list(jeep2)
+ ['1st', '2nd', '3rd']
+
diff --git a/zutil/tests.py b/zutil/tests.py
new file mode 100755
index 0000000..2107bfb
--- /dev/null
+++ b/zutil/tests.py
@@ -0,0 +1,25 @@
+# $Id$
+
+import unittest, doctest
+from zope.testing.doctestunit import DocFileSuite
+
+import cybertools.zutil.jeep
+
+
+class Test(unittest.TestCase):
+ "Basic tests for modules in the util package."
+
+ def testBasicStuff(self):
+ pass
+
+
+def test_suite():
+ flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+ return unittest.TestSuite((
+ #unittest.makeSuite(Test), # we don't need this
+ #doctest.DocTestSuite(cybertools.zutil.property, optionflags=flags),
+ DocFileSuite('jeep.txt', optionflags=flags),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')