[Zope3-checkins] CVS: Zope/lib/python/ZConfig - cfgparser.py:1.1.4.1 datatypes.py:1.1.4.1 info.py:1.1.4.1 loader.py:1.1.4.1 matcher.py:1.1.4.1 schema.py:1.1.4.1 substitution.py:1.1.4.1 url.py:1.1.4.1 BRANCHES.txt:1.1.4.2 Config.py:1.2.4.7 Context.py:1.1.4.7 __init__.py:1.1.4.9
Chris McDonough
chrism@zope.com
Fri, 3 Jan 2003 10:52:34 -0500
Update of /cvs-repository/Zope/lib/python/ZConfig
In directory cvs.zope.org:/tmp/cvs-serv2937
Modified Files:
Tag: chrism-install-branch
BRANCHES.txt Config.py Context.py __init__.py
Added Files:
Tag: chrism-install-branch
cfgparser.py datatypes.py info.py loader.py matcher.py
schema.py substitution.py url.py
Log Message:
Merge zconfig-schema-devel-branch into chrism-install-branch. Why not? ;-)
=== Added File Zope/lib/python/ZConfig/cfgparser.py ===
##############################################################################
#
# Copyright (c) 2002 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
=== Added File Zope/lib/python/ZConfig/datatypes.py ===
##############################################################################
#
# Copyright (c) 2002 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
stock_datatypes = {
"boolean": asBoolean,
"identifier": IdentifierConversion().__call__,
"integer": integer,
"float": float_conversion,
"string": str,
"null": null_conversion,
"locale": MemoizedConversion(check_locale).__call__,
"port-number": port_number,
"basic-key": BasicKeyConversion().__call__,
"logging-level": LogLevelConversion().__call__,
"inet-address": inet_address,
"socket-address":socket_address,
"ipaddr-or-hostname":IpaddrOrHostname().__call__,
"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,
}
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
=== Added File Zope/lib/python/ZConfig/info.py ===
##############################################################################
#
# Copyright (c) 2002 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()
def finish(self):
if not self._types:
raise ZConfig.SchemaError(
"sectiongroup must contain at least on sectiontype")
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
=== Added File Zope/lib/python/ZConfig/loader.py ===
##############################################################################
#
# Copyright (c) 2002 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):
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`)
info = parent.info
info = getattr(info, "sectiontype", info)
ci = info.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)
=== Added File Zope/lib/python/ZConfig/matcher.py ===
##############################################################################
#
# Copyright (c) 2002 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__
return "<%s for >" % (clsname, )
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
=== Added File Zope/lib/python/ZConfig/schema.py ===
##############################################################################
#
# Copyright (c) 2002 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()
if not src:
self.error("import src may not be omitted or empty")
src = url.urljoin(self._url, src)
src, fragment = url.urldefrag(src)
if fragment:
self.error("import src many not include a fragment identifier")
schema = self._loader.loadURL(src)
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:
self._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")
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().finish()
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))
=== Added File Zope/lib/python/ZConfig/substitution.py ===
##############################################################################
#
# Copyright (c) 2002 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
=== Added File Zope/lib/python/ZConfig/url.py ===
"""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
=== Zope/lib/python/ZConfig/BRANCHES.txt 1.1.4.1 => 1.1.4.2 ===
--- Zope/lib/python/ZConfig/BRANCHES.txt:1.1.4.1 Thu Oct 10 14:29:12 2002
+++ Zope/lib/python/ZConfig/BRANCHES.txt Fri Jan 3 10:51:55 2003
@@ -6,3 +6,9 @@
An example of an alternate syntax for ZConfig. This syntax was
developed while trying the package initially, but was rejected.
It is saved on a branch to avoid losing historical information.
+
+zconfig-schema-devel-branch
+ Development branch for schema support in ZConfig. The branch is
+ based on the ZConfig trunk, but the development is strongly based
+ on the work Chris McDonough started in the chrism-install-branch
+ for Zope 2.
=== Zope/lib/python/ZConfig/Config.py 1.2.4.6 => 1.2.4.7 ===
--- Zope/lib/python/ZConfig/Config.py:1.2.4.6 Fri Dec 6 11:06:23 2002
+++ Zope/lib/python/ZConfig/Config.py Fri Jan 3 10:51:55 2003
@@ -1,12 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2002 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,21 +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
-
- 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.
@@ -199,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))
=== Zope/lib/python/ZConfig/Context.py 1.1.4.6 => 1.1.4.7 ===
--- Zope/lib/python/ZConfig/Context.py:1.1.4.6 Fri Dec 6 11:06:23 2002
+++ Zope/lib/python/ZConfig/Context.py Fri Jan 3 10:51:55 2003
@@ -1,40 +1,41 @@
+##############################################################################
+#
+# Copyright (c) 2002 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,74 +43,27 @@
return type.lower()
def parse(self, resource, section):
- from ApacheStyle import Parse
- Parse(resource, self, 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)
- self._all_sections.append(top)
- self._imports = [top]
- self._parse_url(url, top)
- self._finish()
- return top
+ from ZConfig.cfgparser import ZConfigParser
+ ZConfigParser(resource, self).parse(section)
- 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)
+ def loadResource(self, resource):
+ top = self.createToplevelSection(resource.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.parse(resource, top)
self._finish()
return top
# 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
@@ -135,30 +89,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
@@ -195,24 +131,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)
=== Zope/lib/python/ZConfig/__init__.py 1.1.4.8 => 1.1.4.9 ===
--- Zope/lib/python/ZConfig/__init__.py:1.1.4.8 Fri Dec 6 11:06:23 2002
+++ Zope/lib/python/ZConfig/__init__.py Fri Jan 3 10:51:55 2003
@@ -16,20 +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 loadschema(url, schema):
- from SchemaParser import SchemaContext
- return SchemaContext().load(url, schema)
-
-def loadschemafile(file, schema, url=None):
- from SchemaParser import SchemaContext
- return SchemaContext().load(file, url, schema)
+ def __init__(self, source, name):
+ self.source = source
+ self.name = name
+ ConfigurationError.__init__(self, "no replacement for " + `name`)