[Checkins] SVN: cipher.configstore/trunk/ - Implemented dependency support among stores at the same level of object.

Stephen Richter cvs-admin at zope.org
Sat Oct 6 18:27:54 UTC 2012


Log message for revision 127926:
  - Implemented dependency support among stores at the same level of object.
  - Increased test coverage to over 90%.
  
  Get ready for release.
  
  Thanks goes to Jens who discretely pointed out to me that I might have 
  had checked out the code with the wrong URL, which I had. I used svn:// 
  instead of svn+ssh://.
  
  

Changed:
  U   cipher.configstore/trunk/CHANGES.txt
  U   cipher.configstore/trunk/setup.py
  U   cipher.configstore/trunk/src/cipher/configstore/configstore.py
  U   cipher.configstore/trunk/src/cipher/configstore/configstore.txt

-=-
Modified: cipher.configstore/trunk/CHANGES.txt
===================================================================
--- cipher.configstore/trunk/CHANGES.txt	2012-10-06 08:33:16 UTC (rev 127925)
+++ cipher.configstore/trunk/CHANGES.txt	2012-10-06 18:27:50 UTC (rev 127926)
@@ -2,12 +2,14 @@
 CHANGES
 =======
 
-1.2.3 (unreleased)
+1.3.0 (2012-10-05)
 ------------------
 
-- Nothing changed yet.
+- Implemented dependency support among stores at the same level of object.
 
+- Increased test coverage to over 90%.
 
+
 1.2.2 (2012-08-30)
 ------------------
 

