[Zconfig] SVN: ZConfig/trunk/ add minimal implementation of
schema-less parsing;
Fred L. Drake, Jr.
fdrake at gmail.com
Thu Jun 21 13:01:40 EDT 2007
Log message for revision 76904:
add minimal implementation of schema-less parsing;
this is mostly intended for use from zc.buildout recipes, and contains
a number of limitations (see ZConfig/schemaless.txt)
Changed:
A ZConfig/trunk/ZConfig/schemaless.py
A ZConfig/trunk/ZConfig/schemaless.txt
A ZConfig/trunk/ZConfig/tests/test_schemaless.py
U ZConfig/trunk/setup.py
-=-
Added: ZConfig/trunk/ZConfig/schemaless.py
===================================================================
--- ZConfig/trunk/ZConfig/schemaless.py (rev 0)
+++ ZConfig/trunk/ZConfig/schemaless.py 2007-06-21 17:01:39 UTC (rev 76904)
@@ -0,0 +1,114 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""\
+Support for working with ZConfig data without a schema.
+
+"""
+__docformat__ = "reStructuredText"
+
+import ZConfig.cfgparser
+
+
+def loadConfigFile(file, url=None):
+ c = Context()
+ Parser(Resource(file, url), c).parse(c.top)
+ return c.top
+
+
+class Resource:
+
+ def __init__(self, file, url=''):
+ self.file, self.url = file, url
+
+
+class Section(dict):
+
+ imports = ()
+
+ def __init__(self, type='', name='', data=None, sections=None):
+ dict.__init__(self)
+ if data:
+ self.update(data)
+ self.sections = sections or []
+ self.type, self.name = type, name
+
+ def addValue(self, key, value, *args):
+ if key in self:
+ self[key].append(value)
+ else:
+ self[key] = [value]
+
+ def __str__(self, pre=''):
+ result = []
+
+ if self.imports:
+ for pkgname in self.imports:
+ result.append('%import ' + pkgname)
+ result.append('')
+
+ if self.type:
+ if self.name:
+ start = '%s<%s %s>' % (pre, self.type, self.name)
+ else:
+ start = '%s<%s>' % (pre, self.type)
+ result.append(start)
+ pre += ' '
+
+ for name, values in sorted(self.items()):
+ for value in values:
+ result.append('%s%s %s' % (pre, name, value))
+
+ if self.sections and self:
+ result.append('')
+
+ for section in self.sections:
+ result.append(section.__str__(pre))
+
+ if self.type:
+ pre = pre[:-2]
+ result.append('%s</%s>' % (pre, self.type))
+ result.append('')
+
+ result = '\n'.join(result).rstrip()
+ if not pre:
+ result += '\n'
+ return result
+
+
+class Context:
+
+ def __init__(self):
+ self.top = Section()
+ self.sections = []
+
+ def startSection(self, container, type, name):
+ newsec = Section(type, name)
+ container.sections.append(newsec)
+ return newsec
+
+ def endSection(self, container, type, name, newsect):
+ pass
+
+ def importSchemaComponent(self, pkgname):
+ if pkgname not in self.top.imports:
+ self.top.imports += (pkgname, )
+
+ def includeConfiguration(self, section, newurl, defines):
+ raise NotImplementedError('includes are not supported')
+
+
+class Parser(ZConfig.cfgparser.ZConfigParser):
+
+ def handle_define(self, section, rest):
+ raise NotImplementedError('defines are not supported')
Property changes on: ZConfig/trunk/ZConfig/schemaless.py
___________________________________________________________________
Name: svn:mime-type
+ text/x-python
Name: svn:eol-style
+ native
Added: ZConfig/trunk/ZConfig/schemaless.txt
===================================================================
--- ZConfig/trunk/ZConfig/schemaless.txt (rev 0)
+++ ZConfig/trunk/ZConfig/schemaless.txt 2007-06-21 17:01:39 UTC (rev 76904)
@@ -0,0 +1,290 @@
+=================================
+Using ZConfig data without schema
+=================================
+
+Sometimes it's useful to use ZConfig configuration data without a
+schema. This is most interesting when assembling a configuration from
+fragments, as some buildout recipes do. This is not recommended for
+general application use.
+
+The ``ZConfig.schemaless`` module provides some support for working
+without schema. Something things are not (currently) supported,
+including the %define and %include directives. The %import directive
+is supported.
+
+This module provides basic support for loading configuration,
+inspecting and modifying it, and re-serializing the result.
+
+ >>> from ZConfig import schemaless
+
+There is a single function which loads configuration data from a file
+open for reading. Let's take a look at this, and what it returns::
+
+ >>> config_text = '''
+ ...
+ ... some-key some-value
+ ...
+ ... some-key another-value
+ ...
+ ... <section>
+ ... key1 value1.1
+ ... key1 value1.2
+ ... key2 value2
+ ...
+ ... <deeper>
+ ... another key
+ ... another value
+ ... </deeper>
+ ... </section>
+ ...
+ ... another-key whee!
+ ...
+ ...
+ ... <another named>
+ ... nothing here
+ ... </another>
+ ...
+ ... '''
+
+ >>> import StringIO
+
+ >>> config = schemaless.loadConfigFile(StringIO.StringIO(config_text))
+
+The `config` object is a mapping from top-level keys to lists of
+values::
+
+ >>> config["some-key"]
+ ['some-value', 'another-value']
+
+ >>> config["another-key"]
+ ['whee!']
+
+ >>> config["no-such-key-in-the-config"]
+ Traceback (most recent call last):
+ KeyError: 'no-such-key-in-the-config'
+
+ >>> sorted(config)
+ ['another-key', 'some-key']
+
+There is also a ``sections`` attribute that lists child sections::
+
+ >>> len(config.sections)
+ 2
+
+Let's take a look at one of the sections. Like the top-level
+configuration, the section maps keys
+
+ >>> section = config.sections[0]
+
+ >>> section["key1"]
+ ['value1.1', 'value1.2']
+
+ >>> section["key2"]
+ ['value2']
+
+ >>> section["no-such-key-in-the-config"]
+ Traceback (most recent call last):
+ KeyError: 'no-such-key-in-the-config'
+
+ >>> sorted(section)
+ ['key1', 'key2']
+
+Child sections are again available via the ``sections`` attribute::
+
+ >>> len(section.sections)
+ 1
+
+In addition, the section has ``type`` and ``name`` attributes that
+record the type and name of the section as ZConfig understands them::
+
+ >>> section.type
+ 'section'
+
+ >>> print section.name
+ None
+
+Let's look at the named section from our example, so we can see the
+name::
+
+ >>> section = config.sections[1]
+ >>> section.type
+ 'another'
+ >>> section.name
+ 'named'
+
+We can also mutate the configuration, adding new keys and values as
+desired::
+
+ >>> config["new-key"] = ["new-value-1", "new-value-2"]
+ >>> config["some-key"].append("third-value")
+
+New sections can also be added::
+
+ >>> section = schemaless.Section("sectiontype", "my-name")
+ >>> section["key"] = ["value"]
+ >>> config.sections.insert(1, section)
+
+The configuration can be re-serialized using ``str()``::
+
+ >>> print str(config)
+ another-key whee!
+ new-key new-value-1
+ new-key new-value-2
+ some-key some-value
+ some-key another-value
+ some-key third-value
+ <BLANKLINE>
+ <section>
+ key1 value1.1
+ key1 value1.2
+ key2 value2
+ <BLANKLINE>
+ <deeper>
+ another key
+ another value
+ </deeper>
+ </section>
+ <BLANKLINE>
+ <sectiontype my-name>
+ key value
+ </sectiontype>
+ <BLANKLINE>
+ <another named>
+ nothing here
+ </another>
+ <BLANKLINE>
+
+Note that some adjustments have been made:
+
+- key/value pairs come before child sections
+
+- keys are sorted at each level
+
+- blank lines are removed, with new blank lines inserted to preserve
+ some semblance of readability
+
+These are all presentation changes, but not essential changes to the
+configuration data. The ordering of sections is not modified in
+rendering, nor are the values for a single key re-ordered within a
+section or top-level configuration.
+
+
+Support for %import
+-------------------
+
+Imports are supported, and are re-ordered in much the same way that
+other elements of a configuration are::
+
+ >>> config_text = '''
+ ...
+ ... %import some.package
+ ...
+ ... <section>
+ ...
+ ... %import another.package
+ ...
+ ... <another>
+ ... some value
+ ... </another>
+ ...
+ ... </section>
+ ...
+ ... some-key some-value
+ ...
+ ... '''
+
+ >>> config = schemaless.loadConfigFile(StringIO.StringIO(config_text))
+
+ >>> print config
+ %import some.package
+ %import another.package
+ <BLANKLINE>
+ some-key some-value
+ <BLANKLINE>
+ <section>
+ <another>
+ some value
+ </another>
+ </section>
+ <BLANKLINE>
+
+The imports are also available as the ``imports`` attribute of the
+configuration object::
+
+ >>> config.imports
+ ('some.package', 'another.package')
+
+Multiple imports of the same name are removed::
+
+ >>> config_text = '''
+ ...
+ ... %import some.package
+ ... %import another.package
+ ... %import some.package
+ ...
+ ... '''
+
+ >>> config = schemaless.loadConfigFile(StringIO.StringIO(config_text))
+
+ >>> print config
+ %import some.package
+ %import another.package
+ <BLANKLINE>
+
+ >>> config.imports
+ ('some.package', 'another.package')
+
+
+Limitations
+-----------
+
+There are some limitations of handling ZConfig-based configurations
+using the ``ZConfig.schemaless`` module. Some of these are
+implementation issues, and may be corrected in the future:
+
+- %define is not supported.
+
+- %include is not supported.
+
+Others are a function of not processing the schema, and can't easily
+be avoided:
+
+- normalization of keys based on keytypes specified in the <schema> or
+ <sectiontype> elements of the schema if not performed.
+
+ If the transformation of a key might affect the behavior controlled
+ by the resulting configuration, the generated configuration may not
+ be equivalent. Examples of this are unusual, but exist.
+
+Limitations related to the non-processing of the schema cannot be
+detected by the ``ZConfig.schemaless``, so no errors are reported in
+these situations.
+
+For the strictly syntactic limitations, we do get errors when the
+input data requires they be supported. Let's look at both the %define
+and %include handling.
+
+When %define is used in the input configuration, an exception is
+raised when loading the configuration::
+
+ >>> config_text = '''
+ ...
+ ... %define somename somevalue
+ ...
+ ... '''
+
+ >>> schemaless.loadConfigFile(StringIO.StringIO(config_text))
+ Traceback (most recent call last):
+ NotImplementedError: defines are not supported
+
+A similar exception is raised for %include::
+
+ >>> config_text = '''
+ ...
+ ... %include some/other/file.conf
+ ...
+ ... '''
+
+ >>> schemaless.loadConfigFile(StringIO.StringIO(config_text))
+ Traceback (most recent call last):
+ NotImplementedError: includes are not supported
Property changes on: ZConfig/trunk/ZConfig/schemaless.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Added: ZConfig/trunk/ZConfig/tests/test_schemaless.py
===================================================================
--- ZConfig/trunk/ZConfig/tests/test_schemaless.py (rev 0)
+++ ZConfig/trunk/ZConfig/tests/test_schemaless.py 2007-06-21 17:01:39 UTC (rev 76904)
@@ -0,0 +1,27 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""\
+Test driver for ZConfig.schemaless.
+
+"""
+__docformat__ = "reStructuredText"
+
+try:
+ from zope.testing import doctest
+except ImportError:
+ import doctest
+
+
+def test_suite():
+ return doctest.DocFileSuite("schemaless.txt", package="ZConfig")
Property changes on: ZConfig/trunk/ZConfig/tests/test_schemaless.py
___________________________________________________________________
Name: svn:mime-type
+ text/x-python
Name: svn:eol-style
+ native
Modified: ZConfig/trunk/setup.py
===================================================================
--- ZConfig/trunk/setup.py 2007-06-21 16:26:38 UTC (rev 76903)
+++ ZConfig/trunk/setup.py 2007-06-21 17:01:39 UTC (rev 76904)
@@ -22,7 +22,7 @@
setup(
name = name,
- version = "2.4a4",
+ version = "2.4a5",
author = "Fred L. Drake, Jr.",
author_email = "fred at zope.com",
description = "Structured Configuration Library",
More information about the ZConfig
mailing list