Commit a877edc6 authored by Tim Peters's avatar Tim Peters

Prepare to stitch in ZConfig as an SVN external.

parent b3bdf2b8
Changes since ZConfig 2.1:
- More documentation has been written.
- Added a timedelta datatype function; the input is the same as for
the time-interval datatype, but the resulting value is a
datetime.timedelta object.
- Make sure keys specified as attributes of the <default> element are
converted by the appropriate key type, and are re-checked for
derived sections.
- Refactored the ZConfig.components.logger schema components so that a
schema can import just one of the "eventlog" or "logger" sections if
desired. This can be helpful to avoid naming conflicts.
- Added a reopen() method to the logger factories.
- Always use an absolute pathname when opening a FileHandler.
- A fix to the logger 'format' key to allow the %(process)d expansion variable
that the logging package supports.
- A new timedelta built-in datatype was added. Similar to time-interval
except that it returns a datetime.timedelta object instead.
Changes since ZConfig 2.0:
- Removed compatibility with Python 2.1 and 2.2.
- Schema components must really be in Python packages; the directory
search has been modified to perform an import to locate the package
rather than incorrectly implementing the search algorithm.
- The default objects use for section values now provide a method
getSectionAttributes(); this returns a list of all the attributes of
the section object which store configuration-defined data (including
information derived from the schema).
- Default information can now be included in a schema for <key
name="+"> and <multikey name="+"> by using <default key="...">.
- More documentation has been added to discuss schema extension.
- Support for a Unicode-free Python has been fixed.
- Derived section types now inherit the datatype of the base type if
no datatype is identified explicitly.
- Derived section types can now override the keytype instead of always
inheriting from their base type.
- <import package='...'/> makes use of the current prefix if the
package name begins witha dot.
- Added two standard datatypes: dotted-name and dotted-suffix.
- Added two standard schema components: ZConfig.components.basic and
ZConfig.components.logger.
Changes since ZConfig 1.0:
- Configurations can import additional schema components using a new
"%import" directive; this can be used to integrate 3rd-party
components into an application.
- Schemas may be extended using a new "extends" attribute on the
<schema> element.
- Better error messages when elements in a schema definition are
improperly nested.
- The "zconfig" script can now simply verify that a schema definition
is valid, if that's all that's needed.
# Load the license from an external source, so we don't have to keep a
# copy of it sitting around:
<load>
LICENSE.txt http://cvs.zope.org/Zope3/ZopePublicLicense.txt?rev=HEAD
</load>
# Add a few things to the distribution root.
<distribution>
doc
LICENSE.txt
NEWS.txt
README.txt
</distribution>
# Specify what is included in the component.
<collection>
# Python modules from the package:
*.py
# Child packages:
components
tests
# Other files and directories needed when distutils runs:
scripts
</collection>
Metadata-Version: 1.1
Name: ZConfig
Summary: Structured Configuration Library
Home-page: http://www.zope.org/Members/fdrake/zconfig/
Author: Fred L. Drake, Jr.
Author-email: fred@zope.com
License: ZPL 2
Description: ZConfig is a configuration library intended for general use. It
supports a hierarchical schema-driven configuration model that allows
a schema to specify data conversion routines written in Python.
ZConfig's model is very different from the model supported by the
ConfigParser module found in Python's standard library, and is more
suitable to configuration-intensive applications.
ZConfig schema are written in an XML-based language and are able to
"import" schema components provided by Python packages. Since
components are able to bind to conversion functions provided by Python
code in the package (or elsewhere), configuration objects can be
arbitrarily complex, with values that have been verified against
arbitrary constraints. This makes it easy for applications to
separate configuration support from configuration loading even with
configuration data being defined and consumed by a wide range of
separate packages.
Platform: POSIX
Platform: Windows
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
This is ZConfig.
ZConfig is a configuration library intended for general use. It
supports a hierarchical schema-driven configuration model that allows
a schema to specify data conversion routines written in Python.
ZConfig's model is very different from the model supported by the
ConfigParser module found in Python's standard library, and is more
suitable to configuration-intensive applications.
ZConfig schema are written in an XML-based language and are able to
"import" schema components provided by Python packages. Since
components are able to bind to conversion functions provided by Python
code in the package (or elsewhere), configuration objects can be
arbitrarily complex, with values that have been verified against
arbitrary constraints. This makes it easy for applications to
separate configuration support from configuration loading even with
configuration data being defined and consumed by a wide range of
separate packages.
ZConfig is licensed under the Zope Public License, version 2.1. See
the file LICENSE.txt in the distribution for the full license text.
Reference documentation is available in the doc/ directory.
Information on the latest released version of the ZConfig package is
available at
http://www.zope.org/Members/fdrake/zconfig/
You may either create an RPM and install this, or install directly from
the source distribution.
Creating RPMS:
python setup.py bdist_rpm
If you need to force the Python interpreter to, for example, python2:
python2 setup.py bdist_rpm --python=python2
Installation from the source distribution:
python setup.py install
To install to a user's home-dir:
python setup.py install --home=<dir>
To install to another prefix (eg. /usr/local)
python setup.py install --prefix=/usr/local
If you need to force the python interpreter to e.g. python2:
python2 setup.py install
For more information please refer to
http://www.python.org/doc/current/inst/inst.html
# Metadata used by zpkg.
#documentation doc/zconfig.pdf
#documentation doc/schema.dtd
script scripts/zconfig*
##############################################################################
#
# Copyright (c) 2002, 2003 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.
#
##############################################################################
"""Configuration data structures and loader for the ZRS.
$Id: __init__.py,v 1.18 2004/04/15 20:33:32 fdrake Exp $
"""
version_info = (2, 2)
__version__ = ".".join([str(n) for n in version_info])
from ZConfig.loader import loadConfig, loadConfigFile
from ZConfig.loader import loadSchema, loadSchemaFile
class ConfigurationError(Exception):
"""Base class for ZConfig exceptions."""
def __init__(self, msg, url=None):
self.message = msg
self.url = url
Exception.__init__(self, msg)
def __str__(self):
return self.message
class _ParseError(ConfigurationError):
def __init__(self, msg, url, lineno, colno=None):
self.lineno = lineno
self.colno = colno
ConfigurationError.__init__(self, msg, url)
def __str__(self):
s = self.message
if self.url:
s += "\n("
elif (self.lineno, self.colno) != (None, None):
s += " ("
if self.lineno:
s += "line %d" % self.lineno
if self.colno is not None:
s += ", column %d" % self.colno
if self.url:
s += " in %s)" % self.url
else:
s += ")"
elif self.url:
s += self.url + ")"
return s
class SchemaError(_ParseError):
"""Raised when there's an error in the schema itself."""
def __init__(self, msg, url=None, lineno=None, colno=None):
_ParseError.__init__(self, msg, url, lineno, colno)
class SchemaResourceError(SchemaError):
"""Raised when there's an error locating a resource required by the schema.
"""
def __init__(self, msg, url=None, lineno=None, colno=None,
path=None, package=None, filename=None):
self.filename = filename
self.package = package
if path is not None:
path = path[:]
self.path = path
SchemaError.__init__(self, msg, url, lineno, colno)
def __str__(self):
s = SchemaError.__str__(self)
if self.package is not None:
s += "\n Package name: " + repr(self.package)
if self.filename is not None:
s += "\n File name: " + repr(self.filename)
if self.package is not None:
s += "\n Package path: " + repr(self.path)
return s
class ConfigurationSyntaxError(_ParseError):
"""Raised when there's a syntax error in a configuration file."""
class DataConversionError(ConfigurationError, ValueError):
"""Raised when a data type conversion function raises ValueError."""
def __init__(self, exception, value, position):
ConfigurationError.__init__(self, str(exception))
self.exception = exception
self.value = value
self.lineno, self.colno, self.url = position
def __str__(self):
s = "%s (line %s" % (self.message, self.lineno)
if self.colno is not None:
s += ", %s" % self.colno
if self.url:
s += ", in %s)" % self.url
else:
s += ")"
return s
class SubstitutionSyntaxError(ConfigurationError):
"""Raised when interpolation source text contains syntactical errors."""
class SubstitutionReplacementError(ConfigurationSyntaxError, LookupError):
"""Raised when no replacement is available for a reference."""
def __init__(self, source, name, url=None, lineno=None):
self.source = source
self.name = name
ConfigurationSyntaxError.__init__(
self, "no replacement for " + `name`, url, lineno)
##############################################################################
#
# Copyright (c) 2002, 2003 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.
#
##############################################################################
"""Configuration parser."""
import ZConfig
import ZConfig.url
from ZConfig.substitution import isname, substitute
class ZConfigParser:
__metaclass__ = type
__slots__ = ('resource', 'context', 'lineno',
'stack', 'defs', 'file', 'url')
def __init__(self, resource, context, defines=None):
self.resource = resource
self.context = context
self.file = resource.file
self.url = resource.url
self.lineno = 0
self.stack = [] # [(type, name, prevmatcher), ...]
if defines is None:
defines = {}
self.defs = defines
def nextline(self):
line = self.file.readline()
if line:
self.lineno += 1
return False, line.strip()
else:
return True, None
def parse(self, section):
done, line = self.nextline()
while not done:
if line[:1] in ("", "#"):
# blank line or comment
pass
elif line[:2] == "</":
# section end
if line[-1] != ">":
self.error("malformed section end")
section = self.end_section(section, line[2:-1])
elif line[0] == "<":
# section start
if line[-1] != ">":
self.error("malformed section start")
section = self.start_section(section, line[1:-1])
elif line[0] == "%":
self.handle_directive(section, line[1:])
else:
self.handle_key_value(section, line)
done, line = self.nextline()
if self.stack:
self.error("unclosed sections not allowed")
def start_section(self, section, rest):
isempty = rest[-1:] == "/"
if isempty:
text = rest[:-1].rstrip()
else:
text = rest.rstrip()
# parse section start stuff here
m = _section_start_rx.match(text)
if not m:
self.error("malformed section header")
type, name = m.group('type', 'name')
type = type.lower()
if name:
name = name.lower()
try:
newsect = self.context.startSection(section, type, name)
except ZConfig.ConfigurationError, e:
self.error(e[0])
if isempty:
self.context.endSection(section, type, name, newsect)
return section
else:
self.stack.append((type, name, section))
return newsect
def end_section(self, section, rest):
if not self.stack:
self.error("unexpected section end")
type = rest.rstrip().lower()
opentype, name, prevsection = self.stack.pop()
if type != opentype:
self.error("unbalanced section end")
try:
self.context.endSection(
prevsection, type, name, section)
except ZConfig.ConfigurationError, e:
self.error(e[0])
return prevsection
def handle_key_value(self, section, rest):
m = _keyvalue_rx.match(rest)
if not m:
self.error("malformed configuration data")
key, value = m.group('key', 'value')
if not value:
value = ''
else:
value = self.replace(value)
try:
section.addValue(key, value, (self.lineno, None, self.url))
except ZConfig.ConfigurationError, e:
self.error(e[0])
def handle_directive(self, section, rest):
m = _keyvalue_rx.match(rest)
if not m:
self.error("missing or unrecognized directive")
name, arg = m.group('key', 'value')
if name not in ("define", "import", "include"):
self.error("unknown directive: " + `name`)
if not arg:
self.error("missing argument to %%%s directive" % name)
if name == "include":
self.handle_include(section, arg)
elif name == "define":
self.handle_define(section, arg)
elif name == "import":
self.handle_import(section, arg)
else:
assert 0, "unexpected directive for " + `"%" + rest`
def handle_import(self, section, rest):
pkgname = self.replace(rest.strip())
self.context.importSchemaComponent(pkgname)
def handle_include(self, section, rest):
rest = self.replace(rest.strip())
newurl = ZConfig.url.urljoin(self.url, rest)
self.context.includeConfiguration(section, newurl, self.defs)
def handle_define(self, section, rest):
parts = rest.split(None, 1)
defname = parts[0].lower()
defvalue = ''
if len(parts) == 2:
defvalue = parts[1]
if self.defs.has_key(defname):
self.error("cannot redefine " + `defname`)
if not isname(defname):
self.error("not a substitution legal name: " + `defname`)
self.defs[defname] = self.replace(defvalue)
def replace(self, text):
try:
return substitute(text, self.defs)
except ZConfig.SubstitutionReplacementError, e:
e.lineno = self.lineno
e.url = self.url
raise
def error(self, message):
raise ZConfig.ConfigurationSyntaxError(message, self.url, self.lineno)
import re
# _name_re does not allow "(" or ")" for historical reasons. Though
# the restriction could be lifted, there seems no need to do so.
_name_re = r"[^\s()]+"
_keyvalue_rx = re.compile(r"(?P<key>%s)\s*(?P<value>[^\s].*)?$"
% _name_re)
_section_start_rx = re.compile(r"(?P<type>%s)"
r"(?:\s+(?P<name>%s))?"
r"$"
% (_name_re, _name_re))
del re
##############################################################################
#
# Copyright (c) 2003 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 command-line provision of settings.
This module provides an extension of the ConfigLoader class which adds
a way to add configuration settings from an alternate source. Each
setting is described by a string of the form::
some/path/to/key=value
"""
import ZConfig
import ZConfig.loader
import ZConfig.matcher
class ExtendedConfigLoader(ZConfig.loader.ConfigLoader):
def __init__(self, schema):
ZConfig.loader.ConfigLoader.__init__(self, schema)
self.clopts = [] # [(optpath, value, source-position), ...]
def addOption(self, spec, pos=None):
if pos is None:
pos = "<command-line option>", -1, -1
if "=" not in spec:
e = ZConfig.ConfigurationSyntaxError(
"invalid configuration specifier", *pos)
e.specifier = spec
raise e
# For now, just add it to the list; not clear that checking
# against the schema at this point buys anything.
opt, val = spec.split("=", 1)
optpath = opt.split("/")
if "" in optpath:
# // is not allowed in option path
e = ZConfig.ConfigurationSyntaxError(
"'//' is not allowed in an option path", *pos)
e.specifier = spec
raise e
self.clopts.append((optpath, val, pos))
def createSchemaMatcher(self):
if self.clopts:
sm = ExtendedSchemaMatcher(self.schema)
sm.set_optionbag(self.cook())
else:
sm = ZConfig.loader.ConfigLoader.createSchemaMatcher(self)
return sm
def cook(self):
if self.clopts:
return OptionBag(self.schema, self.schema, self.clopts)
else:
return None
class OptionBag:
def __init__(self, schema, sectiontype, options):
self.sectiontype = sectiontype
self.schema = schema
self.keypairs = {}
self.sectitems = []
self._basic_key = schema.registry.get("basic-key")
for item in options:
optpath, val, pos = item
name = sectiontype.keytype(optpath[0])
if len(optpath) == 1:
self.add_value(name, val, pos)
else:
self.sectitems.append(item)
def basic_key(self, s, pos):
try:
return self._basic_key(s)
except ValueError:
raise ZConfig.ConfigurationSyntaxError(
"could not convert basic-key value", *pos)
def add_value(self, name, val, pos):
if self.keypairs.has_key(name):
L = self.keypairs[name]
else:
L = []
self.keypairs[name] = L
L.append((val, pos))
def has_key(self, name):
return self.keypairs.has_key(name)
def get_key(self, name):
"""Return a list of (value, pos) items for the key 'name'.
The returned list may be empty.
"""
L = self.keypairs.get(name)
if L:
del self.keypairs[name]
return L
else:
return []
def keys(self):
return self.keypairs.keys()
def get_section_info(self, type, name):
L = [] # what pertains to the child section
R = [] # what we keep
for item in self.sectitems:
optpath, val, pos = item
s = optpath[0]
bk = self.basic_key(s, pos)
if name and s.lower() == name:
L.append((optpath[1:], val, pos))
elif bk == type:
L.append((optpath[1:], val, pos))
else:
R.append(item)
if L:
self.sectitems[:] = R
return OptionBag(self.schema, self.schema.gettype(type), L)
else:
return None
def finish(self):
if self.sectitems or self.keypairs:
raise ZConfig.ConfigurationError(
"not all command line options were consumed")
class MatcherMixin:
def set_optionbag(self, bag):
self.optionbag = bag
def addValue(self, key, value, position):
try:
realkey = self.type.keytype(key)
except ValueError, e:
raise ZConfig.DataConversionError(e, key, position)
if self.optionbag.has_key(realkey):
return
ZConfig.matcher.BaseMatcher.addValue(self, key, value, position)
def createChildMatcher(self, type, name):
sm = ZConfig.matcher.BaseMatcher.createChildMatcher(self, type, name)
bag = self.optionbag.get_section_info(type.name, name)
if bag is not None:
sm = ExtendedSectionMatcher(
sm.info, sm.type, sm.name, sm.handlers)
sm.set_optionbag(bag)
return sm
def finish_optionbag(self):
for key in self.optionbag.keys():
for val, pos in self.optionbag.get_key(key):
ZConfig.matcher.BaseMatcher.addValue(self, key, val, pos)
self.optionbag.finish()
class ExtendedSectionMatcher(MatcherMixin, ZConfig.matcher.SectionMatcher):
def finish(self):
self.finish_optionbag()
return ZConfig.matcher.SectionMatcher.finish(self)
class ExtendedSchemaMatcher(MatcherMixin, ZConfig.matcher.SchemaMatcher):
def finish(self):
self.finish_optionbag()
return ZConfig.matcher.SchemaMatcher.finish(self)
<component>
<description>
Convenient loader which causes all the "basic" components to be
loaded.
</description>
<import package="ZConfig.components.basic" file="mapping.xml"/>
</component>
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Python datatype for the ZConfig.components.basic.mapping section type."""
def mapping(section):
return section.mapping
<component>
<sectiontype name="ZConfig.basic.mapping"
datatype="ZConfig.components.basic.mapping.mapping">
<description>
Section that provides a simple mapping implementation. An
application should derive a more specific section type for use
in configuration files:
&lt;import package="ZConfig.components.basic"
file="mapping.xml"
/&gt;
&lt;sectiontype name="mapping"
extends="ZConfig.basic.mapping"
/&gt;
If a non-standard keytype is needed, it can be overridden as
well:
&lt;sectiontype name="system-map"
extends="ZConfig.basic.mapping"
keytype="mypkg.datatypes.system_name"
/&gt;
</description>
<key name="+"
attribute="mapping"
required="no"
/>
</sectiontype>
</component>
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Tests of the 'basic' section types provided as part of
ZConfig.components.basic."""
import unittest
from ZConfig.tests import support
SIMPLE_SCHEMA = '''\
<schema>
<import package="ZConfig.components.basic" file="mapping.xml" />
<sectiontype name="dict"
extends="ZConfig.basic.mapping" />
<sectiontype name="intkeys"
extends="ZConfig.basic.mapping"
keytype="integer" />
<section name="*"
type="dict"
attribute="simple_dict" />
<section name="*"
type="intkeys"
attribute="int_dict" />
</schema>
'''
class BasicSectionTypeTestCase(support.TestBase):
schema = None
def setUp(self):
if self.schema is None:
self.__class__.schema = self.load_schema_text(SIMPLE_SCHEMA)
def test_simple_empty_dict(self):
conf = self.load_config_text(self.schema, "<dict/>")
self.assertEqual(conf.simple_dict, {})
conf = self.load_config_text(self.schema, """\
<dict foo>
# comment
</dict>
""")
self.assertEqual(conf.simple_dict, {})
def test_simple_dict(self):
conf = self.load_config_text(self.schema, """\
<dict foo>
key-one value-one
key-two value-two
</dict>
""")
L = conf.simple_dict.items()
L.sort()
self.assertEqual(L, [("key-one", "value-one"),
("key-two", "value-two")])
def test_derived_dict(self):
conf = self.load_config_text(self.schema, """\
<intkeys>
1 foo
2 bar
42 question?
</intkeys>
""")
L = conf.int_dict.items()
L.sort()
self.assertEqual(L, [(1, "foo"), (2, "bar"), (42, "question?")])
def test_suite():
return unittest.makeSuite(BasicSectionTypeTestCase)
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""ZConfig schema component package for logging configuration."""
# Make sure we can't import this if "logging" isn't available; we
# don't want partial imports to appear to succeed.
try:
import logging
except ImportError:
import sys
del sys.modules[__name__]
<component>
<description>
</description>
<abstracttype name="ZConfig.logger.handler"/>
<abstracttype name="ZConfig.logger.log"/>
</component>
<component prefix="ZConfig.components.logger.logger">
<import package="ZConfig.components.logger" file="abstract.xml"/>
<sectiontype name="ZConfig.logger.base-logger">
<description>
Base definition for the logger types defined by
ZConfig.components.logger. This exists entirely to provide
shared key definitions and documentation.
</description>
<key name="level"
datatype="ZConfig.components.logger.datatypes.logging_level"
default="info">
<description>
Verbosity setting for the logger. Values must be a name of
a level, or an integer in the range [0..50]. The names of the
levels, in order of increasing verbosity (names on the same
line are equivalent):
critical, fatal
error
warn, warning
info
blather
debug
trace
all
The special name "notset", or the numeric value 0, indicates
that the setting for the parent logger should be used.
It is strongly recommended that names be used rather than
numeric values to ensure that configuration files can be
deciphered more easily.
</description>
</key>
<multisection type="ZConfig.logger.handler"
attribute="handlers" name="*">
<description>
Handlers to install on this logger. Each handler describes
how logging events should be presented.
</description>
</multisection>
</sectiontype>
</component>
<component prefix="ZConfig.components.logger.datatypes">
<description>
</description>
<import package="ZConfig.components.logger" file="abstract.xml"/>
<import package="ZConfig.components.logger" file="handlers.xml"/>
<import package="ZConfig.components.logger" file="logger.xml"/>
<import package="ZConfig.components.logger" file="eventlog.xml"/>
</component>
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""ZConfig datatypes for logging support."""
_logging_levels = {
"critical": 50,
"fatal": 50,
"error": 40,
"warn": 30,
"warning": 30,
"info": 20,
"blather": 15,
"debug": 10,
"trace": 5,
"all": 1,
"notset": 0,
}
def logging_level(value):
s = str(value).lower()
if _logging_levels.has_key(s):
return _logging_levels[s]
else:
v = int(s)
if v < 0 or v > 50:
raise ValueError("log level not in range: " + `v`)
return v
<component prefix="ZConfig.components.logger.logger">
<import package="ZConfig.components.logger" file="abstract.xml"/>
<import package="ZConfig.components.logger" file="base-logger.xml"/>
<sectiontype name="eventlog"
datatype=".EventLogFactory"
extends="ZConfig.logger.base-logger"
implements="ZConfig.logger.log">
<description>
Configuration for the root logger.
</description>
</sectiontype>
</component>
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
_marker = object()
class Factory:
"""Generic wrapper for instance construction.
Calling the factory causes the instance to be created if it hasn't
already been created, and returns the object. Calling the factory
multiple times returns the same object.
The instance is created using the factory's create() method, which
must be overriden by subclasses.
"""
def __init__(self):
self.instance = _marker
def __call__(self):
if self.instance is _marker:
self.instance = self.create()
return self.instance
def create(self):
raise NotImplementedError("subclasses need to override create()")
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""ZConfig factory datatypes for log handlers."""
import sys
from ZConfig.components.logger.factory import Factory
_log_format_variables = {
'name': '',
'levelno': '3',
'levelname': 'DEBUG',
'pathname': 'apath',
'filename': 'afile',
'module': 'amodule',
'lineno': 1,
'created': 1.1,
'asctime': 'atime',
'msecs': 1,
'relativeCreated': 1,
'thread': 1,
'message': 'amessage',
'process': 1,
}
def log_format(value):
value = ctrl_char_insert(value)
try:
# Make sure the format string uses only names that will be
# provided, and has reasonable type flags for each, and does
# not expect positional args.
value % _log_format_variables
except (ValueError, KeyError):
raise ValueError, 'Invalid log format string %s' % value
return value
_control_char_rewrites = {r'\n': '\n', r'\t': '\t', r'\b': '\b',
r'\f': '\f', r'\r': '\r'}.items()
def ctrl_char_insert(value):
for pattern, replacement in _control_char_rewrites:
value = value.replace(pattern, replacement)
return value
class HandlerFactory(Factory):
def __init__(self, section):
Factory.__init__(self)
self.section = section
def create_loghandler(self):
raise NotImplementedError(
"subclasses must override create_loghandler()")
def create(self):
import logging
logger = self.create_loghandler()
logger.setFormatter(logging.Formatter(self.section.format,
self.section.dateformat))
logger.setLevel(self.section.level)
return logger
def getLevel(self):
return self.section.level
class FileHandlerFactory(HandlerFactory):
def create_loghandler(self):
from ZConfig.components.logger import loghandler
path = self.section.path
if path == "STDERR":
handler = loghandler.StreamHandler(sys.stderr)
elif path == "STDOUT":
handler = loghandler.StreamHandler(sys.stdout)
else:
handler = loghandler.FileHandler(path)
return handler
_syslog_facilities = {
"auth": 1,
"authpriv": 1,
"cron": 1,
"daemon": 1,
"kern": 1,
"lpr": 1,
"mail": 1,
"news": 1,
"security": 1,
"syslog": 1,
"user": 1,
"uucp": 1,
"local0": 1,
"local1": 1,
"local2": 1,
"local3": 1,
"local4": 1,
"local5": 1,
"local6": 1,
"local7": 1,
}
def syslog_facility(value):
value = value.lower()
if not _syslog_facilities.has_key(value):
L = _syslog_facilities.keys()
L.sort()
raise ValueError("Syslog facility must be one of " + ", ".join(L))
return value
class SyslogHandlerFactory(HandlerFactory):
def create_loghandler(self):
from ZConfig.components.logger import loghandler
return loghandler.SysLogHandler(self.section.address.address,
self.section.facility)
class Win32EventLogFactory(HandlerFactory):
def create_loghandler(self):
from ZConfig.components.logger import loghandler
return loghandler.Win32EventLogHandler(self.section.appname)
def http_handler_url(value):
import urlparse
scheme, netloc, path, param, query, fragment = urlparse.urlparse(value)
if scheme != 'http':
raise ValueError, 'url must be an http url'
if not netloc:
raise ValueError, 'url must specify a location'
if not path:
raise ValueError, 'url must specify a path'
q = []
if param:
q.append(';')
q.append(param)
if query:
q.append('?')
q.append(query)
if fragment:
q.append('#')
q.append(fragment)
return (netloc, path + ''.join(q))
def get_or_post(value):
value = value.upper()
if value not in ('GET', 'POST'):
raise ValueError('method must be "GET" or "POST", instead received: '
+ repr(value))
return value
class HTTPHandlerFactory(HandlerFactory):
def create_loghandler(self):
from ZConfig.components.logger import loghandler
host, selector = self.section.url
return loghandler.HTTPHandler(host, selector, self.section.method)
class SMTPHandlerFactory(HandlerFactory):
def create_loghandler(self):
from ZConfig.components.logger import loghandler
host, port = self.section.smtp_server
if not port:
mailhost = host
else:
mailhost = host, port
return loghandler.SMTPHandler(mailhost,
self.section.fromaddr,
self.section.toaddrs,
self.section.subject)
<component prefix="ZConfig.components.logger.handlers">
<description>
</description>
<import package="ZConfig.components.logger" file="abstract.xml"/>
<sectiontype name="ZConfig.logger.base-log-handler">
<description>
Base type for most log handlers. This is cannot be used as a
loghandler directly since it doesn't implement the loghandler
abstract section type.
</description>
<key name="dateformat"
default="%Y-%m-%dT%H:%M:%S"/>
<key name="level"
default="notset"
datatype="ZConfig.components.logger.datatypes.logging_level"/>
</sectiontype>
<sectiontype name="logfile"
datatype=".FileHandlerFactory"
implements="ZConfig.logger.handler"
extends="ZConfig.logger.base-log-handler">
<key name="path" required="yes"/>
<key name="format"
default="------\n%(asctime)s %(levelname)s %(name)s %(message)s"
datatype=".log_format"/>
</sectiontype>
<sectiontype name="syslog"
datatype=".SyslogHandlerFactory"
implements="ZConfig.logger.handler"
extends="ZConfig.logger.base-log-handler">
<key name="facility" default="user" datatype=".syslog_facility"/>
<key name="address" datatype="socket-address" default="localhost:514"/>
<key name="format"
default="%(name)s %(message)s"
datatype=".log_format"/>
</sectiontype>
<sectiontype name="win32-eventlog"
datatype=".Win32EventLogFactory"
implements="ZConfig.logger.handler"
extends="ZConfig.logger.base-log-handler">
<key name="appname" default="Zope"/>
<key name="format"
default="%(levelname)s %(name)s %(message)s"
datatype=".log_format"/>
</sectiontype>
<sectiontype name="http-logger"
datatype=".HTTPHandlerFactory"
implements="ZConfig.logger.handler"
extends="ZConfig.logger.base-log-handler">
<key name="url" default="http://localhost/" datatype=".http_handler_url"/>
<key name="method" default="GET" datatype=".get_or_post"/>
<key name="format"
default="%(asctime)s %(levelname)s %(name)s %(message)s"
datatype=".log_format"/>
</sectiontype>
<sectiontype name="email-notifier"
datatype=".SMTPHandlerFactory"
implements="ZConfig.logger.handler"
extends="ZConfig.logger.base-log-handler">
<key name="from" required="yes" attribute="fromaddr"/>
<multikey name="to" required="yes" attribute="toaddrs"/>
<key name="subject" default="Message from Zope"/>
<key name="smtp-server" default="localhost" datatype="inet-address"/>
<key name="format"
default="%(asctime)s %(levelname)s %(name)s %(message)s"
datatype=".log_format"/>
</sectiontype>
</component>
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""ZConfig factory datatypes for loggers."""
from ZConfig.components.logger.factory import Factory
class LoggerFactoryBase(Factory):
"""Base class for logger factories.
Factory used to create loggers while delaying actual logger
instance construction. We need to do this because we may want to
reference a logger before actually instantiating it (for example,
to allow the app time to set an effective user). An instance of
this wrapper is a callable which, when called, returns a logger
object.
"""
def __init__(self, section):
Factory.__init__(self)
self.level = section.level
self.handler_factories = section.handlers
def create(self):
# set the logger up
import logging
logger = logging.getLogger(self.name)
logger.setLevel(self.level)
if self.handler_factories:
for handler_factory in self.handler_factories:
handler = handler_factory()
logger.addHandler(handler)
else:
from ZConfig.components.logger import loghandler
logger.addHandler(loghandler.NullHandler())
return logger
def startup(self):
# make sure we've instantiated the logger
self()
def getLowestHandlerLevel(self):
"""Return the lowest log level provided by any configured handler.
If all handlers and the logger itself have level==NOTSET, this
returns NOTSET.
"""
import logging
lowest = self.level
for factory in self.handler_factories:
level = factory.getLevel()
if level != logging.NOTSET:
if lowest == logging.NOTSET:
lowest = level
else:
lowest = min(lowest, level)
return lowest
def reopen(self):
"""Re-open any handlers for which this is a meaningful operation.
This only works on handlers on the logger provided by this
factory directly; handlers for child loggers are not affected.
(This can be considered a bug, but is sufficient at the
moment.)
"""
logger = self()
for handler in logger.handlers:
reopen = getattr(handler, "reopen", None)
if reopen is not None and callable(reopen):
reopen()
class EventLogFactory(LoggerFactoryBase):
"""Logger factory that returns the root logger."""
name = None
class LoggerFactory(LoggerFactoryBase):
"""Logger factory that returns the named logger."""
def __init__(self, section):
LoggerFactoryBase.__init__(self, section)
self.name = section.name
self.propagate = section.propagate
def create(self):
logger = LoggerFactoryBase.create(self)
logger.propagate = self.propagate
return logger
<component prefix="ZConfig.components.logger.logger">
<description>
</description>
<import package="ZConfig.components.logger" file="abstract.xml"/>
<import package="ZConfig.components.logger" file="base-logger.xml"/>
<import package="ZConfig.components.logger" file="eventlog.xml"/>
<sectiontype name="logger"
datatype=".LoggerFactory"
extends="ZConfig.logger.base-logger"
implements="ZConfig.logger.log">
<key name="propagate"
datatype="boolean"
default="true">
<description>
Indicates whether events that reach this logger should be
propogated toward the root of the logger hierarchy. If true
(the default), events will be passed to the logger's parent
after being handled. If false, events will be handled and the
parent will not be informed. There is not a way to control
propogation by the severity of the event.
</description>
</key>
<key name="name"
datatype="dotted-name"
required="yes">
<description>
The dotted name of the logger. This give it a location in the
logging hierarchy. Most applications provide a specific set
of subsystem names for which logging is meaning; consult the
application documentation for the set of names that are
actually interesting for the application.
</description>
</key>
</sectiontype>
</component>
##############################################################################
#
# Copyright (c) 2001 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.
#
##############################################################################
"""Handlers which can plug into a PEP 282 logger."""
import os.path
import sys
from logging import Handler, StreamHandler
from logging.handlers import SysLogHandler, BufferingHandler
from logging.handlers import HTTPHandler, SMTPHandler
from logging.handlers import NTEventLogHandler as Win32EventLogHandler
class FileHandler(StreamHandler):
"""File handler which supports reopening of logs.
Re-opening should be used instead of the 'rollover' feature of
the FileHandler from the standard library's logging package.
"""
def __init__(self, filename, mode="a"):
filename = os.path.abspath(filename)
StreamHandler.__init__(self, open(filename, mode))
self.baseFilename = filename
self.mode = mode
def close(self):
self.stream.close()
def reopen(self):
self.close()
self.stream = open(self.baseFilename, self.mode)
class NullHandler(Handler):
"""Handler that does nothing."""
def emit(self, record):
pass
def handle(self, record):
pass
class StartupHandler(BufferingHandler):
"""Handler which stores messages in a buffer until later.
This is useful at startup before we can know that we can safely
write to a configuration-specified handler.
"""
def __init__(self):
BufferingHandler.__init__(self, sys.maxint)
def shouldFlush(self, record):
return False
def flushBufferTo(self, target):
while self.buffer:
target.handle(self.buffer.pop(0))
##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Tests for logging configuration via ZConfig."""
import cStringIO as StringIO
import logging
import sys
import tempfile
import unittest
import ZConfig
from ZConfig.components.logger import datatypes
from ZConfig.components.logger import handlers
from ZConfig.components.logger import loghandler
class LoggingTestBase(unittest.TestCase):
# XXX This tries to save and restore the state of logging around
# the test. Somewhat surgical; there may be a better way.
name = None
def setUp(self):
self._old_logger = logging.getLogger(self.name)
self._old_level = self._old_logger.level
self._old_handlers = self._old_logger.handlers[:]
self._old_logger.handlers[:] = []
self._old_logger.setLevel(logging.WARN)
def tearDown(self):
for h in self._old_logger.handlers:
self._old_logger.removeHandler(h)
for h in self._old_handlers:
self._old_logger.addHandler(h)
self._old_logger.setLevel(self._old_level)
_schema = None
def get_schema(self):
if self._schema is None:
sio = StringIO.StringIO(self._schematext)
self.__class__._schema = ZConfig.loadSchemaFile(sio)
return self._schema
def get_config(self, text):
conf, handler = ZConfig.loadConfigFile(self.get_schema(),
StringIO.StringIO(text))
self.assert_(not handler)
return conf
class TestConfig(LoggingTestBase):
_schematext = """
<schema>
<import package='ZConfig.components.logger'/>
<section type='eventlog' name='*' attribute='eventlog'/>
</schema>
"""
def test_logging_level(self):
# Make sure the expected names are supported; it's not clear
# how to check the values in a meaningful way.
# Just make sure they're case-insensitive.
convert = datatypes.logging_level
for name in ["notset", "all", "trace", "debug", "blather",
"info", "warn", "warning", "error", "fatal",
"critical"]:
self.assertEqual(convert(name), convert(name.upper()))
self.assertRaises(ValueError, convert, "hopefully-not-a-valid-value")
def test_http_method(self):
convert = handlers.get_or_post
self.assertEqual(convert("get"), "GET")
self.assertEqual(convert("GET"), "GET")
self.assertEqual(convert("post"), "POST")
self.assertEqual(convert("POST"), "POST")
self.assertRaises(ValueError, convert, "")
self.assertRaises(ValueError, convert, "foo")
def test_syslog_facility(self):
convert = handlers.syslog_facility
for name in ["auth", "authpriv", "cron", "daemon", "kern",
"lpr", "mail", "news", "security", "syslog",
"user", "uucp", "local0", "local1", "local2",
"local3", "local4", "local5", "local6", "local7"]:
self.assertEqual(convert(name), name)
self.assertEqual(convert(name.upper()), name)
self.assertRaises(ValueError, convert, "hopefully-never-a-valid-value")
def test_config_without_logger(self):
conf = self.get_config("")
self.assert_(conf.eventlog is None)
def test_config_without_handlers(self):
logger = self.check_simple_logger("<eventlog/>")
# Make sure there's a NullHandler, since a warning gets
# printed if there are no handlers:
self.assertEqual(len(logger.handlers), 1)
self.assert_(isinstance(logger.handlers[0],
loghandler.NullHandler))
def test_with_logfile(self):
import os
fn = tempfile.mktemp()
logger = self.check_simple_logger("<eventlog>\n"
" <logfile>\n"
" path %s\n"
" level debug\n"
" </logfile>\n"
"</eventlog>" % fn)
logfile = logger.handlers[0]
self.assertEqual(logfile.level, logging.DEBUG)
self.assert_(isinstance(logfile, loghandler.FileHandler))
logfile.close()
os.remove(fn)
def test_with_stderr(self):
self.check_standard_stream("stderr")
def test_with_stdout(self):
self.check_standard_stream("stdout")
def check_standard_stream(self, name):
old_stream = getattr(sys, name)
conf = self.get_config("""
<eventlog>
<logfile>
level info
path %s
</logfile>
</eventlog>
""" % name.upper())
self.assert_(conf.eventlog is not None)
# The factory has already been created; make sure it picks up
# the stderr we set here when we create the logger and
# handlers:
sio = StringIO.StringIO()
setattr(sys, name, sio)
try:
logger = conf.eventlog()
finally:
setattr(sys, name, old_stream)
logger.warn("woohoo!")
self.assert_(sio.getvalue().find("woohoo!") >= 0)
def test_with_syslog(self):
logger = self.check_simple_logger("<eventlog>\n"
" <syslog>\n"
" level error\n"
" facility local3\n"
" </syslog>\n"
"</eventlog>")
syslog = logger.handlers[0]
self.assertEqual(syslog.level, logging.ERROR)
self.assert_(isinstance(syslog, loghandler.SysLogHandler))
def test_with_http_logger_localhost(self):
logger = self.check_simple_logger("<eventlog>\n"
" <http-logger>\n"
" level error\n"
" method post\n"
" </http-logger>\n"
"</eventlog>")
handler = logger.handlers[0]
self.assertEqual(handler.host, "localhost")
# XXX The "url" attribute of the handler is misnamed; it
# really means just the selector portion of the URL.
self.assertEqual(handler.url, "/")
self.assertEqual(handler.level, logging.ERROR)
self.assertEqual(handler.method, "POST")
self.assert_(isinstance(handler, loghandler.HTTPHandler))
def test_with_http_logger_remote_host(self):
logger = self.check_simple_logger("<eventlog>\n"
" <http-logger>\n"
" method get\n"
" url http://example.com/log/\n"
" </http-logger>\n"
"</eventlog>")
handler = logger.handlers[0]
self.assertEqual(handler.host, "example.com")
# XXX The "url" attribute of the handler is misnamed; it
# really means just the selector portion of the URL.
self.assertEqual(handler.url, "/log/")
self.assertEqual(handler.level, logging.NOTSET)
self.assertEqual(handler.method, "GET")
self.assert_(isinstance(handler, loghandler.HTTPHandler))
def test_with_email_notifier(self):
logger = self.check_simple_logger("<eventlog>\n"
" <email-notifier>\n"
" to sysadmin@example.com\n"
" to sa-pager@example.com\n"
" from zlog-user@example.com\n"
" level fatal\n"
" </email-notifier>\n"
"</eventlog>")
handler = logger.handlers[0]
self.assertEqual(handler.toaddrs, ["sysadmin@example.com",
"sa-pager@example.com"])
self.assertEqual(handler.fromaddr, "zlog-user@example.com")
self.assertEqual(handler.level, logging.FATAL)
def check_simple_logger(self, text, level=logging.INFO):
conf = self.get_config(text)
self.assert_(conf.eventlog is not None)
self.assertEqual(conf.eventlog.level, level)
logger = conf.eventlog()
self.assert_(isinstance(logger, logging.Logger))
self.assertEqual(len(logger.handlers), 1)
return logger
def test_suite():
return unittest.makeSuite(TestConfig)
if __name__ == '__main__':
unittest.main(defaultTest="test_suite")
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002, 2003 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.
#
##############################################################################
# Rules to convert the documentation to a single PDF file.
#
# PostScript, HTML, and plain text output are also supported, though
# PDF is the default.
#
# See the README.txt file for information on the mkhowto program used
# to generate the formatted versions of the documentation.
.PHONY: default all html pdf ps text
default: pdf
all: html pdf ps text
html: zconfig/zconfig.html
pdf: zconfig.pdf
ps: zconfig.ps
text: zconfig.txt
zconfig/zconfig.html: zconfig.tex schema.dtd xmlmarkup.perl
mkhowto --html $<
zconfig.pdf: zconfig.tex schema.dtd xmlmarkup.sty
mkhowto --pdf $<
zconfig.ps: zconfig.tex schema.dtd xmlmarkup.sty
mkhowto --postscript $<
zconfig.txt: zconfig.tex schema.dtd xmlmarkup.sty
mkhowto --text $<
clean:
rm -f zconfig.l2h zconfig.l2h~
clobber: clean
rm -f zconfig.pdf zconfig.ps zconfig.txt
rm -rf zconfig
The zconfig.tex document in this directory contains the reference
documentation for the ZConfig package. This documentation is written
using the Python LaTeX styles.
To format the documentation, get a copy of the Python documentation
tools (the Doc/ directory from the Python sources), and create a
symlink to the tools/mkhowto script from some convenient bin/
directory. You will need to have a fairly complete set of
documentation tools installed on your platform; see
http://www.python.org/doc/current/doc/doc.html
for more information on the tools.
This documentation requires the latest version of the Python
documentation tools from CVS.
<!--
*************************************************************************
Copyright (c) 2002, 2003 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.
*************************************************************************
Please note that not all documents that conform to this DTD are
legal ZConfig schema. The ZConfig reference manual describes many
constraints that are important to understanding ZConfig schema.
-->
<!-- DTD for ZConfig schema documents. -->
<!ELEMENT schema (description?, metadefault?, example?,
import*,
(sectiontype | abstracttype)*,
(section | key | multisection | multikey)*)>
<!ATTLIST schema
extends NMTOKEN #IMPLIED
prefix NMTOKEN #IMPLIED
handler NMTOKEN #IMPLIED
keytype NMTOKEN #IMPLIED
datatype NMTOKEN #IMPLIED>
<!ELEMENT component (description?, (sectiontype | abstracttype)*)>
<!ATTLIST component
prefix NMTOKEN #IMPLIED>
<!ELEMENT import EMPTY>
<!ATTLIST import
file CDATA #IMPLIED
package NMTOKEN #IMPLIED
src CDATA #IMPLIED>
<!ELEMENT description (#PCDATA)*>
<!ATTLIST description
format NMTOKEN #IMPLIED>
<!ELEMENT metadefault (#PCDATA)*>
<!ELEMENT example (#PCDATA)*>
<!ELEMENT sectiontype (description?,
(section | key | multisection | multikey)*)>
<!ATTLIST sectiontype
name NMTOKEN #REQUIRED
prefix NMTOKEN #IMPLIED
keytype NMTOKEN #IMPLIED
datatype NMTOKEN #IMPLIED
implements NMTOKEN #IMPLIED
extends NMTOKEN #IMPLIED>
<!ELEMENT abstracttype (description?)>
<!ATTLIST abstracttype
name NMTOKEN #REQUIRED
prefix NMTOKEN #IMPLIED>
<!ELEMENT default (#PCDATA)*>
<!ATTLIST default
key CDATA #IMPLIED>
<!ELEMENT key (description?, metadefault?, example?, default*)>
<!ATTLIST key
name CDATA #REQUIRED
attribute NMTOKEN #IMPLIED
datatype NMTOKEN #IMPLIED
handler NMTOKEN #IMPLIED
required (yes|no) "no"
default CDATA #IMPLIED>
<!ELEMENT multikey (description?, metadefault?, example?, default*)>
<!ATTLIST multikey
name CDATA #REQUIRED
attribute NMTOKEN #IMPLIED
datatype NMTOKEN #IMPLIED
handler NMTOKEN #IMPLIED
required (yes|no) "no">
<!ELEMENT section (description?)>
<!ATTLIST section
name CDATA #REQUIRED
attribute NMTOKEN #IMPLIED
type NMTOKEN #REQUIRED
handler NMTOKEN #IMPLIED
required (yes|no) "no">
<!ELEMENT multisection (description?)>
<!ATTLIST multisection
name CDATA #REQUIRED
attribute NMTOKEN #IMPLIED
type NMTOKEN #REQUIRED
handler NMTOKEN #IMPLIED
required (yes|no) "no">
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
# LaTeX2HTML support for the xmlmarkup package. Doesn't do indexing.
package main;
sub do_cmd_element{
local($_) = @_;
my $name = next_argument();
return "<tt class='element'>$name</tt>" . $_;
}
sub do_cmd_attribute{
local($_) = @_;
my $name = next_argument();
return "<tt class='attribute'>$name</tt>" . $_;
}
sub do_env_attributedesc{
local($_) = @_;
my $name = next_argument();
my $valuetype = next_argument();
return ("\n<dl class='macrodesc'>"
. "\n<dt><b><tt class='macro'>$name</tt></b>"
. "&nbsp;&nbsp;&nbsp;($valuetype)"
. "\n<dd>"
. $_
. "</dl>");
}
sub do_env_elementdesc{
local($_) = @_;
my $name = next_argument();
my $contentmodel = next_argument();
return ("\n<dl class='elementdesc'>"
. "\n<dt class='start-tag'><tt>&lt;"
. "<b class='element'>$name</b>&gt;</tt>"
. "\n<dd class='content-model'>$contentmodel"
. "\n<dt class='endtag'><tt>&lt;/"
. "<b class='element'>$name</b>&gt;</tt>"
. "\n<dd class='descrition'>"
. $_
. "</dl>");
}
1; # Must end with this, because Perl is bogus.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright (c) 2003 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.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Define some simple markup for the LaTeX command documentation:
\ProvidesPackage{xmlmarkup}
\RequirePackage{python} % fulllineitems environment
\newcommand{\element}[1]{\code{#1}}
\newcommand{\attribute}[1]{\code{#1}}
% \begin{elementdesc}{type}{content-model}
\newenvironment{elementdesc}[2]{
\begin{fulllineitems}
\item[\code{\textless{\bfseries #1}\textgreater}]
\code{#2}
\item[\code{\textless/{\bfseries #1}\textgreater}]
\index{#1 element@\py@idxcode{#1} element}
\index{elements!#1@\py@idxcode{#1}}
}{\end{fulllineitems}}
% \begin{attributedesc}{name}{content-type}
\newenvironment{attributedesc}[2]{
\begin{fulllineitems}
\item[\code{\bfseries#1}{\quad(#2)}]
\index{#1@\py@idxcode{#1}}
}{\end{fulllineitems}}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""zconfig: Script to check validity of a configuration file.
Usage:
zconfig [options] [file...]
Options:
-h
--help Print this help text.
-s file
--schema file Use the schema in 'file' to validate the configuration;
this must be specified.
Each file named on the command line is checked for syntactical errors
and schema conformance. The schema must be specified. If no files
are specified and standard input is not a TTY, standard in is treated
as a configuration file. Specifying a schema and no configuration
files causes the schema to be checked.
"""
import optparse
import sys
import ZConfig
def main():
optparser = optparse.OptionParser(
usage="usage: %prog [-s FILE] [file ...]")
optparser.add_option(
"-s", "--schema", dest="schema",
help="use the schema in FILE (can be a URL)",
metavar="FILE")
options, args = optparser.parse_args()
if not options.schema:
print >>sys.stderr, "No schema specified, but is required."
usage(sys.stderr)
return 2
schema = ZConfig.loadSchema(options.schema)
if not args:
if sys.stdin.isatty():
# just checking the schema
return 0
else:
# stdin is a pipe
args = ["-"]
errors = 0
for fn in args:
try:
if fn == "-":
ZConfig.loadConfigFile(schema, sys.stdin)
else:
ZConfig.loadConfig(schema, fn)
except ZConfig.ConfigurationError, e:
print >>sys.stderr, str(e)
errors += 1
if errors:
return 1
else:
return 0
def usage(fp):
print >>fp, __doc__
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
__version__ = '$Revision: 1.3 $'[11:-2]
import ZConfig.loader
from ZConfig.info import *
import sys, cgi
def esc(x): return cgi.escape(str(x))
def dt(x):
tn = type(x).__name__
if tn == 'instance':
return '%s %s'%(tn, x.__class__.__module__ + '.' + x.__class__.__name__)
elif tn == 'class':
return '%s %s'%(tn, x.__module__ + '.' + x.__name__)
else:
return '%s %s'%(tn, x.__name__)
class explain:
done = []
def __call__(self, st):
if st.name in self.done:
return
self.done.append(st.name)
if st.description:
print st.description
for sub in st.getsubtypenames():
print '<dl>'
printContents(None, st.getsubtype(sub))
print '</dl>'
explain = explain()
def printSchema(schema):
print '<dl>'
for child in schema:
printContents(*child)
print '</dl>'
def printContents(name, info):
if isinstance(info, SectionType):
print '<dt><b><i>', info.name, '</i></b> (%s)</dt>'%dt(info.datatype)
print '<dd>'
if info.description:
print info.description
print '<dl>'
for sub in info:
printContents(*sub)
print '</dl></dd>'
elif isinstance(info, SectionInfo):
st = info.sectiontype
if st.isabstract():
print '<dt><b><i>', st.name, '</i>', info.name, '</b></dt>'
print '<dd>'
if info.description:
print info.description
explain(st)
print '</dd>'
else:
print '<dt><b>', info.attribute, info.name, '</b>'
print '(%s)</dt>'%dt(info.datatype)
print '<dd><dl>'
for sub in info.sectiontype:
printContents(*sub)
print '</dl></dd>'
else:
print '<dt><b>',info.name, '</b>', '(%s)'%dt(info.datatype)
default = info.getdefault()
if isinstance(default, ValueInfo):
print '(default: %r)'%esc(default.value)
elif default is not None:
print '(default: %r)'%esc(default)
if info.metadefault:
print '(metadefault: %s)' % info.metadefault
print '</dt>'
if info.description:
print '<dd>',info.description,'</dd>'
schema = ZConfig.loader.loadSchemaFile(sys.argv[1])
print '''<html><body>
<style>
dl {margin: 0 0 1em 0;}
</style>
'''
printSchema(schema)
print '</body></html>'
# vim: set filetype=python ts=4 sw=4 et si
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002, 2003 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.
#
##############################################################################
"""Tests for the configuration data structures and loader.
$Id: __init__.py,v 1.2 2003/01/03 21:05:56 fdrake Exp $
"""
<schema datatype="ZConfig.tests.test_schema.MySection">
<sectiontype name="type-1"/>
</schema>
<schema datatype="ZConfig.tests.test_schema.appsection">
<sectiontype name="type-2"/>
</schema>
<schema keytype="basic-key">
<sectiontype name="type-1"/>
</schema>
<schema keytype="ZConfig.tests.test_schema.uppercase">
<sectiontype name="type-2"/>
</schema>
<schema>
<sectiontype name="type-X"/>
</schema>
var2 value2
%include simple.conf
var3 value3
var4 $name
refouter $outervar
%define innervar inner
<schema>
<description>
Sample library of reusable data types.
</description>
<sectiontype name="type-a"/>
<sectiontype name="type-b"/>
</schema>
This diff is collapsed.
%define outervar outer
%include inner.conf
refinner $innervar
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This is a sample library of configuration schema components. This is
used for testing.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment