[Zope3-checkins] CVS: Zope/lib/python/ZConfig - Exceptions.py:1.1.10.1 ApacheStyle.py:1.1.4.5 Config.py:1.2.4.6 Context.py:1.1.4.6 SchemaContext.py:1.1.2.6 SchemaParser.py:1.1.2.19 Substitution.py:1.3.6.2 __init__.py:1.1.4.8 Common.py:NONE

Fred L. Drake, Jr. fred@zope.com
Fri, 6 Dec 2002 11:06:59 -0500


Update of /cvs-repository/Zope/lib/python/ZConfig
In directory cvs.zope.org:/tmp/cvs-serv7113/ZConfig

Modified Files:
      Tag: chrism-install-branch
	ApacheStyle.py Config.py Context.py SchemaContext.py 
	SchemaParser.py Substitution.py __init__.py 
Added Files:
      Tag: chrism-install-branch
	Exceptions.py 
Removed Files:
      Tag: chrism-install-branch
	Common.py 
Log Message:
Merge ZConfig trunk into the chrism-install-branch.

=== Added File Zope/lib/python/ZConfig/Exceptions.py ===
"""Names used from all modules in the package.

Since some names are only defined if needed, this module should be
imported using the from-import-* syntax.
"""


class ConfigurationError(Exception):
    def __init__(self, msg):
        self.message = msg
        Exception.__init__(self, msg)

    def __str__(self):
        return self.message


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(ConfigurationError):
    def __init__(self, msg, url, lineno):
        self.url = url
        self.lineno = lineno
        ConfigurationError.__init__(self, msg)

    def __str__(self):
        return "%s\n(line %s in %s)" % (self.message, self.lineno, self.url)


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`)


=== Zope/lib/python/ZConfig/ApacheStyle.py 1.1.4.4 => 1.1.4.5 ===
--- Zope/lib/python/ZConfig/ApacheStyle.py:1.1.4.4	Sat Nov 16 01:38:39 2002
+++ Zope/lib/python/ZConfig/ApacheStyle.py	Fri Dec  6 11:06:23 2002
@@ -2,14 +2,14 @@
 
 import urlparse
 
-from Common import *
+from ZConfig import ConfigurationError, ConfigurationSyntaxError
 
 
-def Parse(file, context, section, url):
+def Parse(resource, context, section):
     lineno = 0
     stack = []
     while 1:
-        line = file.readline()
+        line = resource.file.readline()
         if not line:
             break
         lineno += 1
@@ -20,29 +20,31 @@
         if line[0] == "#":
             # comment
             continue
+
         if line[:2] == "</":
             # section end
             if line[-1] != ">":
                 raise ConfigurationSyntaxError(
-                    "malformed section end", url, lineno)
+                    "malformed section end", resource.url, lineno)
             if not stack:
                 raise ConfigurationSyntaxError(
-                    "unexpected section end", url, lineno)
+                    "unexpected section end", resource.url, lineno)
             type = line[2:-1].rstrip()
             if type.lower() != section.type:
                 raise ConfigurationSyntaxError(
-                    "unbalanced section end", url, lineno)
+                    "unbalanced section end", resource.url, lineno)
             try:
                 section.finish()
             except ConfigurationError, e:
-                raise ConfigurationSyntaxError(e[0], url, lineno)
+                raise ConfigurationSyntaxError(e[0], resource.url, lineno)
             section = stack.pop()
             continue
+
         if line[0] == "<":
             # section start
             if line[-1] != ">":
                 raise ConfigurationSyntaxError(
-                    "malformed section start", url, lineno)
+                    "malformed section start", resource.url, lineno)
             isempty = line[-2] == "/"
             if isempty:
                 text = line[1:-2].rstrip()
@@ -52,43 +54,74 @@
             m = _section_start_rx.match(text)
             if not m:
                 raise ConfigurationSyntaxError(
-                    "malformed section header", url, lineno)
+                    "malformed section header", resource.url, lineno)
             type, name, delegatename = m.group('type', 'name', 'delegatename')
             try:
                 newsect = context.nestSection(section, type, name,
                                               delegatename)
             except ConfigurationError, e:
-                raise ConfigurationSyntaxError(e[0], url, lineno)
+                raise ConfigurationSyntaxError(e[0], resource.url, lineno)
             if not isempty:
                 stack.append(section)
                 section = newsect
             continue
+
+        if line[0] == "%":
+            # directive: import, include
+            m = _keyvalue_rx.match(line, 1)
+            if not m:
+                raise ConfigurationSyntaxError(
+                    "missing or unrecognized directive", resource.url, lineno)
+            name, arg = m.group('key', 'value')
+            if name not in ("define", "import", "include"):
+                raise ConfigurationSyntaxError(
+                    "unknown directive: " + `name`, resource.url, lineno)
+            if not arg:
+                raise ConfigurationSyntaxError(
+                    "missing argument to %%%s directive" % name,
+                    resource.url, lineno)
+            if name == "import":
+                if stack:
+                    raise ConfigurationSyntaxError(
+                        "import only allowed at outermost level of a resource",
+                        resource.url, lineno)
+                newurl = urlparse.urljoin(resource.url, arg)
+                context.importConfiguration(section, newurl)
+            elif name == "include":
+                newurl = urlparse.urljoin(resource.url, arg)
+                context.includeConfiguration(section, newurl)
+            elif name == "define":
+                parts = arg.split(None, 1)
+                defname = parts[0]
+                defvalue = ''
+                if len(parts) == 2:
+                    defvalue = parts[1]
+                try:
+                    resource.define(defname, defvalue)
+                except ConfigurationError, e:
+                    raise ConfigurationSyntaxError(e[0], resource.url, lineno)
+            else:
+                assert 0, "unexpected directive for " + `line`
+            continue
+
         # key-value
         m = _keyvalue_rx.match(line)
         if not m:
             raise ConfigurationSyntaxError(
-                "malformed configuration data", url, lineno)
+                "malformed configuration data", resource.url, lineno)
         key, value = m.group('key', 'value')
-        if key == "import":
-            if stack:
-                raise ConfigurationSyntaxError(
-                    "import only allowed at the outermost level of a resource",
-                    url, lineno)
-            newurl = urlparse.urljoin(url, value)
-            context.importConfiguration(section, newurl)
-        elif key == "include":
-            newurl = urlparse.urljoin(section.url, value)
-            context.includeConfiguration(section, newurl)
+        if not value:
+            value = ''
         else:
-            if not value:
-                value = ''
-            try:
-                section.addValue(key, value)
-            except ConfigurationError, e:
-                raise ConfigurationSyntaxError(e[0], url, lineno)
+            value = resource.substitute(value)
+        try:
+            section.addValue(key, value)
+        except ConfigurationError, e:
+            raise ConfigurationSyntaxError(e[0], resource.url, lineno)
+
     if stack:
         raise ConfigurationSyntaxError(
-            "unclosed sections no allowed", url, lineno + 1)
+            "unclosed sections not allowed", resource.url, lineno + 1)
 
 
 import re


=== Zope/lib/python/ZConfig/Config.py 1.2.4.5 => 1.2.4.6 ===
--- Zope/lib/python/ZConfig/Config.py:1.2.4.5	Tue Nov 26 18:52:33 2002
+++ Zope/lib/python/ZConfig/Config.py	Fri Dec  6 11:06:23 2002
@@ -1,6 +1,12 @@
 """Configuration data structure."""
 
-from Common import *
+import ZConfig
+
+try:
+    True
+except NameError:
+    True = 1
+    False = 0
 
 class Configuration:
     def __init__(self, container, type, name, url):
@@ -31,7 +37,7 @@
 
     def setDelegate(self, section):
         if self.delegate is not None:
-            raise ConfigurationError("cannot modify delegation")
+            raise ZConfig.ConfigurationError("cannot modify delegation")
         self.delegate = section
 
     def addChildSection(self, section):
@@ -53,7 +59,7 @@
         if child is None or child.url != self.url:
             self._sections_by_name[key] = section
         else:
-            raise ConfigurationError(
+            raise ZConfig.ConfigurationError(
                 "cannot replace existing named section")
 
     def getSection(self, type, name=None):
@@ -64,14 +70,14 @@
             try:
                 return self._sections_by_name[key]
             except KeyError:
-                raise ConfigurationMissingSectionError(type, name)
+                raise ZConfig.ConfigurationMissingSectionError(type, name)
         else:
             L = []
             for sect in self._sections:
                 if sect.type == type:
                     L.append(sect)
             if len(L) > 1:
-                raise ConfigurationConflictingSectionError(type, name)
+                raise ZConfig.ConfigurationConflictingSectionError(type, name)
             if L:
                 return L[0]
             elif self.delegate:
@@ -93,11 +99,16 @@
         except KeyError:
             self._data[key] = value
         else:
-            raise ConfigurationError("cannot add existing key")
+            raise ZConfig.ConfigurationError("cannot add existing key")
 
-    def setValue(self, key, value):
+    def has_key(self, key):
         key = key.lower()
-        self._data[key] = value
+        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()
@@ -206,3 +217,14 @@
                 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.5 => 1.1.4.6 ===
--- Zope/lib/python/ZConfig/Context.py:1.1.4.5	Sun Nov 24 18:28:43 2002
+++ Zope/lib/python/ZConfig/Context.py	Fri Dec  6 11:06:23 2002
@@ -5,14 +5,15 @@
 import urllib2
 import urlparse
 
-from Common import *
+import ZConfig
+
 from Config import Configuration, ImportingConfiguration
+from Substitution import isname, substitute
 
 
 class Context:
 
     def __init__(self):
-        #Configuration.__init__(self, None, None, url)
         self._imports = []         # URL  -> Configuration
         self._named_sections = {}  # name -> Configuration
         self._needed_names = {}    # name -> [needy Configuration, ...]
@@ -32,14 +33,17 @@
     def createToplevelSection(self, url):
         return ImportingConfiguration(url)
 
+    def createResource(self, file, url):
+        return Resource(file, url)
+
     def getDelegateType(self, type):
         # Applications must provide delegation typing information by
         # overriding the Context.getDelegateType() method.
         return type.lower()
 
-    def parse(self, file, section, url):
+    def parse(self, resource, section):
         from ApacheStyle import Parse
-        Parse(file, self, section, url)
+        Parse(resource, self, section)
 
     def _normalize_url(self, url):
         if os.path.exists(url):
@@ -72,8 +76,9 @@
         self._all_sections.append(top)
         self._imports = [top]
         self._current_imports.append(top)
+        r = self.createResource(file, url)
         try:
-            self.parse(file, top, url)
+            self.parse(r, top)
         finally:
             del self._current_imports[-1]
         self._finish()
@@ -95,8 +100,9 @@
     def includeConfiguration(self, section, url):
         # XXX we always re-parse, unlike import
         file = urllib2.urlopen(url)
+        r = self.createResource(file, url)
         try:
-            self.parse(file, section, url)
+            self.parse(r, section)
         finally:
             file.close()
 
@@ -111,10 +117,10 @@
             # another resource.
             oldsect = self._named_sections[name]
             if oldsect.url == section.url:
-                raise ConfigurationError(
+                raise ZConfig.ConfigurationError(
                     "named section cannot be defined twice in same resource")
             if oldsect.type != type:
-                raise ConfigurationError(
+                raise ZConfig.ConfigurationError(
                     "named section cannot change type")
         newsect = self.createNestedSection(section, type, name, delegatename)
         self._all_sections.append(newsect)
@@ -143,12 +149,13 @@
     def _parse_url(self, url, section):
         url, fragment = urlparse.urldefrag(url)
         if fragment:
-            raise ConfigurationError(
+            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(file, section, url)
+            self.parse(r, section)
         finally:
             del self._current_imports[-1]
             file.close()
@@ -160,14 +167,14 @@
             for referrer in L:
                 type = self.getDelegateType(referrer.type)
                 if type is None:
-                    raise ConfigurationTypeError(
+                    raise ZConfig.ConfigurationTypeError(
                         "%s sections are not allowed to specify delegation\n"
                         "(in %s)"
                         % (repr(referrer.type), referrer.url),
                         referrer.type, None)
                 type = type.lower()
                 if type != section.type:
-                    raise ConfigurationTypeError(
+                    raise ZConfig.ConfigurationTypeError(
                         "%s sections can only inherit from %s sections\n"
                         "(in %s)"
                         % (repr(referrer.type), repr(type), referrer.url),
@@ -188,3 +195,24 @@
             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/SchemaContext.py 1.1.2.5 => 1.1.2.6 ===
--- Zope/lib/python/ZConfig/SchemaContext.py:1.1.2.5	Tue Nov 26 12:09:12 2002
+++ Zope/lib/python/ZConfig/SchemaContext.py	Fri Dec  6 11:06:23 2002
@@ -17,7 +17,6 @@
 import Context
 import SchemaParser
 
-from Common import *
 from SchemaInterfaces import ISchemaContext
 
 
@@ -78,10 +77,10 @@
 ## can be true that two sections can exist in the same resource
 ##
 ##             if oldsect.url == section.url:
-##                 raise ConfigurationError(
+##                 raise ZConfig.ConfigurationError(
 ##                     "named section cannot be defined twice in same resource")
             if oldsect.type != type:
-                raise ConfigurationError(
+                raise ZConfig.ConfigurationError(
                     "named section cannot change type")
         newsect = self.createNestedSection(section, type, name, delegatename)
         self._all_sections.append(newsect)


=== Zope/lib/python/ZConfig/SchemaParser.py 1.1.2.18 => 1.1.2.19 ===
--- Zope/lib/python/ZConfig/SchemaParser.py:1.1.2.18	Thu Dec  5 23:38:36 2002
+++ Zope/lib/python/ZConfig/SchemaParser.py	Fri Dec  6 11:06:23 2002
@@ -20,7 +20,8 @@
 import types
 import xml.sax
 
-from Common import *
+import ZConfig
+
 from Config import Configuration
 from SchemaInterfaces import IConfigKey, IConfigSection, IConfigRoot,\
      IConfigMultiKey, IConfigMultiSection
@@ -270,18 +271,18 @@
             lineno = self._locator.getLineNumber()
         raise SchemaError(msg, lineno, colno)
 
-class SchemaError(Exception):
+class SchemaError(ZConfig.ConfigurationError):
     def __init__(self, msg, line, col):
-        self.msg = msg
         self.line = line
         self.col = col
+        ZConfig.ConfigurationError.__init__(self, msg)
 
     def __str__(self):
         if self.line is not None:
             pos = " at line %d, column %s" % (self.line, self.col)
         else:
             pos = " (unknown position)"
-        return self.msg + pos
+        return self.message + pos
 
 class SchemaConfiguration(Configuration):
     resolved = missing
@@ -302,31 +303,31 @@
     def hasDefault(self):
         try:
             self.default()
-        except ConfigurationError:
+        except ZConfig.ConfigurationError:
             return False
         return True
 
     # subclass required methods
     def default(self):
-        raise ConfigurationError(
+        raise ZConfig.ConfigurationError(
             'no "default" method provided for "%s" named "%s"' %
             (self.type, self.name)
             )
 
     def handle(self, *args):
-        raise ConfigurationError(
+        raise ZConfig.ConfigurationError(
             'no "handle" method provided for "%s" named "%s"' %
             (self.type, self.name)
             )
 
     def multihandle(self, l):
-        raise ConfigurationError(
+        raise ZConfig.ConfigurationError(
             'no "multihandle" method provided for "%s" named "%s"' %
             (self.type, self.name)
             )
 
     def validate(self, value):
-        raise ConfigurationError(
+        raise ZConfig.ConfigurationError(
             'no "validate" method provided for "%s" named "%s"' %
             (self.type, self.name)
             )
@@ -336,7 +337,7 @@
 
     def populate(self, value):
         if self.value is not missing:
-            raise ConfigurationError, (
+            raise ZConfig.ConfigurationError, (
                 'The key "%s" cannot have multiple values' % self.name
                 )
         self.setValue(value)
@@ -345,11 +346,11 @@
         return []
 
     def setValue(self, value):
-        # set this key's value with prejudice (dont raise an error
+        # set this key's value with prejudice (don't raise an error
         # unless the validator fails)
         msg = self.validate(value)
         if msg:
-            raise ConfigurationError, msg
+            raise ZConfig.ConfigurationError, msg
         self.value = value
 
     def canFinish(self):
@@ -357,7 +358,8 @@
 
     def finish(self):
         if not self.canFinish():
-            raise ConfigurationError, 'missing value for %s' % self.name
+            raise ZConfig.ConfigurationError('missing value for '
+                                             + `self.name`)
 
     def getValue(self):
         if self.resolved is missing:
@@ -375,11 +377,11 @@
         self.setValue(value)
 
     def setValue(self, value):
-        # set this key's value with prejudice (dont raise an error
+        # set this key's value with prejudice (don't raise an error
         # unless the validator fails)
         msg = self.validate(value)
         if msg:
-            raise ConfigurationError, msg
+            raise ZConfig.ConfigurationError, msg
         if self.value is missing:
             self.value = []
         self.value.append(value)
@@ -420,7 +422,7 @@
         try:
             return self.data[(meta_type, type, name)]
         except KeyError:
-            raise ConfigurationError(
+            raise ZConfig.ConfigurationError(
                 'invalid %s of type "%s" with name "%s"'
                 % (meta_type, type, name))
 
@@ -443,7 +445,7 @@
         for type, name in unfinishable:
             msg = msg + ' %s "%s",' % (type, name)
         msg = msg[:-1]
-        raise ConfigurationError, msg
+        raise ZConfig.ConfigurationError, msg
 
     def unfinishable(self):
         l = []
@@ -467,7 +469,7 @@
         key = key.lower()
         try:
             return self.getSub(KEY_TYPE, KEY_TYPE, key).getValue()
-        except ConfigurationError:
+        except ZConfig.ConfigurationError:
             if self.delegate is None:
                 return default
             else:
@@ -521,7 +523,7 @@
                 if meta_type == SECTION_TYPE and type == s_type:
                     L.append(self.getSub(meta_type, type, name))
             if len(L) > 1:
-                raise ConfigurationConflictingSectionError(type, name)
+                raise ZConfig.ConfigurationConflictingSectionError(type, name)
             if L:
                 return L[0]
             elif self.delegate:
@@ -566,7 +568,7 @@
         self.current = None
         self.finished = []
         if self.prototype_class is None:
-            raise ConfigurationError(
+            raise ZConfig.ConfigurationError(
                 'No prototype_class for section typed "%s" named "%s"' %
                 (type, name)
                 )


=== Zope/lib/python/ZConfig/Substitution.py 1.3.6.1 => 1.3.6.2 ===
--- Zope/lib/python/ZConfig/Substitution.py:1.3.6.1	Fri Nov 15 20:26:17 2002
+++ Zope/lib/python/ZConfig/Substitution.py	Fri Dec  6 11:06:23 2002
@@ -1,111 +1,76 @@
 """Substitution support for ZConfig values."""
 
-class SubstitutionError(Exception):
-    """Base exception for string substitution errors."""
+import ZConfig
 
-    def __init__(self, msg, context):
-        self.message = msg
-        self.context = context
-
-    def __str__(self):
-        return self.message
-
-class SubstitutionSyntaxError(SubstitutionError):
-    """Raised when interpolation source text contains syntactical errors."""
-
-    def __init__(self, msg, context):
-        if context is not None:
-            context = context[:]
-        SubstitutionError.__init__(self, msg, context)
-
-class SubstitutionRecursionError(SubstitutionError):
-    """Raised when a nested interpolation is recursive."""
-
-    def __init__(self, name, context):
-        self.name = name
-        msg = ("recursion on %s; current context:\n%s"
-               % (repr(name), ", ".join(context)))
-        SubstitutionError.__init__(self, msg, context[:])
-
-
-def get(section, name, default=None):
-    # XXX should this interpolate from default if that's what's used?
-    missing = []
-    s = section.get(name, missing)
-    if s is missing:
-        return default
-    if "$" in s:
-        accum = []
-        _interp(accum, s, section, [name])
-        s = ''.join(accum)
-    return s
+try:
+    True
+except NameError:
+    True = 1
+    False = 0
 
 
-def substitute(s, section):
+def substitute(s, mapping):
     """Interpolate variables from `section` into `s`."""
-    if '$' in s:
-        accum = []
-        _interp(accum, s, section, None)
-        s = ''.join(accum)
-    return s
-
-
-def _interp(accum, rest, section, context):
-    while 1:
-        i = rest.find("$")
-        if i < 0:
-            accum.append(rest)
-            break
-        accum.append(rest[:i])
-        rest = rest[i+1:]
-        if not rest:
-            accum.append("$")
-            break
-        if rest[0] == "$":
-            accum.append("$")
-            rest = rest[1:]
-        elif rest[0] == "{":
-            rest = rest[1:]
-            m = _name_match(rest[:])
+    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 SubstitutionSyntaxError("'${' not followed by name",
-                                              context)
+                raise ZConfig.SubstitutionSyntaxError(
+                    "'${' not followed by name")
             name = m.group(0)
-            length = len(name)
-            if rest[length:length+1] != "}":
-                raise SubstitutionSyntaxError(
-                    "'${%s' not followed by '}'" % name, context)
-            v = section.get(name)
-            if v is None:
-                parent = getattr(section, "container", None)
-                while parent is not None:
-                    v = parent.get(name)
-                    if v is not None:
-                        break
-                    parent = getattr(parent, "container", None)
-                else:
-                    v = ""
-            if "$" in v and context:
-                if name in context:
-                    raise SubstitutionRecursionError(name, context)
-                _interp(accum, v, section, context + [name])
-            else:
-                accum.append(v)
-            rest = rest[length+1:]
+            i = m.end() + 1
+            if not s.startswith("}", i - 1):
+                raise ZConfig.SubstitutionSyntaxError(
+                    "'${%s' not followed by '}'" % name)
         else:
-            m = _name_match(rest)
+            m = _name_match(s, i+1)
             if not m:
-                accum.append("$")
-                continue
+                raise ZConfig.SubstitutionSyntaxError(
+                    "'$' not followed by '$' or name")
             name = m.group(0)
-            v = section.get(name, "")
-            if "$" in v and context:
-                if name in context:
-                    raise SubstitutionRecursionError(name, context)
-                _interp(accum, v, section, context + [name])
-            else:
-                accum.append(v)
-            rest = rest[len(name):]
+            i = m.end()
+        return prefix, name.lower(), s[i:]
+    else:
+        return s, None, None
 
 
 import re


=== Zope/lib/python/ZConfig/__init__.py 1.1.4.7 => 1.1.4.8 ===
--- Zope/lib/python/ZConfig/__init__.py:1.1.4.7	Tue Dec  3 16:24:50 2002
+++ Zope/lib/python/ZConfig/__init__.py	Fri Dec  6 11:06:23 2002
@@ -16,6 +16,8 @@
 $Id$
 """
 
+from ZConfig.Exceptions import *
+
 def load(url):
     import Context
     return Context.Context().load(url)

=== Removed File Zope/lib/python/ZConfig/Common.py ===