[Zope3-checkins] CVS: Packages/ZConfig - cfgparser.py:1.2 datatypes.py:1.2 info.py:1.2 loader.py:1.2 matcher.py:1.2 schema.py:1.2 substitution.py:1.2 url.py:1.2 Config.py:1.15 Context.py:1.18 __init__.py:1.4 ApacheStyle.py:NONE Exceptions.py:NONE Substitution.py:NONE
Fred L. Drake, Jr.
fred@zope.com
Fri, 3 Jan 2003 16:06:28 -0500
Update of /cvs-repository/Packages/ZConfig
In directory cvs.zope.org:/tmp/cvs-serv11713
Modified Files:
Config.py Context.py __init__.py
Added Files:
cfgparser.py datatypes.py info.py loader.py matcher.py
schema.py substitution.py url.py
Removed Files:
ApacheStyle.py Exceptions.py Substitution.py
Log Message:
Merge the zconfig-schema-devel-branch into the trunk for the ZConfig package.
Copyright notices get 2003 added as well.
The zconfig-schema-devel-branch should no longer be used.
=== Packages/ZConfig/cfgparser.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:27 2003
+++ Packages/ZConfig/cfgparser.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,178 @@
+##############################################################################
+#
+# 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.0 (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."""
+
+from ZConfig import ConfigurationError, ConfigurationSyntaxError
+from ZConfig.substitution import isname, substitute
+from ZConfig.url import urljoin
+
+try:
+ True
+except NameError:
+ True = 1
+ False = 0
+
+
+class ZConfigParser:
+ def __init__(self, resource, context):
+ self.resource = resource
+ self.context = context
+ self.file = resource.file
+ self.url = resource.url
+ self.lineno = 0
+ self.stack = [] # [(type, name, delegatename, prevmatcher), ...]
+ self.defs = {}
+
+ 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, delegatename = m.group('type', 'name', 'delegatename')
+ type = type.lower()
+ if name:
+ name = name.lower()
+ try:
+ newsect = self.context.startSection(section, type, name,
+ delegatename)
+ except ConfigurationError, e:
+ self.error(e[0])
+
+ if isempty:
+ self.context.endSection(section, type, name, delegatename, newsect)
+ return section
+ else:
+ self.stack.append((type, name, delegatename, section))
+ return newsect
+
+ def end_section(self, section, rest):
+ if not self.stack:
+ self.error("unexpected section end")
+ type = rest.rstrip().lower()
+ opentype, name, delegatename, prevsection = self.stack.pop()
+ if type != opentype:
+ self.error("unbalanced section end")
+ try:
+ self.context.endSection(
+ prevsection, type, name, delegatename, section)
+ except 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 = substitute(value, self.defs)
+ try:
+ section.addValue(key, value)
+ except 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", "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)
+ else:
+ assert 0, "unexpected directive for " + `"%" + rest`
+
+ def handle_include(self, section, rest):
+ newurl = urljoin(self.url, rest)
+ self.context.includeConfiguration(section, newurl)
+
+ 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] = defvalue
+
+ def error(self, message):
+ raise ConfigurationSyntaxError(message, self.url, self.lineno)
+
+
+import re
+# _name_re cannot allow "(" or ")" since we need to be able to tell if
+# a section has a name or not: <section (name)> would be ambiguous if
+# parentheses were allowed in names.
+_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"(?:\s*[(](?P<delegatename>%s)[)])?"
+ r"$"
+ % (_name_re, _name_re, _name_re))
+del re
=== Packages/ZConfig/datatypes.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:27 2003
+++ Packages/ZConfig/datatypes.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,372 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
+"""Selection of standard datatypes for ZConfig."""
+
+import re
+import sys
+import os
+
+try:
+ True
+except NameError:
+ True = 1
+ False = 0
+
+
+class MemoizedConversion:
+ """Conversion helper that caches the results of expensive conversions."""
+
+ def __init__(self, conversion):
+ self._memo = {}
+ self._conversion = conversion
+
+ def __call__(self, value):
+ try:
+ return self._memo[value]
+ except KeyError:
+ v = self._conversion(value)
+ self._memo[value] = v
+ return v
+
+
+class RangeCheckedConversion:
+ """Conversion helper that range checks another conversion."""
+
+ def __init__(self, conversion, min=None, max=None):
+ self._min = min
+ self._max = max
+ self._conversion = conversion
+
+ def __call__(self, value):
+ v = self._conversion(value)
+ if self._min is not None and v < self._min:
+ raise ValueError("%s is below lower bound (%s)"
+ % (`v`, `self._min`))
+ if self._max is not None and v > self._max:
+ raise ValueError("%s is above upper bound (%s)"
+ % (`v`, `self._max`))
+ return v
+
+
+class RegularExpressionConversion:
+ def __init__(self, regex):
+ self._rx = re.compile(regex)
+
+ def __call__(self, value):
+ m = self._rx.match(value)
+ if m and m.group() == value:
+ return value
+ else:
+ raise ValueError("value did not match regular expression: "
+ + `value`)
+
+
+def check_locale(value):
+ import locale
+ prev = locale.setlocale(locale.LC_ALL)
+ try:
+ try:
+ locale.setlocale(locale.LC_ALL, value)
+ finally:
+ locale.setlocale(locale.LC_ALL, prev)
+ except locale.Error:
+ raise ValueError(
+ 'The specified locale "%s" is not supported by your system.\n'
+ 'See your operating system documentation for more\n'
+ 'information on locale support.' % value)
+ else:
+ return value
+
+
+class BasicKeyConversion(RegularExpressionConversion):
+ def __init__(self):
+ RegularExpressionConversion.__init__(self, "[a-zA-Z][-._a-zA-Z0-9]*")
+
+ def __call__(self, value):
+ value = str(value)
+ return RegularExpressionConversion.__call__(self, value).lower()
+
+
+class IdentifierConversion(RegularExpressionConversion):
+ def __init__(self):
+ RegularExpressionConversion.__init__(self, "[_a-zA-Z][_a-zA-Z0-9]*")
+
+
+class LogLevelConversion:
+ # This uses the 'logging' package conventions; only makes sense
+ # for Zope 2.7 (and newer) and Zope 3. Not sure what the
+ # compatibility should be.
+
+ _levels = {
+ "critical": 50,
+ "fatal": 50,
+ "error": 40,
+ "warn": 30,
+ "info": 20,
+ "debug": 10,
+ "all": 0,
+ }
+
+ def __call__(self, value):
+ s = str(value).lower()
+ if self._levels.has_key(s):
+ return self._levels[s]
+ else:
+ v = int(s)
+ if v < 0 or v > 50:
+ raise ValueError("log level not in range: " + `v`)
+ return v
+
+
+if sys.version[:3] < "2.3":
+ def integer(value):
+ try:
+ return int(value)
+ except ValueError:
+ return long(value)
+else:
+ integer = int
+
+
+def null_conversion(value):
+ return value
+
+
+def asBoolean(s):
+ """Convert a string value to a boolean value."""
+ ss = str(s).lower()
+ if ss in ('yes', 'true', 'on'):
+ return True
+ elif ss in ('no', 'false', 'off'):
+ return False
+ else:
+ raise ValueError("not a valid boolean value: " + repr(s))
+
+
+port_number = RangeCheckedConversion(integer, min=1, max=0xffff).__call__
+
+
+def inet_address(s):
+ # returns (host, port) tuple
+ host = ''
+ port = None
+ if ":" in s:
+ host, s = s.split(":", 1)
+ if s:
+ port = port_number(s)
+ host = host.lower()
+ else:
+ try:
+ port = port_number(s)
+ except ValueError:
+ host = s.lower()
+ return host, port
+
+
+def socket_address(s):
+ # returns (family, address) tuple
+ import socket
+ if "/" in s:
+ if hasattr(socket, "AF_UNIX"):
+ return socket.AF_UNIX, s
+ else:
+ raise ValueError(
+ "AF_UNIX sockets are not supported on this platform")
+ else:
+ return socket.AF_INET, inet_address(s)
+
+def float_conversion(v):
+ if isinstance(v, type('')) or isinstance(v, type(u'')):
+ if v.lower() in ["inf", "-inf", "nan"]:
+ raise ValueError(`v` + " is not a portable float representation")
+ return float(v)
+
+class IpaddrOrHostname(RegularExpressionConversion):
+ def __init__(self):
+ # IP address regex from the Perl Cookbook, Recipe 6.23 (revised ed.)
+ # We allow underscores in hostnames although this is considered
+ # illegal according to RFC1034.
+ expr = (r"(^(\d|[01]?\d\d|2[0-4]\d|25[0-5])\." #ipaddr
+ r"(\d|[01]?\d\d|2[0-4]\d|25[0-5])\." #ipaddr cont'd
+ r"(\d|[01]?\d\d|2[0-4]\d|25[0-5])\." #ipaddr cont'd
+ r"(\d|[01]?\d\d|2[0-4]\d|25[0-5])$)" #ipaddr cont'd
+ r"|([^0-9][A-Za-z0-9-_.]+)") # or hostname
+ RegularExpressionConversion.__init__(self, expr)
+
+def existing_directory(v):
+ if os.path.isdir(v):
+ return v
+ raise ValueError, '%s is not an existing directory' % v
+
+def existing_path(v):
+ if os.path.exists(v):
+ return v
+ raise ValueError, '%s is not an existing path' % v
+
+def existing_file(v):
+ if os.path.exists(v):
+ return v
+ raise ValueError, '%s is not an existing file' % v
+
+def existing_dirpath(v):
+ if not os.path.split(v)[0]:
+ # relative pathname
+ return v
+ dir = os.path.dirname(v)
+ if os.path.isdir(dir):
+ return v
+ raise ValueError, ('The directory named as part of the path %s '
+ 'does not exist.' % v)
+
+def parse_constructor(v):
+ parenmsg = (
+ 'Invalid constructor (unbalanced parenthesis in "%s")' % v
+ )
+ openparen = v.find('(')
+ if openparen < 0:
+ raise ValueError(parenmsg)
+ klass = v[:openparen]
+ if not v.endswith(')'):
+ raise ValueError(parenmsg)
+ arglist = v[openparen+1:-1]
+ return klass, arglist
+
+def get_arglist(s):
+ # someone could do a better job at this.
+ pos = []
+ kw = {}
+ args = s.split(',')
+ args = filter(None, args)
+ while args:
+ arg = args.pop(0)
+ try:
+ if '=' in arg:
+ k,v=arg.split('=', 1)
+ k = k.strip()
+ v = v.strip()
+ kw[k] = eval(v)
+ else:
+ arg = arg.strip()
+ pos.append(eval(arg))
+ except SyntaxError:
+ if not args:
+ raise
+ args[0] = '%s, %s' % (arg, args[0])
+ return pos, kw
+
+def constructor_conversion(v):
+ klass, arglist = parse_constructor(v)
+ pos, kw = get_arglist(arglist)
+ return klass, pos, kw
+
+def space_sep_key_value_conversion(v):
+ l = v.split(' ', 1)
+ if len(l) < 2:
+ l.append('')
+ return l
+
+
+class SuffixMultiplier:
+ # d is a dictionary of suffixes to integer multipliers. If no suffixes
+ # match, default is the multiplier. Matches are case insensitive. Return
+ # values are in the fundamental unit.
+ def __init__(self, d, default=1):
+ self._d = d
+ self._default = default
+ # all keys must be the same size
+ self._keysz = None
+ for k in d.keys():
+ if self._keysz is None:
+ self._keysz = len(k)
+ else:
+ assert self._keysz == len(k)
+
+ def __call__(self, v):
+ v = v.lower()
+ for s, m in self._d.items():
+ if v[-self._keysz:] == s:
+ return int(v[:-self._keysz]) * m
+ return int(v) * self._default
+
+stock_datatypes = {
+ "boolean": asBoolean,
+ "identifier": IdentifierConversion(),
+ "integer": integer,
+ "float": float_conversion,
+ "string": str,
+ "null": null_conversion,
+ "locale": MemoizedConversion(check_locale),
+ "port-number": port_number,
+ "basic-key": BasicKeyConversion(),
+ "logging-level": LogLevelConversion(),
+ "inet-address": inet_address,
+ "socket-address": socket_address,
+ "ipaddr-or-hostname":IpaddrOrHostname(),
+ "existing-directory":existing_directory,
+ "existing-path": existing_path,
+ "existing-file": existing_file,
+ "existing-dirpath": existing_dirpath,
+ "constructor": constructor_conversion,
+ "key-value": space_sep_key_value_conversion,
+ "byte-size": SuffixMultiplier({'kb': 1024,
+ 'mb': 1024*1024,
+ 'gb': 1024*1024*1024L,
+ }),
+ "time-interval": SuffixMultiplier({'s': 1,
+ 'm': 60,
+ 'h': 60*60,
+ 'd': 60*60*24,
+ }),
+ }
+
+class Registry:
+ def __init__(self, stock=None):
+ if stock is None:
+ stock = stock_datatypes.copy()
+ self._stock = stock
+ self._other = {}
+
+ def get(self, name):
+ t = self._stock.get(name)
+ if t is None:
+ t = self._other.get(name)
+ if t is None:
+ t = self.search(name)
+ return t
+
+ def register(self, name, conversion):
+ if self._stock.has_key(name):
+ raise ValueError("datatype name conflicts with built-in type: "
+ + `name`)
+ if self._other.has_key(name):
+ raise ValueError("datatype name already registered:" + `name`)
+ self._other[name] = conversion
+
+ def search(self, name):
+ if not "." in name:
+ raise ValueError("unloadable datatype name: " + `name`)
+ components = name.split('.')
+ start = components[0]
+ g = globals()
+ package = __import__(start, g, g)
+ modulenames = [start]
+ for component in components[1:]:
+ modulenames.append(component)
+ try:
+ package = getattr(package, component)
+ except AttributeError:
+ n = '.'.join(modulenames)
+ package = __import__(n, g, g, component)
+ self._other[name] = package
+ return package
=== Packages/ZConfig/info.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:27 2003
+++ Packages/ZConfig/info.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,363 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
+"""Objects that can describe a ZConfig schema."""
+
+import ZConfig
+
+
+try:
+ True
+except NameError:
+ True = 1
+ False = 0
+
+
+class UnboundedThing:
+ def __lt__(self, other):
+ return False
+
+ def __le__(self, other):
+ return isinstance(other, self.__class__)
+
+ def __gt__(self, other):
+ return True
+
+ def __ge__(self, other):
+ return True
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__)
+
+ def __ne__(self, other):
+ return not isinstance(other, self.__class__)
+
+ def __repr__(self):
+ return "<Unbounded>"
+
+Unbounded = UnboundedThing()
+
+
+class BaseInfo:
+ """Information about a single configuration key."""
+
+ description = None
+ example = None
+ metadefault = None
+
+ def __init__(self, name, datatype, minOccurs, maxOccurs, handler,
+ attribute):
+ if maxOccurs is not None and maxOccurs < 1:
+ if maxOccurs < 1:
+ raise ZConfig.SchemaError(
+ "maxOccurs must be at least 1")
+ if minOccurs is not None and minOccurs < maxOccurs:
+ raise ZConfig.SchemaError(
+ "minOccurs must be at least maxOccurs")
+ self.name = name
+ self.datatype = datatype
+ self.minOccurs = minOccurs
+ self.maxOccurs = maxOccurs
+ self.handler = handler
+ self.attribute = attribute
+
+ def __repr__(self):
+ clsname = self.__class__.__name__
+ return "<%s for %s>" % (clsname, `self.name`)
+
+ def istypegroup(self):
+ return False
+
+ def ismulti(self):
+ return self.maxOccurs > 1
+
+ def issection(self):
+ return False
+
+
+class KeyInfo(BaseInfo):
+ def __init__(self, name, datatype, minOccurs, maxOccurs, handler,
+ attribute):
+ assert minOccurs is not None
+ BaseInfo.__init__(self, name, datatype, minOccurs, maxOccurs,
+ handler, attribute)
+ self._finished = False
+ self._default = None
+
+ def finish(self):
+ if self._finished:
+ raise ZConfig.SchemaError(
+ "cannot finish KeyInfo more than once")
+ self._finished = True
+
+ def adddefault(self, value):
+ if self._finished:
+ raise ZConfig.SchemaError(
+ "cannot add default values to finished KeyInfo")
+ if self.maxOccurs > 1:
+ if self._default is None:
+ self._default = [value]
+ else:
+ self._default.append(value)
+ elif self._default is not None:
+ raise ZConfig.SchemaError(
+ "cannot set more than one default to key with maxOccurs == 1")
+ else:
+ self._default = value
+
+ def getdefault(self):
+ if not self._finished:
+ raise ZConfig.SchemaError(
+ "cannot get default value of key before KeyInfo"
+ " has been completely initialized")
+ if self._default is None and self.maxOccurs > 1:
+ return []
+ else:
+ return self._default
+
+
+class SectionInfo(BaseInfo):
+ def __init__(self, name, sectiontype, minOccurs, maxOccurs, handler,
+ attribute):
+ # name - name of the section; one of '*', '+', or name1
+ # sectiontype - SectionType instance
+ # minOccurs - minimum number of occurances of the section
+ # maxOccurs - maximum number of occurances; if > 1, name
+ # must be '*' or '+'
+ # handler - handler name called when value(s) must take effect,
+ # or None
+ # attribute - name of the attribute on the SectionValue object
+ if maxOccurs > 1:
+ if name not in ('*', '+'):
+ raise ZConfig.SchemaError(
+ "sections which can occur more than once must"
+ " use a name of '*' or '+'")
+ if not attribute:
+ raise ZConfig.SchemaError(
+ "sections which can occur more than once must"
+ " specify a target attribute name")
+ if sectiontype.istypegroup():
+ datatype = None
+ else:
+ datatype = sectiontype.datatype
+ BaseInfo.__init__(self, name, datatype,
+ minOccurs, maxOccurs, handler, attribute)
+ self.sectiontype = sectiontype
+
+ def __repr__(self):
+ clsname = self.__class__.__name__
+ return "<%s for %s (%s)>" % (
+ clsname, self.sectiontype.name, `self.name`)
+
+ def issection(self):
+ return True
+
+ def allowUnnamed(self):
+ return self.name == "*"
+
+ def isAllowedName(self, name):
+ if name == "*" or name == "+":
+ return False
+ elif self.name == "+":
+ return name and True or False
+ elif not name:
+ return self.name == "*"
+ else:
+ return name == self.name
+
+ def getdefault(self):
+ # sections cannot have defaults
+ if self.maxOccurs > 1:
+ return []
+ else:
+ return None
+
+class TypeContainer:
+ def __init__(self):
+ self._types = {}
+
+ def addtype(self, typeinfo):
+ n = typeinfo.name.lower()
+ if self._types.has_key(n):
+ raise ZConfig.SchemaError("type name cannot be redefined: "
+ + `typeinfo.name`)
+ self._types[n] = typeinfo
+
+ def gettype(self, name):
+ n = name.lower()
+ try:
+ return self._types[n]
+ except KeyError:
+ raise ZConfig.SchemaError("unknown type name: " + `name`)
+
+ def gettypenames(self):
+ return self._types.keys()
+
+
+class GroupType(TypeContainer):
+ def __init__(self, name):
+ TypeContainer.__init__(self)
+ self.name = name
+
+ def istypegroup(self):
+ return True
+
+
+class SectionType:
+ def __init__(self, name, keytype, valuetype, datatype):
+ # name - name of the section, or '*' or '+'
+ # datatype - type for the section itself
+ # keytype - type for the keys themselves
+ # valuetype - default type for key values
+ self.name = name
+ self.datatype = datatype
+ self.keytype = keytype
+ self.valuetype = valuetype
+ self._children = [] # [(key, info), ...]
+ self._attrmap = {} # {attribute: index, ...}
+ self._keymap = {} # {key: index, ...}
+
+ def __len__(self):
+ return len(self._children)
+
+ def __getitem__(self, index):
+ return self._children[index]
+
+ def _add_child(self, key, info):
+ # check naming constraints
+ assert key or info.attribute
+ if key and self._keymap.has_key(key):
+ raise ZConfig.SchemaError(
+ "child name %s already used" % key)
+ if info.attribute and self._attrmap.has_key(info.attribute):
+ raise ZConfig.SchemaError(
+ "child attribute name %s already used" % info.attribute)
+ # a-ok, add the item to the appropriate maps
+ if info.attribute:
+ self._attrmap[info.attribute] = len(self._children)
+ if key:
+ self._keymap[key] = len(self._children)
+ self._children.append((key, info))
+
+ def addkey(self, keyinfo):
+ self._add_child(keyinfo.name, keyinfo)
+
+ def addsection(self, name, sectinfo):
+ assert name not in ("*", "+")
+ self._add_child(name, sectinfo)
+
+ def getinfo(self, key):
+ if not key:
+ raise ZConfig.ConfigurationError(
+ "cannot match a key without a name")
+ index = self._keymap.get(key)
+ if index is None:
+ raise ZConfig.ConfigurationError("no key matching " + `key`)
+ else:
+ return self._children[index][1]
+
+ def getchildnames(self):
+ return [key for (key, info) in self._children]
+
+ def getrequiredtypes(self):
+ d = {}
+ if self.name:
+ d[self.name] = 1
+ stack = [self]
+ while stack:
+ info = stack.pop()
+ for key, ci in info._children:
+ if ci.issection():
+ t = ci.sectiontype
+ if not d.has_key(t.name):
+ d[t.name] = 1
+ stack.append(t)
+ return d.keys()
+
+ def getsectionindex(self, type, name):
+ index = -1
+ for key, info in self._children:
+ index += 1
+ if key:
+ if key == name:
+ if not info.issection():
+ raise ZConfig.ConfigurationError(
+ "section name %s already in use for key" % key)
+ st = info.sectiontype
+ if st.istypegroup():
+ try:
+ st = st.gettype(type)
+ except ZConfig.ConfigurationError:
+ raise ZConfig.ConfigurationError(
+ "section type %s not allowed for name %s"
+ % (`type`, `key`))
+ if not st.name == type:
+ raise ZConfig.ConfigurationError(
+ "name %s must be used for a %s section"
+ % (`name`, `st.name`))
+ return index
+ # else must be a section or a sectiongroup:
+ elif info.sectiontype.name == type:
+ if not (name or info.allowUnnamed()):
+ raise ZConfig.ConfigurationError(
+ `type` + " sections must be named")
+ return index
+ elif info.sectiontype.istypegroup():
+ st = info.sectiontype
+ if st.name == type:
+ raise ZConfig.ConfigurationError(
+ "cannot define section with a sectiongroup type")
+ try:
+ st = st.gettype(type)
+ except ZConfig.ConfigurationError:
+ # not this one; maybe a different one
+ pass
+ else:
+ return index
+ raise ZConfig.ConfigurationError("no matching section defined")
+
+ def getsectioninfo(self, type, name):
+ i = self.getsectionindex(type, name)
+ st = self._children[i][1]
+ if st.istypegroup():
+ st = st.gettype(type)
+ return st
+
+ def istypegroup(self):
+ return False
+
+
+class SchemaType(TypeContainer, SectionType):
+ def __init__(self, name, keytype, valuetype, datatype, handler, url):
+ SectionType.__init__(self, name, keytype, valuetype, datatype)
+ TypeContainer.__init__(self)
+ self.handler = handler
+ self.url = url
+
+ def allowUnnamed(self):
+ return True
+
+ def isAllowedName(self, name):
+ return False
+
+ def issection(self):
+ return True
+
+ def getunusedtypes(self):
+ alltypes = self.gettypenames()
+ reqtypes = self.getrequiredtypes()
+ for n in reqtypes:
+ alltypes.remove(n)
+ if self.name and self.name in alltypes:
+ alltypes.remove(self.name)
+ return alltypes
=== Packages/ZConfig/loader.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:27 2003
+++ Packages/ZConfig/loader.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,242 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
+"""Schema loader utility."""
+
+import os.path
+import urllib
+import urllib2
+
+import ZConfig
+
+from ZConfig import datatypes
+from ZConfig import matcher
+from ZConfig.url import urlnormalize, urldefrag, urljoin, urlsplit, urlunsplit
+
+try:
+ True
+except NameError:
+ True = 1
+ False = 0
+
+
+RESOURCE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "resources")
+
+
+def loadSchema(url):
+ return SchemaLoader().loadURL(url)
+
+def loadSchemaFile(file, url=None):
+ return SchemaLoader().loadFile(file, url)
+
+def loadConfig(schema, url):
+ return ConfigLoader(schema).loadURL(url)
+
+def loadConfigFile(schema, file, url=None):
+ return ConfigLoader(schema).loadFile(file, url)
+
+
+class BaseLoader:
+ def __init__(self):
+ pass
+
+ def createResource(self, file, url, fragment=None):
+ return Resource(file, url, fragment)
+
+ def loadURL(self, url):
+ url = self.normalizeURL(url)
+ r = self.openResource(url)
+ try:
+ return self.loadResource(r)
+ finally:
+ r.close()
+
+ def loadFile(self, file, url=None):
+ if not url:
+ url = _url_from_file(file)
+ r = self.createResource(file, url)
+ return self.loadResource(r)
+
+ # utilities
+
+ def loadResource(self, resource):
+ raise NotImpementedError(
+ "BaseLoader.loadResource() must be overridden by a subclass")
+
+ def openResource(self, url):
+ # XXX This should be replaced to use a local cache for remote
+ # resources. The policy needs to support both re-retrieve on
+ # change and provide the cached resource when the remote
+ # resource is not accessible.
+ parts = list(urlsplit(url))
+ fragment = parts[-1]
+ if fragment:
+ parts[-1] = ''
+ url = urlunsplit(tuple(parts))
+ if parts[0] == 'zconfig':
+ file = _open_resource_file(url)
+ else:
+ file = urllib2.urlopen(url)
+ return self.createResource(file, url, fragment or None)
+
+ def normalizeURL(self, url):
+ if os.path.exists(url):
+ url = "file://" + urllib.pathname2url(os.path.abspath(url))
+ else:
+ url = urlnormalize(url)
+ if url and not self.allowFragments():
+ url, fragment = urldefrag(url)
+ if fragment:
+ raise ZConfig.ConfigurationError(
+ "fragment identifiers are not supported")
+ return url
+
+ def allowFragments(self):
+ return False
+
+
+def _url_from_file(file):
+ name = getattr(file, "name", None)
+ if name and name[0] != "<" and name[-1] != ">":
+ return "file://" + urllib.pathname2url(os.path.abspath(name))
+ else:
+ return None
+
+
+def _open_resource_file(url):
+ parts = urlsplit(url)
+ assert parts[0] == 'zconfig'
+ fn = os.path.join(RESOURCE_DIR, parts[2])
+ if not os.path.isfile(fn):
+ raise ValueError("no such resource: " + `parts[2]`)
+ return open(fn)
+
+
+class SchemaLoader(BaseLoader):
+ def __init__(self, registry=None):
+ if registry is None:
+ registry = datatypes.Registry()
+ self.registry = registry
+ self._cache = {}
+ BaseLoader.__init__(self)
+
+ def loadResource(self, resource):
+ if resource.url and self._cache.has_key(resource.url):
+ schema = self._cache[resource.url]
+ else:
+ from ZConfig.schema import parseResource
+ schema = parseResource(resource, self.registry, self)
+ self._cache[resource.url] = schema
+ if resource.fragment:
+ type = self.registry.get("basic-key")(resource.fragment)
+ schema = schema.gettype(type)
+ return schema
+
+ def allowFragments(self):
+ return True
+
+
+class ConfigLoader(BaseLoader):
+ def __init__(self, schema):
+ if schema.istypegroup():
+ raise ZConfig.SchemaError(
+ "cannot check a configuration an abstract type")
+ BaseLoader.__init__(self)
+ self.schema = schema
+
+ def loadResource(self, resource):
+ self.handlers = []
+ sm = matcher.SchemaMatcher(self.schema, self.handlers)
+ self._parse_resource(sm, resource)
+ return sm.finish(), CompositeHandler(self.handlers, self.schema)
+
+ # parser support API
+
+ def startSection(self, parent, type, name, delegatename):
+ if delegatename:
+ raise NotImpementedError("section delegation is not yet supported")
+ t = self.schema.gettype(type)
+ if t.istypegroup():
+ raise ZConfig.ConfigurationError(
+ "concrete sections cannot match abstract section types;"
+ " found abstract type " + `type`)
+ ci = parent.type.getsectioninfo(type, name)
+ assert not ci.istypegroup()
+ if not ci.isAllowedName(name):
+ raise ZConfig.ConfigurationError(
+ "%s is not an allowed name for %s sections"
+ % (`name`, `ci.sectiontype.name`))
+ return matcher.SectionMatcher(ci, t, name, self.handlers)
+
+ def endSection(self, parent, type, name, delegatename, matcher):
+ assert not delegatename
+ sectvalue = matcher.finish()
+ parent.addSection(type, name, sectvalue)
+
+ def includeConfiguration(self, section, url):
+ r = self.openResource(url)
+ self._parse_resource(section, r)
+
+ # internal helper
+
+ def _parse_resource(self, matcher, resource):
+ from ZConfig.cfgparser import ZConfigParser
+ parser = ZConfigParser(resource, self)
+ parser.parse(matcher)
+
+
+class CompositeHandler:
+ def __init__(self, handlers, schema):
+ self._handlers = handlers
+ self._convert = schema.registry.get("basic-key")
+
+ def __call__(self, handlermap):
+ d = {}
+ for name, callback in handlermap.items():
+ n = self._convert(name)
+ if d.has_key(n):
+ raise ZConfig.ConfigurationError(
+ "handler name not unique when converted to a basic-key: "
+ + `name`)
+ d[n] = callback
+ L = []
+ for handler, value in self._handlers:
+ if not d.has_key(handler):
+ L.append(handler)
+ if L:
+ raise ZConfig.ConfigurationError(
+ "undefined handlers: " + ", ".join(L))
+ for handler, value in self._handlers:
+ f = d[handler]
+ if f is not None:
+ f(value)
+
+ def __len__(self):
+ return len(self._handlers)
+
+
+class Resource:
+ def __init__(self, file, url, fragment=None):
+ self.file = file
+ self.url = url
+ self.fragment = fragment
+
+ def close(self):
+ if self.file is not None:
+ self.file.close()
+ self.file = None
+ self.closed = True
+
+ def __getattr__(self, name):
+ return getattr(self.file, name)
=== Packages/ZConfig/matcher.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:27 2003
+++ Packages/ZConfig/matcher.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,284 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
+"""Utility that manages the binding of configuration data to a section."""
+
+import types
+
+import ZConfig
+
+
+class BaseMatcher:
+ def __init__(self, info, type, handlers):
+ self.info = info
+ self.type = type
+ self._values = [None] * len(type)
+ self._sectionnames = {}
+ if handlers is None:
+ handlers = []
+ self._handlers = handlers
+
+ def __repr__(self):
+ clsname = self.__class__.__name__
+ extra = "type " + `self.type.name`
+ return "<%s for %s>" % (clsname, extra)
+
+ def addSection(self, type, name, sectvalue):
+ if name:
+ if self._sectionnames.has_key(name):
+ raise ZConfig.ConfigurationError(
+ "section names must not be re-used within the"
+ " same container:" + `name`)
+ self._sectionnames[name] = name
+ i = self.type.getsectionindex(type, name)
+ ci = self.type.getsectioninfo(type, name)
+ v = self._values[i]
+ if v is None:
+ if ci.ismulti():
+ self._values[i] = [sectvalue]
+ else:
+ self._values[i] = sectvalue
+ elif ci.ismulti():
+ v.append(sectvalue)
+ else:
+ raise ZConfig.ConfigurationError(
+ "too many instances of %s section" % `ci.sectiontype.name`)
+
+ def addValue(self, key, value):
+ length = len(self.type)
+ arbkey_info = None
+ for i in range(length):
+ k, ci = self.type[i]
+ if k == key:
+ break
+ if ci.name == "+" and not ci.issection():
+ arbkey_info = i, k, ci
+ else:
+ if arbkey_info is None:
+ raise ZConfig.ConfigurationError(
+ `key` + " is not a known key name")
+ i, k, ci = arbkey_info
+ if ci.issection():
+ if ci.name:
+ extra = " in %s sections" % `self.type.name`
+ else:
+ extra = ""
+ raise ZConfig.ConfigurationError(
+ "%s is not a valid key name%s" % (`key`, extra))
+
+ ismulti = ci.ismulti()
+ v = self._values[i]
+ if v is None:
+ if k == '+':
+ v = {}
+ elif ismulti:
+ v = []
+ self._values[i] = v
+ elif not ismulti:
+ if k != '+':
+ raise ZConfig.ConfigurationError(
+ `key` + " does not support multiple values")
+ elif len(v) == ci.maxOccurs:
+ raise ZConfig.ConfigurationError(
+ "too many values for " + `name`)
+
+ if k == '+':
+ if ismulti:
+ if v.has_key(key):
+ v[key].append(value)
+ else:
+ v[key] = [value]
+ else:
+ if v.has_key(key):
+ raise ZConfig.ConfigurationError("too many")
+ v[key] = value
+ elif ismulti:
+ v.append(value)
+ else:
+ self._values[i] = value
+
+ def finish(self):
+ """Check the constraints of the section and convert to an application
+ object."""
+ length = len(self.type)
+ values = self._values
+ attrnames = [None] * length
+ for i in range(length):
+ key, ci = self.type[i]
+ attrnames[i] = ci.attribute or key
+ v = values[i]
+ if v is None and ci.name == '+' and not ci.issection():
+ if ci.minOccurs > 0:
+ raise ZConfig.ConfigurationError(
+ "no keys defined for the %s key/value map; at least %d"
+ " must be specified" % (ci.attribute, ci.minOccurs))
+ v = {}
+ values[i] = v
+ if v is None and ci.minOccurs:
+ default = ci.getdefault()
+ if default is None:
+ if key:
+ s = `key`
+ else:
+ s = "section type " + `ci.typename`
+ raise ZConfig.ConfigurationError(
+ "no values for %s; %s required" % (s, ci.minOccurs))
+ else:
+ v = values[i] = default[:]
+ if ci.ismulti():
+ if v is None:
+ v = values[i] = ci.getdefault()[:]
+ if len(v) < ci.minOccurs:
+ raise ZConfig.ConfigurationError(
+ "not enough values for %s; %d found, %d required"
+ % (`key`, len(v), ci.minOccurs))
+ if v is None and not ci.issection():
+ if ci.ismulti():
+ v = ci.getdefault()[:]
+ else:
+ v = ci.getdefault()
+ values[i] = v
+ return self.constuct(attrnames)
+
+ def constuct(self, attrnames):
+ values = self._values
+ for i in range(len(values)):
+ name, ci = self.type[i]
+ if ci.ismulti():
+ if ci.issection():
+ v = []
+ for s in values[i]:
+ if s is not None:
+ v.append(s._type.datatype(s))
+ else:
+ v.append(None)
+ elif ci.name == '+':
+ v = values[i]
+ for key, val in v.items():
+ v[key] = [ci.datatype(s) for s in val]
+ else:
+ v = [ci.datatype(s) for s in values[i]]
+ elif ci.issection():
+ if values[i] is not None:
+ v = values[i]._type.datatype(values[i])
+ else:
+ v = None
+ elif name == '+':
+ v = values[i]
+ for key, val in v.items():
+ v[key] = ci.datatype(val)
+ else:
+ v = values[i]
+ if v is not None:
+ v = ci.datatype(v)
+ values[i] = v
+ if ci.handler is not None:
+ self._handlers.append((ci.handler, v))
+ return self.createValue(attrnames)
+
+ def createValue(self, attrnames):
+ return SectionValue(attrnames, self._values, None, self.type)
+
+
+class SectionMatcher(BaseMatcher):
+ def __init__(self, info, type, name, handlers):
+ if name or info.allowUnnamed():
+ self.name = name
+ else:
+ raise ZConfig.ConfigurationError(
+ `type.name` + " sections may not be unnamed")
+ BaseMatcher.__init__(self, info, type, handlers)
+
+ def createValue(self, attrnames):
+ return SectionValue(attrnames, self._values, self.name, self.type)
+
+
+class SchemaMatcher(BaseMatcher):
+ def __init__(self, info, handlers=None):
+ BaseMatcher.__init__(self, info, info, handlers)
+
+ def finish(self):
+ # Since there's no outer container to call datatype()
+ # for the schema, we convert on the way out.
+ v = BaseMatcher.finish(self)
+ v = self.type.datatype(v)
+ if self.type.handler is not None:
+ self._handlers.append((self.type.handler, v))
+ return v
+
+
+class SectionValue:
+ """Generic 'bag-of-values' object for a section.
+
+ Derived classes should always call the SectionValue constructor
+ before attempting to modify self.
+ """
+
+ def __init__(self, attrnames, values, name, type):
+ d = self.__dict__
+ d['_attrnames'] = attrnames
+ d['_values'] = values
+ d['_name'] = name
+ d['_type'] = type
+
+ def __repr__(self):
+ if self._name:
+ # probably unique for a given config file; more readable than id()
+ name = `self._name`
+ else:
+ # identify uniquely
+ name = "at %#x" % id(self)
+ clsname = self.__class__.__name__
+ return "<%s for %s %s>" % (clsname, self._type.name, name)
+
+ def __len__(self):
+ return len(self._values)
+
+ def __getitem__(self, index):
+ if isinstance(index, types.SliceType):
+ raise TypeError("SectionValue does not support slicing")
+ return self._values[index]
+
+ def __setitem__(self, index, value):
+ if isinstance(index, types.SliceType):
+ raise TypeError("SectionValue does not support slicing")
+ self._values[index] = value
+
+ def __getattr__(self, name):
+ try:
+ i = self._attrnames.index(name)
+ except ValueError:
+ raise AttributeError, name
+ return self._values[i]
+
+ def __setattr__(self, name, value):
+ try:
+ i = self._attrnames.index(name)
+ except ValueError:
+ self.__dict__[name] = value
+ else:
+ self._values[i] = value
+
+ def __str__(self):
+ l = []
+ for i in range(len(self._attrnames)):
+ k = self._attrnames[i]
+ v = self._values[i]
+ l.append('%-40s: %s' % (k, v))
+ return '\n'.join(l)
+
+ def getSectionName(self):
+ return self._name
+
+ def getSectionType(self):
+ return self._type.name
=== Packages/ZConfig/schema.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:28 2003
+++ Packages/ZConfig/schema.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,393 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
+"""Parser for ZConfig schemas."""
+
+import xml.sax
+import xml.sax.saxutils
+
+import ZConfig
+
+from ZConfig import info
+from ZConfig import url
+
+
+try:
+ True
+except NameError:
+ True = 1
+ False = 0
+
+try:
+ dict
+except NameError:
+ def dict(mapping):
+ d = {}
+ for k, v in mapping.items():
+ d[k] = v
+ return d
+
+
+def parseResource(resource, registry, loader):
+ parser = SchemaParser(registry, loader, resource.url)
+ xml.sax.parse(resource.file, parser)
+ return parser._schema
+
+
+class SchemaParser(xml.sax.ContentHandler):
+
+ _cdata_tags = "description", "metadefault", "example", "default"
+ _handled_tags = ("schema", "import", "sectiongroup", "sectiontype",
+ "key", "multikey", "section", "multisection")
+
+ def __init__(self, registry, loader, url):
+ self._registry = registry
+ self._loader = loader
+ self._basic_key = registry.get("basic-key")
+ self._identifier = registry.get("identifier")
+ self._cdata = None
+ self._locator = None
+ self._prefixes = []
+ self._schema = None
+ self._stack = []
+ self._group = None
+ self._url = url
+
+ # SAX 2 ContentHandler methods
+
+ def setDocumentLocator(self, locator):
+ self._locator = locator
+
+ def startElement(self, name, attrs):
+ attrs = dict(attrs)
+ if name == "schema":
+ if self._schema is not None:
+ self.error("schema element improperly nested")
+ self.start_schema(attrs)
+ elif name in self._handled_tags:
+ if self._schema is None:
+ self.error(name + " element outside of schema")
+ getattr(self, "start_" + name)(attrs)
+ elif name in self._cdata_tags:
+ if self._schema is None:
+ self.error(name + " element outside of schema")
+ if self._cdata is not None:
+ self.error(name + " element improperly nested")
+ self._cdata = []
+ else:
+ self.error("Unknown tag " + name)
+
+ def characters(self, data):
+ if self._cdata is not None:
+ self._cdata.append(data)
+ elif data.strip():
+ self.error("unexpected non-blank character data: "
+ + `data.strip()`)
+
+ def endElement(self, name):
+ if name in self._handled_tags:
+ getattr(self, "end_" + name)()
+ else:
+ data = ''.join(self._cdata).strip()
+ self._cdata = None
+ if name == "default":
+ # value for a key
+ self._stack[-1].adddefault(data)
+ else:
+ setattr(self._stack[-1], name, data)
+
+ def endDocument(self):
+ if self._schema is None:
+ self.error("no schema found")
+
+ # schema loading logic
+
+ def start_import(self, attrs):
+ src = attrs.get("src", "").strip()
+ pkg = attrs.get("package", "").strip()
+ if not (src or pkg):
+ self.error("import must specify either src or package")
+ if src and pkg:
+ self.error("import may only specify one of src or package")
+ if src:
+ src = url.urljoin(self._url, src)
+ src, fragment = url.urldefrag(src)
+ if fragment:
+ self.error("import src many not include a fragment identifier")
+ urls = [src]
+ else:
+ raise NotImpementedError(
+ "<import package='...'/> not yet implemented")
+ for s in urls:
+ schema = self._loader.loadURL(s)
+ for n in schema.gettypenames():
+ self._schema.addtype(schema.gettype(n))
+
+ def end_import(self):
+ pass
+
+ def get_handler(self, attrs):
+ v = attrs.get("handler")
+ if v is None:
+ return v
+ else:
+ return self.basic_key(v)
+
+ def push_prefix(self, attrs):
+ name = attrs.get("prefix")
+ if name:
+ if name.startswith(".") and self._prefixes:
+ prefix = self._prefixes[-1] + name
+ elif name.startswith("."):
+ self.error("prefix may not begin with '.'")
+ else:
+ prefix = name
+ elif self._prefixes:
+ prefix = self._prefixes[-1]
+ else:
+ prefix = ''
+ self._prefixes.append(prefix)
+
+ def get_classname(self, name):
+ if name.startswith("."):
+ return self._prefixes[-1] + name
+ else:
+ return name
+
+ def get_datatype(self, attrs, attrkey, default):
+ if attrs.has_key(attrkey):
+ dtname = self.get_classname(attrs[attrkey])
+ else:
+ dtname = default
+
+ try:
+ return self._registry.get(dtname)
+ except ValueError, e:
+ self.error(e[0])
+
+ def get_sect_typeinfo(self, attrs):
+ keytype = self.get_datatype(attrs, "keytype", "basic-key")
+ valuetype = self.get_datatype(attrs, "valuetype", "string")
+ datatype = self.get_datatype(attrs, "datatype", "null")
+ return keytype, valuetype, datatype
+
+ def start_schema(self, attrs):
+ self.push_prefix(attrs)
+ handler = self.get_handler(attrs)
+ keytype, valuetype, datatype = self.get_sect_typeinfo(attrs)
+ name = attrs.get("type")
+ if name is not None:
+ name = self.basic_key(name)
+ self._schema = info.SchemaType(name, keytype, valuetype, datatype,
+ handler, self._url)
+ if name is not None:
+ # XXX circular reference
+ self._schema.addtype(self._schema)
+ self._schema.registry = self._registry
+ self._stack = [self._schema]
+
+ def end_schema(self):
+ del self._prefixes[-1]
+ assert not self._prefixes
+
+ def start_sectiontype(self, attrs):
+ name = attrs.get("type")
+ if not name:
+ self.error("sectiontype type must not be omitted or empty")
+ name = self.basic_key(name)
+ self.push_prefix(attrs)
+ keytype, valuetype, datatype = self.get_sect_typeinfo(attrs)
+ sectinfo = info.SectionType(name, keytype, valuetype, datatype)
+ self._schema.addtype(sectinfo)
+ if self._group is not None:
+ if attrs.has_key("group"):
+ self.error("sectiontype cannot specify group"
+ " if nested in a sectiongroup")
+ self._group.addtype(sectinfo)
+ elif attrs.has_key("group"):
+ groupname = self.basic_key(attrs["group"])
+ group = self._schema.gettype(groupname)
+ if not group.istypegroup():
+ self.error("type specified as group is not a sectiongroup")
+ group.addtype(sectinfo)
+ self._stack.append(sectinfo)
+
+ def end_sectiontype(self):
+ del self._prefixes[-1]
+ self._stack.pop()
+
+ def get_required(self, attrs):
+ if attrs.has_key("required"):
+ v = attrs["required"]
+ if v == "yes":
+ return True
+ elif v == "no":
+ return False
+ self.error("value for 'required' must be 'yes' or 'no'")
+ else:
+ return False
+
+ def get_ordinality(self, attrs):
+ min, max = 0, info.Unbounded
+ if self.get_required(attrs):
+ min = 1
+ return min, max
+
+ def get_sectiontype(self, attrs):
+ type = attrs.get("type")
+ if not type:
+ self.error("section must specify type")
+ return self._schema.gettype(type)
+
+ def start_section(self, attrs):
+ sectiontype = self.get_sectiontype(attrs)
+ handler = self.get_handler(attrs)
+ min = self.get_required(attrs) and 1 or 0
+ any, name, attribute = self.get_name_info(attrs, "section")
+ if any and not attribute:
+ self.error(
+ "attribute must be specified if section name is '*' or '+'")
+ section = info.SectionInfo(any or name, sectiontype,
+ min, 1, handler, attribute)
+ self._stack[-1].addsection(name, section)
+ self._stack.append(section)
+
+ def end_section(self):
+ self._stack.pop()
+
+ def start_multisection(self, attrs):
+ sectiontype = self.get_sectiontype(attrs)
+ min, max = self.get_ordinality(attrs)
+ any, name, attribute = self.get_name_info(attrs, "multisection")
+ if any not in ("*", "+"):
+ self.error("multisection must specify '*' or '+' for the name")
+ handler = self.get_handler(attrs)
+ section = info.SectionInfo(any or name, sectiontype,
+ min, max, handler, attribute)
+ self._stack[-1].addsection(name, section)
+ self._stack.append(section)
+
+ def end_multisection(self):
+ self._stack.pop()
+
+ def start_sectiongroup(self, attrs):
+ if self._group is not None:
+ self.error("sectiongroup elements cannot be nested")
+ self.push_prefix(attrs)
+ name = attrs.get("type")
+ if not name:
+ self.error("sectiongroup must be named")
+ name = self.basic_key(name)
+ self._group = info.GroupType(name)
+ self._schema.addtype(self._group)
+ self._stack.append(self._group)
+
+ def end_sectiongroup(self):
+ del self._prefixes[-1]
+ self._group = None
+ self._stack.pop()
+
+ def get_key_info(self, attrs, element):
+ any, name, attribute = self.get_name_info(attrs, element)
+ if any == '*':
+ self.error(element + " may not specify '*' for name")
+ if not name and any != '+':
+ self.error(element + " name may not be omitted or empty")
+ datatype = self.get_datatype(attrs, "datatype", "string")
+ handler = self.get_handler(attrs)
+ return name or any, datatype, handler, attribute
+
+ def start_key(self, attrs):
+ name, datatype, handler, attribute = self.get_key_info(attrs, "key")
+ min = self.get_required(attrs) and 1 or 0
+ key = info.KeyInfo(name, datatype, min, 1, handler, attribute)
+ if attrs.has_key("default"):
+ if min:
+ self.error("required key cannot have a default value")
+ key.adddefault(str(attrs["default"]).strip())
+ key.finish()
+ self._stack[-1].addkey(key)
+ self._stack.append(key)
+
+ def end_key(self):
+ self._stack.pop()
+
+ def start_multikey(self, attrs):
+ if attrs.has_key("default"):
+ self.error("default values for multikey must be given using"
+ " 'default' elements")
+ name, datatype, handler, attribute = self.get_key_info(attrs,
+ "multikey")
+ min, max = self.get_ordinality(attrs)
+ key = info.KeyInfo(name, datatype, min, max, handler, attribute)
+ self._stack[-1].addkey(key)
+ self._stack.append(key)
+
+ def end_multikey(self):
+ self._stack.pop().finish()
+
+ def get_name_info(self, attrs, element):
+ name = attrs.get("name")
+ if not name:
+ self.error(element + " name must be specified and non-empty")
+ aname = attrs.get("attribute")
+ if aname:
+ aname = self.identifier(aname)
+ if aname.startswith("getSection"):
+ # reserved; used for SectionValue methods to get meta-info
+ self.error("attribute names may not start with 'getSection'")
+ if name in ("*", "+"):
+ if not aname:
+ self.error(
+ "container attribute must be specified and non-empty"
+ " when using '*' or '+' for a section name")
+ return name, None, aname
+ else:
+ # run the keytype converter to make sure this is a valid key
+ name = self._stack[-1].keytype(name)
+ if not aname:
+ aname = self.basic_key(name)
+ aname = self.identifier(aname.replace('-', '_'))
+ return None, self.basic_key(name), aname
+
+ # datatype conversion wrappers
+
+ def basic_key(self, s):
+ try:
+ return self._basic_key(s)
+ except ValueError, e:
+ self.error(e[0])
+ except ZConfig.SchemaError, e:
+ self.initerror(e)
+ raise
+
+ def identifier(self, s):
+ try:
+ return self._identifier(s)
+ except ValueError, e:
+ self.error(e[0])
+ except ZConfig.SchemaError, e:
+ self.initerror(e)
+ raise
+
+ # exception setup helpers
+
+ def initerror(self, e):
+ if self._locator is not None:
+ e.colno = self._locator.getColumnNumber()
+ e.lineno = self._locator.getLineNumber()
+ e.url = self._locator.getSystemId()
+ return e
+
+ def error(self, message):
+ raise self.initerror(ZConfig.SchemaError(message))
=== Packages/ZConfig/substitution.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:28 2003
+++ Packages/ZConfig/substitution.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,91 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
+"""Substitution support for ZConfig values."""
+
+import ZConfig
+
+try:
+ True
+except NameError:
+ True = 1
+ False = 0
+
+
+def substitute(s, mapping):
+ """Interpolate variables from `section` into `s`."""
+ if "$" in s:
+ result = ''
+ rest = s
+ while rest:
+ p, name, rest = _split(rest)
+ result += p
+ if name:
+ v = mapping.get(name)
+ if v is None:
+ raise ZConfig.SubstitutionReplacementError(s, name)
+ result += v
+ return result
+ else:
+ return s
+
+
+def isname(s):
+ """Return True iff s is a valid substitution name."""
+ m = _name_match(s)
+ if m:
+ return m.group() == s
+ else:
+ return False
+
+
+def _split(s):
+ # Return a triple: prefix, name, suffix
+ # - prefix is text that can be used literally in the result (may be '')
+ # - name is a referenced name, or None
+ # - suffix is trailling text that may contain additional references
+ # (may be '' or None)
+ if "$" in s:
+ i = s.find("$")
+ c = s[i+1:i+2]
+ if c == "":
+ raise ZConfig.SubstitutionSyntaxError(
+ "illegal lone '$' at end of source")
+ if c == "$":
+ return s[:i+1], None, s[i+2:]
+ prefix = s[:i]
+ if c == "{":
+ m = _name_match(s, i + 2)
+ if not m:
+ raise ZConfig.SubstitutionSyntaxError(
+ "'${' not followed by name")
+ name = m.group(0)
+ i = m.end() + 1
+ if not s.startswith("}", i - 1):
+ raise ZConfig.SubstitutionSyntaxError(
+ "'${%s' not followed by '}'" % name)
+ else:
+ m = _name_match(s, i+1)
+ if not m:
+ raise ZConfig.SubstitutionSyntaxError(
+ "'$' not followed by '$' or name")
+ name = m.group(0)
+ i = m.end()
+ return prefix, name.lower(), s[i:]
+ else:
+ return s, None, None
+
+
+import re
+_name_match = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*").match
+del re
=== Packages/ZConfig/url.py 1.1 => 1.2 ===
--- /dev/null Fri Jan 3 16:06:28 2003
+++ Packages/ZConfig/url.py Fri Jan 3 16:05:51 2003
@@ -0,0 +1,94 @@
+"""urlparse-like helpers that support the zconfig scheme."""
+
+import posixpath as _posixpath
+import urlparse as _urlparse
+
+from urllib import splittype as _splittype
+
+
+def urlnormalize(url):
+ parts = urlsplit(url)
+ if not parts[0]:
+ raise ValueError("invalid URL, or file does not exist:\n"
+ + repr(url))
+ url = urlunsplit(parts)
+ if url.startswith("file:/") and not url.startswith("file:///"):
+ url = "file://" + url[5:]
+ return url
+
+
+def urlunsplit(parts):
+ url = _urlparse.urlunsplit(parts)
+ if ( parts[0] == "file"
+ and url.startswith("file:/")
+ and not url.startswith("file:///")):
+ url = "file://" + url[5:]
+ return url
+
+
+def urldefrag(url):
+ parts = urlsplit(url)
+ if parts[0] == "zconfig":
+ return "zconfig:" + parts[2], parts[4]
+ else:
+ url, fragment = _urlparse.urldefrag(url)
+ return urlnormalize(url), fragment
+
+
+def urljoin(base, relurl):
+ scheme = _splittype(base)[0]
+ if scheme != "zconfig":
+ url = _urlparse.urljoin(base, relurl)
+ if url.startswith("file:/") and not url.startswith("file:///"):
+ url = "file://" + url[5:]
+ return url
+ relscheme = _splittype(relurl)[0]
+ if relscheme and relscheme != "zconfig":
+ return _urlparse.urljoin(base, relurl)
+ baseparts = urlsplit(base)
+ relparts = urlsplit(relurl, "zconfig")
+ if relparts[2]:
+ d = _posixpath.dirname(baseparts[2])
+ if d:
+ d += "/"
+ path = _posixpath.normpath(_posixpath.join(d, relparts[2]))
+ else:
+ path = baseparts[2]
+ parts = path.split('/')
+ if '..' in parts:
+ raise ValueError("zconfig URIs cannot include '..' references: "
+ + `path`)
+ s = "zconfig:" + path
+ if relparts[4]:
+ s += "#" + relparts[4]
+ return s
+
+
+def urlsplit(url, scheme=''):
+ stated_scheme = _splittype(url)[0]
+ scheme = stated_scheme or scheme
+ parts = _urlparse.urlsplit(url, scheme=scheme)
+ if scheme == "zconfig":
+ path = parts[2]
+ if stated_scheme:
+ # These constraints only apply to absolute zconfig: URLs
+ if not path:
+ # Require a non-empty path; empty path is ok for
+ # relative URL ("#frag").
+ raise ValueError(
+ "zconfig URIs require a non-empty path component")
+ if '..' in path.split('/'):
+ raise ValueError(
+ "zconfig URIs cannot include '..' references: " + `url`)
+ # Split the fragment ourselves since the urlparse module
+ # doesn't know about the zconfig: scheme.
+ if '#' in path:
+ path, fragment = path.split('#', 1)
+ parts = "zconfig", '', path, '', fragment
+ if path[:1] == '/':
+ raise ValueError(
+ "path component of zconfig: URIs may not start with '/'")
+ if '?' in path:
+ raise ValueError("zconfig: URIs may not contain queries: "
+ + `url`)
+ return parts
=== Packages/ZConfig/Config.py 1.14 => 1.15 ===
--- Packages/ZConfig/Config.py:1.14 Thu Dec 5 00:17:45 2002
+++ Packages/ZConfig/Config.py Fri Jan 3 16:05:51 2003
@@ -1,12 +1,22 @@
+##############################################################################
+#
+# 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.0 (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 structure."""
import ZConfig
-try:
- True
-except NameError:
- True = 1
- False = 0
+from ZConfig.datatypes import asBoolean
+
class Configuration:
def __init__(self, container, type, name, url):
@@ -103,12 +113,10 @@
def has_key(self, key):
key = key.lower()
- if self._data.has_key(key):
- return True
- elif self.delegate:
- return self.delegate.has_key(key)
- else:
- return False
+ have = self._data.has_key(key)
+ if self.delegate and not have:
+ have = self.delegate.has_key(key)
+ return have
def items(self):
"""Returns a list of key-value pairs for this section.
@@ -190,32 +198,3 @@
return default
else:
return s.split()
-
-
-class ImportingConfiguration(Configuration):
- def __init__(self, url):
- self._imports = []
- Configuration.__init__(self, None, None, None, url)
-
- def addImport(self, section):
- self._imports.append(section)
-
- def get(self, key, default=None):
- s = Configuration.get(self, key, default)
- if s is default:
- for config in self._imports:
- s = config.get(key, default)
- if s is not default:
- break
- return s
-
-
-def asBoolean(s):
- """Convert a string value to a boolean value."""
- ss = str(s).lower()
- if ss in ('yes', 'true', 'on'):
- return True
- elif ss in ('no', 'false', 'off'):
- return False
- else:
- raise ValueError("not a valid boolean value: " + repr(s))
=== Packages/ZConfig/Context.py 1.17 => 1.18 ===
--- Packages/ZConfig/Context.py:1.17 Thu Jan 2 13:29:55 2003
+++ Packages/ZConfig/Context.py Fri Jan 3 16:05:51 2003
@@ -1,40 +1,41 @@
+##############################################################################
+#
+# 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.0 (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.
+#
+##############################################################################
"""Top-level configuration handle."""
-import os
-import urllib
-import urllib2
import urlparse
import ZConfig
-from Config import Configuration, ImportingConfiguration
-from Substitution import isname, substitute
+from ZConfig import loader
+from ZConfig.Config import Configuration
-class Context:
+class Context(loader.BaseLoader):
def __init__(self):
- self._imports = [] # URL -> Configuration
+ loader.BaseLoader.__init__(self)
self._named_sections = {} # name -> Configuration
self._needed_names = {} # name -> [needy Configuration, ...]
- self._current_imports = []
self._all_sections = []
# subclass-support API
- def createImportedSection(self, section, url):
- return ImportingConfiguration(url)
-
def createNestedSection(self, section, type, name, delegatename):
- if name:
- name = name.lower()
- return Configuration(section, type.lower(), name, section.url)
+ return Configuration(section, type, name, section.url)
def createToplevelSection(self, url):
- return ImportingConfiguration(url)
-
- def createResource(self, file, url):
- return Resource(file, url)
+ return Configuration(None, None, None, url)
def getDelegateType(self, type):
# Applications must provide delegation typing information by
@@ -42,78 +43,26 @@
return type.lower()
def parse(self, resource, section):
- from ApacheStyle import Parse
- Parse(resource, self, section)
+ from ZConfig.cfgparser import ZConfigParser
+ ZConfigParser(resource, self).parse(section)
- def _normalize_url(self, url):
- if os.path.exists(url):
- url = "file://" + urllib.pathname2url(os.path.abspath(url))
- else:
- parts = urlparse.urlparse(url)
- if not parts[0]:
- raise ValueError("invalid URL, or file does not exist:\n"
- + repr(url))
- return url
-
- # public API
-
- def load(self, url):
- """Load a resource from a URL or pathname."""
- url = self._normalize_url(url)
- top = self.createToplevelSection(url)
+ def loadResource(self, resource):
+ top = self.createToplevelSection(resource.url)
self._all_sections.append(top)
- self._imports = [top]
- self._parse_url(url, top)
+ self.parse(resource, top)
self._finish()
return top
- loadURL = load # Forward-compatible alias
-
- def loadfile(self, file, url=None):
- if not url:
- name = getattr(file, "name", None)
- if name and name[0] != "<" and name[-1] != ">":
- url = "file://" + urllib.pathname2url(os.path.abspath(name))
- top = self.createToplevelSection(url)
- self._all_sections.append(top)
- self._imports = [top]
- self._current_imports.append(top)
- r = self.createResource(file, url)
- try:
- self.parse(r, top)
- finally:
- del self._current_imports[-1]
- self._finish()
- return top
-
- loadFile = loadfile # Forward-compatible alias
-
-
# interface for parser
- def importConfiguration(self, section, url):
- for config in self._imports:
- if config.url == url:
- return config
- newsect = self.createImportedSection(section, url)
- self._all_sections.append(newsect)
- self._imports.append(newsect)
- section.addImport(newsect)
- self._parse_url(url, newsect)
-
def includeConfiguration(self, section, url):
- # XXX we always re-parse, unlike import
- file = urllib2.urlopen(url)
- r = self.createResource(file, url)
+ r = self.openResource(url)
try:
self.parse(r, section)
finally:
- file.close()
+ r.close()
- def nestSection(self, section, type, name, delegatename):
- if name:
- name = name.lower()
- type = type.lower()
+ def startSection(self, section, type, name, delegatename):
if name and self._named_sections.has_key(name):
# Make sure sections of the same name are not defined
# twice in the same resource, and that once a name has
@@ -139,30 +88,12 @@
section.addChildSection(newsect)
if name:
self._named_sections[name] = newsect
- current = self._current_imports[-1]
- if section is not current:
- current.addNamedSection(newsect)
- for config in self._current_imports[:-1]:
- # XXX seems very painful
- if not config._sections_by_name.has_key((type, name)):
- config.addNamedSection(newsect)
return newsect
- # internal helpers
+ def endSection(self, parent, type, name, delegatename, section):
+ section.finish()
- def _parse_url(self, url, section):
- url, fragment = urlparse.urldefrag(url)
- if fragment:
- raise ZConfig.ConfigurationError(
- "fragment identifiers are not currently supported")
- file = urllib2.urlopen(url)
- self._current_imports.append(section)
- r = self.createResource(file, url)
- try:
- self.parse(r, section)
- finally:
- del self._current_imports[-1]
- file.close()
+ # internal helpers
def _finish(self):
# Resolve section delegations
@@ -199,24 +130,3 @@
if sect.delegate is not None:
sect.finish()
self._all_sections = None
-
-
-class Resource:
- def __init__(self, file, url):
- self.file = file
- self.url = url
- self._definitions = {}
-
- def define(self, name, value):
- key = name.lower()
- if self._definitions.has_key(key):
- raise ZConfig.ConfigurationError("cannot redefine " + `name`)
- if not isname(name):
- raise ZConfig.ConfigurationError(
- "not a substitution legal name: " + `name`)
- self._definitions[key] = value
-
- def substitute(self, s):
- # XXX I don't really like calling this substitute(),
- # XXX but it will do for now.
- return substitute(s, self._definitions)
=== Packages/ZConfig/__init__.py 1.3 => 1.4 ===
--- Packages/ZConfig/__init__.py:1.3 Thu Dec 5 00:17:45 2002
+++ Packages/ZConfig/__init__.py Fri Jan 3 16:05:51 2003
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2002 Zope Corporation and Contributors.
+# Copyright (c) 2002, 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -16,12 +16,100 @@
$Id$
"""
-from ZConfig.Exceptions import *
+from ZConfig.loader import loadConfig, loadConfigFile
+from ZConfig.loader import loadSchema, loadSchemaFile
-def load(url):
+
+def loadURL(url):
import Context
- return Context.Context().load(url)
+ return Context.Context().loadURL(url)
-def loadfile(file, url=None):
+def loadFile(file, url=None):
import Context
- return Context.Context().loadfile(file, url)
+ return Context.Context().loadFile(file, url)
+
+
+class ConfigurationError(Exception):
+ def __init__(self, msg):
+ self.message = msg
+ Exception.__init__(self, msg)
+
+ def __str__(self):
+ return self.message
+
+
+class _ParseError(ConfigurationError):
+ def __init__(self, msg, url, lineno, colno=None):
+ self.url = url
+ self.lineno = lineno
+ self.colno = colno
+ ConfigurationError.__init__(self, msg)
+
+ 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 ConfigurationMissingSectionError(ConfigurationError):
+ def __init__(self, type, name=None):
+ self.type = type
+ self.name = name
+ details = 'Missing section (type: %s' % type
+ if name is not None:
+ details += ', name: %s' % name
+ ConfigurationError.__init__(self, details + ')')
+
+
+class ConfigurationConflictingSectionError(ConfigurationError):
+ def __init__(self, type, name=None):
+ self.type = type
+ self.name = name
+ details = 'Conflicting sections (type: %s' % type
+ if name is not None:
+ details += ', name: %s' % name
+ ConfigurationError.__init__(self, details + ')')
+
+
+class ConfigurationSyntaxError(_ParseError):
+ """Raised when there's a syntax error in a configuration file."""
+
+
+class ConfigurationTypeError(ConfigurationError):
+ def __init__(self, msg, found, expected):
+ self.found = found
+ self.expected = expected
+ ConfigurationError.__init__(self, msg)
+
+
+class SubstitutionSyntaxError(ConfigurationError):
+ """Raised when interpolation source text contains syntactical errors."""
+
+
+class SubstitutionReplacementError(ConfigurationError, LookupError):
+ """Raised when no replacement is available for a reference."""
+
+ def __init__(self, source, name):
+ self.source = source
+ self.name = name
+ ConfigurationError.__init__(self, "no replacement for " + `name`)
=== Removed File Packages/ZConfig/ApacheStyle.py ===
=== Removed File Packages/ZConfig/Exceptions.py ===
=== Removed File Packages/ZConfig/Substitution.py ===