Modified: cipher.configstore/trunk/setup.py
===================================================================
--- cipher.configstore/trunk/setup.py	2012-10-06 08:33:16 UTC (rev 127925)
+++ cipher.configstore/trunk/setup.py	2012-10-06 18:27:50 UTC (rev 127926)
@@ -23,7 +23,7 @@
 
 setup(
     name='cipher.configstore',
-    version='1.2.3.dev0',
+    version='1.3.0',
     url="http://pypi.python.org/pypi/cipher.configstore/",
     author='Zope Foundation and Contributors',
     author_email='zope-dev at zope.org',

Modified: cipher.configstore/trunk/src/cipher/configstore/configstore.py
===================================================================
--- cipher.configstore/trunk/src/cipher/configstore/configstore.py	2012-10-06 08:33:16 UTC (rev 127925)
+++ cipher.configstore/trunk/src/cipher/configstore/configstore.py	2012-10-06 18:27:50 UTC (rev 127926)
@@ -45,6 +45,42 @@
     return dateutil.parser.parse(s).time()
 
 
+class StoreSorter(object):
+
+    NEW = 'new'
+    OPEN = 'open'
+    CLOSED = 'closed'
+
+    def __init__(self, stores):
+        self.stores = dict([(s.name, s) for s in stores])
+
+    def addStore(self, name, store):
+        if name in self.callstack:
+            raise interfaces.CyclicDependencyError(name)
+        self.callstack.append(name)
+        if name not in self.status:
+            self.status[name] = self.NEW
+        if self.status[name] == self.NEW:
+            self.status[name] = self.OPEN
+            for depName in store.dependencies:
+                if (depName not in self.status or
+                    self.status[depName] is not self.CLOSED):
+                    self.addStore(depName, self.stores[depName])
+            self.ordered.append(store)
+            self.status[name] = self.CLOSED
+        self.callstack.pop(-1)
+
+    def __call__(self):
+        self.status = {}
+        self.callstack = []
+        self.ordered = []
+
+        for name, store in self.stores.items():
+            self.addStore(name, store)
+
+        return self.ordered
+
+
 class ConfigurationStore(object):
     zope.interface.implements(interfaces.IConfigurationStore)
     listValueSeparator = ', '
@@ -55,6 +91,12 @@
     container = None
     root = None
 
+    dependencies = ()
+
+    @property
+    def name(self):
+        return '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
+
     def __init__(self, context, schema=None, section=None):
         if self.schema is None:
             self.schema = schema or zope.component.adaptedBy(self.__class__)[0]
@@ -144,6 +186,7 @@
         changed_fields = self._load(config)
         stores = zope.component.subscribers(
             (self.context,), interfaces.IConfigurationStore)
+        stores = StoreSorter(stores)()
         for store in stores:
             if not isinstance(store, self.__class__):
                 store.root = self.root

Modified: cipher.configstore/trunk/src/cipher/configstore/configstore.txt
===================================================================
--- cipher.configstore/trunk/src/cipher/configstore/configstore.txt	2012-10-06 08:33:16 UTC (rev 127925)
+++ cipher.configstore/trunk/src/cipher/configstore/configstore.txt	2012-10-06 18:27:50 UTC (rev 127926)
@@ -15,7 +15,7 @@
 
   >>> class Person(object):
   ...     zope.interface.implements(IPerson)
-  ...     def __init__(self, fn, ln):
+  ...     def __init__(self, fn=u'', ln=u''):
   ...         self.firstName = fn
   ...         self.lastName = ln
   ...         self.nickname = None
@@ -274,3 +274,188 @@
   >>> import os
   >>> os.unlink(conf_fn)
 
+External Stores
+---------------
+
+External Configuration Stores allow configuration to be written to another
+file only leaving a reference in the current one. This allows complex
+configuration to be split up into multiple files.
+
+  >>> import tempfile, os
+  >>> conf_dir = tempfile.mkdtemp()
+  >>> os.mkdir(os.path.join(conf_dir, 'test'))
+
+  >>> class ExternalPersonStore(configstore.ExternalConfigurationStore):
+  ...     schema = IPerson
+  ...
+  ...     def get_config_dir(self):
+  ...         return conf_dir
+  ...
+  ...     def get_site(self):
+  ...         return type('FakeSite', (), {'__name__': 'test'})()
+  ...
+  ...     def get_filename(self):
+  ...         return 'person.ini'
+
+  >>> store = ExternalPersonStore(stephan)
+
+We can now dump the configuration to a file:
+
+  >>> config = store.dump()
+  >>> config
+  <ConfigParser.RawConfigParser instance at ...>
+
+  >>> conf_fn = os.path.join(conf_dir, 'test', 'main.ini')
+  >>> config.write(open(conf_fn, 'w'))
+  >>> print open(conf_fn, 'r').read()
+  [IPerson]
+  config-path = test/person.ini
+
+  >>> ext_conf_fn = os.path.join(conf_dir, 'test', 'person.ini')
+  >>> print open(ext_conf_fn, 'r').read()
+  [general]
+  firstName = Stephan
+  lastName = Richter
+  nickname =
+  <BLANKLINE>
+  [address]
+  zip = 01754
+  <BLANKLINE>
+  [number:home]
+  name = home
+  number = 555-111-2222
+  <BLANKLINE>
+  [number:work]
+  name = work
+  number = 555-333-4444
+
+Let's now load the configuration again:
+
+  >>> stephan2 = Person()
+  >>> store = ExternalPersonStore(stephan2)
+  >>> store.load(config)
+
+  >>> stephan2.firstName
+  u'Stephan'
+  >>> stephan2.lastName
+  u'Richter'
+  >>> stephan2.nickname
+  u''
+
+Field Support
+-------------
+
+The configuration store supports several field types by default.
+
+  >>> store = configstore.ConfigurationStore(Person(), schema=IPerson)
+
+Time
+~~~~
+
+  >>> import datetime
+  >>> field = None
+
+  >>> store.dump_type_Time(datetime.time(3, 47), field)
+  '03:47'
+  >>> store.load_type_Time('03:47', field)
+  datetime.time(3, 47)
+
+  >>> store.dump_type_Time(datetime.time(15, 47), field)
+  '15:47'
+  >>> store.load_type_Time('15:47', field)
+  datetime.time(15, 47)
+
+Timedelta
+~~~~~~~~~
+
+  >>> field = None
+
+  >>> store.dump_type_Timedelta(datetime.timedelta(seconds=3661), field)
+  '1:01:01'
+  >>> store.load_type_Timedelta('1:01:01', field)
+  datetime.timedelta(0, 3661)
+
+  >>> store.load_type_Timedelta('', field) is None
+  True
+
+Text
+~~~~
+
+  >>> field = None
+
+  >>> store.dump_type_Text('foo\n\nbar', field)
+  'foo\n<BLANKLINE>\nbar'
+
+  >>> store.load_type_Text('foo\n<BLANKLINE>\nbar', field)
+  'foo\n\nbar'
+
+  >>> store.dump_type_Text(None, field)
+  ''
+
+Choice
+~~~~~~
+
+  >>> import zope.schema
+  >>> field = zope.schema.Choice(
+  ...     vocabulary=zope.schema.vocabulary.SimpleVocabulary([
+  ...                    zope.schema.vocabulary.SimpleTerm(1, 'one'),
+  ...                    zope.schema.vocabulary.SimpleTerm(2, 'two')
+  ...                    ]))
+
+  >>> store.dump_type_Choice(1, field)
+  'one'
+  >>> store.load_type_Choice('one', field)
+  1
+
+  >>> store.dump_type_Choice(None, field)
+  ''
+  >>> store.load_type_Choice('', field) is None
+  True
+
+List
+~~~~
+
+  >>> field = None
+
+  >>> store.dump_type_List(['one', 'two', 'three'], field)
+  'one, two, three'
+
+  >>> store.load_type_List('one, two, three', field)
+  ['one', 'two', 'three']
+
+  >>> store.dump_type_List(None, field)
+  ''
+  >>> store.load_type_List('', field)
+  []
+
+Tuple
+~~~~~
+
+  >>> field = None
+
+  >>> store.dump_type_Tuple(('one', 'two', 'three'), field)
+  'one, two, three'
+
+  >>> store.load_type_Tuple('one, two, three', field)
+  ('one', 'two', 'three')
+
+  >>> store.dump_type_Tuple(None, field)
+  ''
+  >>> store.load_type_Tuple('', field)
+  ()
+
+Set
+~~~
+
+  >>> field = None
+
+  >>> store.dump_type_Set(set(['one', 'two', 'three']), field)
+  'three, two, one'
+
+  >>> store.load_type_Set('one, two, three', field)
+  set(['three', 'two', 'one'])
+
+  >>> store.dump_type_Set(None, field)
+  ''
+  >>> store.load_type_Set('', field)
+  set([])



More information about the checkins mailing list