[Zodb-checkins] CVS: Zope/lib/python/ZConfig - BRANCHES.txt:1.2.2.1 Config.py:1.14.10.5 Context.py:1.15.10.8 __init__.py:1.3.10.9 cfgparser.py:1.1.2.8 datatypes.py:1.1.2.26 info.py:1.1.2.23 loader.py:1.1.2.28 matcher.py:1.1.2.33 schema.py:1.1.2.40 substitution.py:1.1.2.2 url.py:1.1.2.5

Fred L. Drake, Jr. fred@zope.com
Thu, 9 Jan 2003 14:28:24 -0500


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

Modified Files:
      Tag: zconfig-schema-devel-branch
	BRANCHES.txt Config.py Context.py __init__.py cfgparser.py 
	datatypes.py info.py loader.py matcher.py schema.py 
	substitution.py url.py 
Log Message:
Merge from the ZConfig HEAD.

=== Zope/lib/python/ZConfig/BRANCHES.txt 1.2 => 1.2.2.1 ===
--- Zope/lib/python/ZConfig/BRANCHES.txt:1.2	Fri Dec  6 13:15:00 2002
+++ Zope/lib/python/ZConfig/BRANCHES.txt	Thu Jan  9 14:27:44 2003
@@ -3,12 +3,14 @@
 ZConfig package.
 
 zconfig-brace-syntax
+    NOT ACTIVE
     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
+    NOT ACTIVE
     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.
+    for Zope 2.  This was merged into the trunk on 3-Jan-2002.


=== Zope/lib/python/ZConfig/Config.py 1.14.10.4 => 1.14.10.5 ===
--- Zope/lib/python/ZConfig/Config.py:1.14.10.4	Wed Dec 11 15:20:37 2002
+++ Zope/lib/python/ZConfig/Config.py	Thu Jan  9 14:27:44 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,
@@ -102,7 +102,8 @@
             type = type.lower()
             return [sect for sect in self._sections if sect.type == type]
 
-    def addValue(self, key, value):
+    def addValue(self, key, value, position=None):
+        # position is needed for interface compatibility, but isn't used here
         key = key.lower()
         try:
             self._data[key]


=== Zope/lib/python/ZConfig/Context.py 1.15.10.7 => 1.15.10.8 ===
--- Zope/lib/python/ZConfig/Context.py:1.15.10.7	Wed Dec 11 15:20:37 2002
+++ Zope/lib/python/ZConfig/Context.py	Thu Jan  9 14:27:44 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,
@@ -13,8 +13,6 @@
 ##############################################################################
 """Top-level configuration handle."""
 
-import urlparse
-
 import ZConfig
 
 from ZConfig import loader
@@ -42,9 +40,9 @@
         # overriding the Context.getDelegateType() method.
         return type.lower()
 
-    def parse(self, resource, section):
+    def parse(self, resource, section, defines=None):
         from ZConfig.cfgparser import ZConfigParser
-        ZConfigParser(resource, self).parse(section)
+        ZConfigParser(resource, self, defines).parse(section)
 
     def loadResource(self, resource):
         top = self.createToplevelSection(resource.url)
@@ -53,13 +51,12 @@
         self._finish()
         return top
 
-
     # interface for parser
 
-    def includeConfiguration(self, section, url):
+    def includeConfiguration(self, section, url, defines):
         r = self.openResource(url)
         try:
-            self.parse(r, section)
+            self.parse(r, section, defines)
         finally:
             r.close()
 


=== Zope/lib/python/ZConfig/__init__.py 1.3.10.8 => 1.3.10.9 ===
--- Zope/lib/python/ZConfig/__init__.py:1.3.10.8	Thu Jan  2 17:50:19 2003
+++ Zope/lib/python/ZConfig/__init__.py	Thu Jan  9 14:27:44 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,
@@ -100,6 +100,26 @@
         self.found = found
         self.expected = expected
         ConfigurationError.__init__(self, msg)
+
+
+class DataConversionError(ConfigurationError, ValueError):
+    """Raised when a data type conversion function raises ValueError."""
+
+    def __init__(self, exception, value, position):
+        ConfigurationError.__init__(self, str(exception))
+        self.exception = exception
+        self.value = value
+        self.lineno, self.colno, self.url = position
+
+    def __str__(self):
+        s = "%s (line %d" % (self.message, self.lineno)
+        if self.colno is not None:
+            s += ", %d" % self.colno
+        if self.url:
+            s += ", in %s)" % self.url
+        else:
+            s += ")"
+        return s
 
 
 class SubstitutionSyntaxError(ConfigurationError):


=== Zope/lib/python/ZConfig/cfgparser.py 1.1.2.7 => 1.1.2.8 ===
--- Zope/lib/python/ZConfig/cfgparser.py:1.1.2.7	Tue Dec 24 15:48:18 2002
+++ Zope/lib/python/ZConfig/cfgparser.py	Thu Jan  9 14:27:45 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,
@@ -25,14 +25,20 @@
 
 
 class ZConfigParser:
-    def __init__(self, resource, context):
+    __metaclass__ = type
+    __slots__ = ('resource', 'context', 'lineno',
+                 'stack', 'defs', 'file', 'url')
+
+    def __init__(self, resource, context, defines=None):
         self.resource = resource
         self.context = context
         self.file = resource.file
         self.url = resource.url
         self.lineno = 0
         self.stack = []   # [(type, name, delegatename, prevmatcher), ...]
-        self.defs = {}
+        if defines is None:
+            defines = {}
+        self.defs = defines
 
     def nextline(self):
         line = self.file.readline()
@@ -123,7 +129,7 @@
         else:
             value = substitute(value, self.defs)
         try:
-            section.addValue(key, value)
+            section.addValue(key, value, (self.lineno, None, self.url))
         except ConfigurationError, e:
             self.error(e[0])
 
@@ -145,7 +151,7 @@
 
     def handle_include(self, section, rest):
         newurl = urljoin(self.url, rest)
-        self.context.includeConfiguration(section, newurl)
+        self.context.includeConfiguration(section, newurl, self.defs)
 
     def handle_define(self, section, rest):
         parts = rest.split(None, 1)
@@ -157,7 +163,7 @@
             self.error("cannot redefine " + `defname`)
         if not isname(defname):
             self.error("not a substitution legal name: " + `defname`)
-        self.defs[defname] = defvalue
+        self.defs[defname] = substitute(defvalue, self.defs)
 
     def error(self, message):
         raise ConfigurationSyntaxError(message, self.url, self.lineno)


=== Zope/lib/python/ZConfig/datatypes.py 1.1.2.25 => 1.1.2.26 ===
--- Zope/lib/python/ZConfig/datatypes.py:1.1.2.25	Fri Jan  3 14:01:50 2003
+++ Zope/lib/python/ZConfig/datatypes.py	Thu Jan  9 14:27:45 2003
@@ -174,17 +174,16 @@
     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
+class SocketAddress:
+    def __init__(self, s):
+        # returns (family, address) tuple
+        import socket
+        if "/" in s:
+            self.family = getattr(socket, "AF_UNIX", None)
+            self.address = s
         else:
-            raise ValueError(
-                "AF_UNIX sockets are not supported on this platform")
-    else:
-        return socket.AF_INET, inet_address(s)
+            self.family = socket.AF_INET
+            self.address = inet_address(s)
 
 def float_conversion(v):
     if isinstance(v, type('')) or isinstance(v, type(u'')):
@@ -311,7 +310,7 @@
     "basic-key":         BasicKeyConversion(),
     "logging-level":     LogLevelConversion(),
     "inet-address":      inet_address,
-    "socket-address":    socket_address,
+    "socket-address":    SocketAddress,
     "ipaddr-or-hostname":IpaddrOrHostname(),
     "existing-directory":existing_directory,
     "existing-path":     existing_path,
@@ -331,6 +330,9 @@
     }
 
 class Registry:
+    __metatype__ = type
+    __slots__ = '_stock', '_other'
+
     def __init__(self, stock=None):
         if stock is None:
             stock = stock_datatypes.copy()


=== Zope/lib/python/ZConfig/info.py 1.1.2.22 => 1.1.2.23 ===
--- Zope/lib/python/ZConfig/info.py:1.1.2.22	Fri Jan  3 12:05:21 2003
+++ Zope/lib/python/ZConfig/info.py	Thu Jan  9 14:27:45 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,
@@ -24,6 +24,9 @@
 
 
 class UnboundedThing:
+    __metaclass__ = type
+    __slots__ = ()
+
     def __lt__(self, other):
         return False
 
@@ -48,6 +51,22 @@
 Unbounded = UnboundedThing()
 
 
+class ValueInfo:
+    __metaclass__ = type
+    __slots__ = 'value', 'position'
+
+    def __init__(self, value, position):
+        self.value = value
+        # position is (lineno, colno, url)
+        self.position = position
+
+    def convert(self, datatype):
+        try:
+            return datatype(self.value)
+        except ValueError, e:
+            raise ZConfig.DataConversionError(e, self.value, self.position)
+
+
 class BaseInfo:
     """Information about a single configuration key."""
 
@@ -75,7 +94,7 @@
         clsname = self.__class__.__name__
         return "<%s for %s>" % (clsname, `self.name`)
 
-    def istypegroup(self):
+    def isabstract(self):
         return False
 
     def ismulti(self):
@@ -100,10 +119,11 @@
                 "cannot finish KeyInfo more than once")
         self._finished = True
 
-    def adddefault(self, value):
+    def adddefault(self, value, position):
         if self._finished:
             raise ZConfig.SchemaError(
                 "cannot add default values to finished KeyInfo")
+        value = ValueInfo(value, position)
         if self.maxOccurs > 1:
             if self._default is None:
                 self._default = [value]
@@ -146,7 +166,7 @@
                 raise ZConfig.SchemaError(
                     "sections which can occur more than once must"
                     " specify a target attribute name")
-        if sectiontype.istypegroup():
+        if sectiontype.isabstract():
             datatype = None
         else:
             datatype = sectiontype.datatype
@@ -170,8 +190,8 @@
             return False
         elif self.name == "+":
             return name and True or False
-        elif not name:
-            return self.name == "*"
+        elif self.name == "*":
+            return True
         else:
             return name == self.name
 
@@ -182,39 +202,36 @@
         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
+class AbstractType:
+    __metaclass__ = type
+    __slots__ = '_subtypes', 'name'
 
-    def gettype(self, name):
-        n = name.lower()
+    def __init__(self, name):
+        self._subtypes = {}
+        self.name = name
+
+    def addsubtype(self, type):
+        self._subtypes[type.name] = type
+
+    def getsubtype(self, name):
         try:
-            return self._types[n]
+            return self._subtypes[name]
         except KeyError:
-            raise ZConfig.SchemaError("unknown type name: " + `name`)
+            raise ZConfig.SchemaError("no sectiontype %s in abstracttype %s"
+                                      % (`name`, `self.name`))
 
-    def gettypenames(self):
-        return self._types.keys()
+    def getsubtypenames(self):
+        L = self._subtypes.keys()
+        L.sort()
+        return L
 
-
-class GroupType(TypeContainer):
-    def __init__(self, name):
-        TypeContainer.__init__(self)
-        self.name = name
-
-    def istypegroup(self):
+    def isabstract(self):
         return True
 
 
 class SectionType:
-    def __init__(self, name, keytype, valuetype, datatype):
+    def __init__(self, name, keytype, valuetype, datatype, registry, types):
         # name      - name of the section, or '*' or '+'
         # datatype  - type for the section itself
         # keytype   - type for the keys themselves
@@ -223,9 +240,22 @@
         self.datatype = datatype
         self.keytype = keytype
         self.valuetype = valuetype
+        self.handler = None
+        self.registry = registry
         self._children = []    # [(key, info), ...]
         self._attrmap = {}     # {attribute: index, ...}
         self._keymap = {}      # {key: index, ...}
+        self._types = types
+
+    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 __len__(self):
         return len(self._children)
@@ -294,9 +324,9 @@
                         raise ZConfig.ConfigurationError(
                             "section name %s already in use for key" % key)
                     st = info.sectiontype
-                    if st.istypegroup():
+                    if st.isabstract():
                         try:
-                            st = st.gettype(type)
+                            st = st.getsubtype(type)
                         except ZConfig.ConfigurationError:
                             raise ZConfig.ConfigurationError(
                                 "section type %s not allowed for name %s"
@@ -306,19 +336,19 @@
                             "name %s must be used for a %s section"
                             % (`name`, `st.name`))
                     return index
-            # else must be a section or a sectiongroup:
+            # else must be a sectiontype or an abstracttype:
             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():
+            elif info.sectiontype.isabstract():
                 st = info.sectiontype
                 if st.name == type:
                     raise ZConfig.ConfigurationError(
-                        "cannot define section with a sectiongroup type")
+                        "cannot define section with an abstract type")
                 try:
-                    st = st.gettype(type)
+                    st = st.getsubtype(type)
                 except ZConfig.ConfigurationError:
                     # not this one; maybe a different one
                     pass
@@ -329,21 +359,29 @@
     def getsectioninfo(self, type, name):
         i = self.getsectionindex(type, name)
         st = self._children[i][1]
-        if st.istypegroup():
+        if st.isabstract():
             st = st.gettype(type)
         return st
 
-    def istypegroup(self):
+    def isabstract(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)
+class SchemaType(SectionType):
+    def __init__(self, name, keytype, valuetype, datatype, handler, url,
+                 registry):
+        SectionType.__init__(self, name, keytype, valuetype, datatype,
+                             registry, {})
         self.handler = handler
         self.url = url
 
+    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 allowUnnamed(self):
         return True
 
@@ -361,3 +399,19 @@
         if self.name and self.name in alltypes:
             alltypes.remove(self.name)
         return alltypes
+
+    def createSectionType(self, name, keytype, valuetype, datatype):
+        t = SectionType(name, keytype, valuetype, datatype,
+                        self.registry, self._types)
+        self.addtype(t)
+        return t
+
+    def deriveSectionType(self, base, name, valuetype, datatype):
+        if isinstance(base, SchemaType):
+            raise ZConfig.SchemaError(
+                "cannot derive sectiontype from top-level schema")
+        t = self.createSectionType(name, base.keytype, valuetype, datatype)
+        t._attrmap.update(base._attrmap)
+        t._keymap.update(base._keymap)
+        t._children.extend(base._children)
+        return t


=== Zope/lib/python/ZConfig/loader.py 1.1.2.27 => 1.1.2.28 ===
--- Zope/lib/python/ZConfig/loader.py:1.1.2.27	Fri Jan  3 13:53:03 2003
+++ Zope/lib/python/ZConfig/loader.py	Thu Jan  9 14:27:45 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,
@@ -14,6 +14,7 @@
 """Schema loader utility."""
 
 import os.path
+import sys
 import urllib
 import urllib2
 
@@ -30,8 +31,7 @@
     False = 0
 
 
-RESOURCE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                            "resources")
+LIBRARY_DIR = os.path.join(sys.prefix, "lib", "zconfig")
 
 
 def loadSchema(url):
@@ -79,15 +79,14 @@
         # 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))
+        url = str(url)
+        parts = urlsplit(url)
         fragment = parts[-1]
         if fragment:
+            parts = list(parts)
             parts[-1] = ''
             url = urlunsplit(tuple(parts))
-        if parts[0] == 'zconfig':
-            file = _open_resource_file(url)
-        else:
-            file = urllib2.urlopen(url)
+        file = urllib2.urlopen(url)
         return self.createResource(file, url, fragment or None)
 
     def normalizeURL(self, url):
@@ -114,22 +113,16 @@
         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):
+    def __init__(self, registry=None, library=None):
         if registry is None:
             registry = datatypes.Registry()
+        BaseLoader.__init__(self)
         self.registry = registry
         self._cache = {}
-        BaseLoader.__init__(self)
+        if library is None:
+            library = LIBRARY_DIR
+        self._library = library
 
     def loadResource(self, resource):
         if resource.url and self._cache.has_key(resource.url):
@@ -146,10 +139,36 @@
     def allowFragments(self):
         return True
 
+    # schema parser support API
+
+    def schemaPackageInfo(self, package):
+        parts = package.split(".")
+        if not parts:
+            raise ZConfig.SchemaError(
+                "illegal schema component name: " + `package`)
+        if len(filter(None, parts)) != len(parts):
+            # '' somewhere in the package spec; still illegal
+            raise ZConfig.SchemaError(
+                "illegal schema component name: " + `package`)
+        dirname = os.path.join(self._library, *parts)
+        fn = os.path.join(dirname, "component.xml")
+        if not os.path.exists(fn):
+            raise ZConfig.SchemaError(
+                "schema component not found: " + `package`)
+        url = "file://" + urllib.pathname2url(fn)
+        extensions = []
+        for fn in os.listdir(dirname):
+            if fn == "component.xml":
+                continue
+            path = os.path.join(dirname, fn, "extension.xml")
+            if os.path.exists(path):
+                extensions.append("file://" + urllib.pathname2url(path))
+        return url, extensions
+
 
 class ConfigLoader(BaseLoader):
     def __init__(self, schema):
-        if schema.istypegroup():
+        if schema.isabstract():
             raise ZConfig.SchemaError(
                 "cannot check a configuration an abstract type")
         BaseLoader.__init__(self)
@@ -161,18 +180,18 @@
         self._parse_resource(sm, resource)
         return sm.finish(), CompositeHandler(self.handlers, self.schema)
 
-    # parser support API
+    # config 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():
+        if t.isabstract():
             raise ZConfig.ConfigurationError(
                 "concrete sections cannot match abstract section types;"
                 " found abstract type " + `type`)
         ci = parent.type.getsectioninfo(type, name)
-        assert not ci.istypegroup()
+        assert not ci.isabstract()
         if not ci.isAllowedName(name):
             raise ZConfig.ConfigurationError(
                 "%s is not an allowed name for %s sections"
@@ -184,19 +203,22 @@
         sectvalue = matcher.finish()
         parent.addSection(type, name, sectvalue)
 
-    def includeConfiguration(self, section, url):
+    def includeConfiguration(self, section, url, defines):
         r = self.openResource(url)
-        self._parse_resource(section, r)
+        self._parse_resource(section, r, defines)
 
     # internal helper
 
-    def _parse_resource(self, matcher, resource):
+    def _parse_resource(self, matcher, resource, defines=None):
         from ZConfig.cfgparser import ZConfigParser
-        parser = ZConfigParser(resource, self)
+        parser = ZConfigParser(resource, self, defines)
         parser.parse(matcher)
 
 
 class CompositeHandler:
+    __metatype__ = type
+    __slots__ = '_handlers', '_convert'
+
     def __init__(self, handlers, schema):
         self._handlers = handlers
         self._convert = schema.registry.get("basic-key")


=== Zope/lib/python/ZConfig/matcher.py 1.1.2.32 => 1.1.2.33 ===
--- Zope/lib/python/ZConfig/matcher.py:1.1.2.32	Fri Jan  3 13:54:01 2003
+++ Zope/lib/python/ZConfig/matcher.py	Thu Jan  9 14:27:45 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,
@@ -17,6 +17,8 @@
 
 import ZConfig
 
+from ZConfig.info import ValueInfo
+
 
 class BaseMatcher:
     def __init__(self, info, type, handlers):
@@ -54,7 +56,7 @@
             raise ZConfig.ConfigurationError(
                 "too many instances of %s section" % `ci.sectiontype.name`)
 
-    def addValue(self, key, value):
+    def addValue(self, key, value, position):
         length = len(self.type)
         arbkey_info = None
         for i in range(length):
@@ -92,6 +94,7 @@
             raise ZConfig.ConfigurationError(
                 "too many values for " + `name`)
 
+        value = ValueInfo(value, position)
         if k == '+':
             if ismulti:
                 if v.has_key(key):
@@ -100,7 +103,8 @@
                     v[key] = [value]
             else:
                 if v.has_key(key):
-                    raise ZConfig.ConfigurationError("too many")
+                    raise ZConfig.ConfigurationError(
+                        "too many values for " + `key`)
                 v[key] = value
         elif ismulti:
             v.append(value)
@@ -165,9 +169,9 @@
                 elif ci.name == '+':
                     v = values[i]
                     for key, val in v.items():
-                        v[key] = [ci.datatype(s) for s in val]
+                        v[key] = [vi.convert(ci.datatype) for vi in val]
                 else:
-                    v = [ci.datatype(s) for s in values[i]]
+                    v = [vi.convert(ci.datatype) for vi in values[i]]
             elif ci.issection():
                 if values[i] is not None:
                     v = values[i]._type.datatype(values[i])
@@ -176,11 +180,11 @@
             elif name == '+':
                 v = values[i]
                 for key, val in v.items():
-                    v[key] = ci.datatype(val)
+                    v[key] = val.convert(ci.datatype)
             else:
                 v = values[i]
                 if v is not None:
-                    v = ci.datatype(v)
+                    v = v.convert(ci.datatype)
             values[i] = v
             if ci.handler is not None:
                 self._handlers.append((ci.handler, v))


=== Zope/lib/python/ZConfig/schema.py 1.1.2.39 => 1.1.2.40 === (420/520 lines abridged)
--- Zope/lib/python/ZConfig/schema.py:1.1.2.39	Fri Jan  3 13:54:58 2003
+++ Zope/lib/python/ZConfig/schema.py	Thu Jan  9 14:27:45 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,
@@ -44,10 +44,10 @@
     return parser._schema
 
 
-class SchemaParser(xml.sax.ContentHandler):
+class BaseParser(xml.sax.ContentHandler):
 
     _cdata_tags = "description", "metadefault", "example", "default"
-    _handled_tags = ("schema", "import", "sectiongroup", "sectiontype",
+    _handled_tags = ("import", "abstracttype", "sectiontype",
                      "key", "multikey", "section", "multisection")
 
     def __init__(self, registry, loader, url):
@@ -60,8 +60,8 @@
         self._prefixes = []
         self._schema = None
         self._stack = []
-        self._group = None
         self._url = url
+        self._components = {}
 
     # SAX 2 ContentHandler methods
 
@@ -70,10 +70,10 @@
 
     def startElement(self, name, attrs):
         attrs = dict(attrs)
-        if name == "schema":
+        if name == self._top_level:
             if self._schema is not None:
                 self.error("schema element improperly nested")
-            self.start_schema(attrs)
+            getattr(self, "start_" + name)(attrs)
         elif name in self._handled_tags:
             if self._schema is None:
                 self.error(name + " element outside of schema")
@@ -84,11 +84,14 @@
             if self._cdata is not None:
                 self.error(name + " element improperly nested")
             self._cdata = []

[-=- -=- -=- 420 lines omitted -=- -=- -=-]

+                "cannot define top-level sections in a schema "
+                + self._top_level)
+        BaseParser.start_section(self, attrs)
+
+    def start_multisection(self, attrs):
+        if not self._stack:
+            self.error(
+                "cannot define top-level multisections in a schema "
+                + self._top_level)
+        BaseParser.start_multisection(self, attrs)
+
+
+class ComponentParser(BaseComponentParser):
+
+    _handled_tags = BaseComponentParser._handled_tags + ("component",)
+    _top_level = "component"
+
+    def __init__(self, registry, loader, url, schema):
+        BaseComponentParser.__init__(self, registry, loader, url, {})
+        self._parent = schema
+
+    def start_component(self, attrs):
+        self._schema = self._parent
+        self.push_prefix(attrs)
+
+    def end_component(self):
+        self.pop_prefix()
+
+    def loadExtension(self, resource):
+        parser = ExtensionParser(self._registry, self._loader, resource.url,
+                                 self._parent, self._localtypes)
+        parser._components = self._components
+        xml.sax.parse(resource.file, parser)
+
+
+class ExtensionParser(BaseComponentParser):
+
+    _handled_tags = BaseComponentParser._handled_tags + ("extension",)
+    _top_level = "extension"
+
+    def __init__(self, registry, loader, url, schema, localtypes):
+        BaseComponentParser.__init__(self, registry, loader, url, localtypes)
+        self._parent = schema
+
+    def start_extension(self, attrs):
+        self._schema = self._parent
+        self.push_prefix(attrs)
+
+    def end_extension(self):
+        self.pop_prefix()


=== Zope/lib/python/ZConfig/substitution.py 1.1.2.1 => 1.1.2.2 ===
--- Zope/lib/python/ZConfig/substitution.py:1.1.2.1	Fri Dec 20 12:49:00 2002
+++ Zope/lib/python/ZConfig/substitution.py	Thu Jan  9 14:27:45 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,


=== Zope/lib/python/ZConfig/url.py 1.1.2.4 => 1.1.2.5 ===
--- Zope/lib/python/ZConfig/url.py:1.1.2.4	Thu Jan  2 12:26:29 2003
+++ Zope/lib/python/ZConfig/url.py	Thu Jan  9 14:27:45 2003
@@ -1,10 +1,44 @@
-"""urlparse-like helpers that support the zconfig scheme."""
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""urlparse-like helpers that normalize file: URLs.
+
+ZConfig and urllib2 expect file: URLs to consistently use the '//'
+hostpart seperator; the functions here enforce this constraint.
+"""
 
 import posixpath as _posixpath
 import urlparse as _urlparse
 
 from urllib import splittype as _splittype
 
+try:
+    from urlparse import urlsplit
+except ImportError:
+    def urlsplit(url):
+        # Check for the fragment here, since Python 2.1.3 didn't get
+        # it right for things like "http://www.python.org#frag".
+        if '#' in url:
+            url, fragment = url.split('#', 1)
+        else:
+            fragment = ''
+        parts = list(_urlparse.urlparse(url))
+        parts[-1] = fragment
+        param = parts.pop(3)
+        if param:
+            parts[2] += ";" + param
+        return tuple(parts)
+
 
 def urlnormalize(url):
     parts = urlsplit(url)
@@ -18,7 +52,9 @@
 
 
 def urlunsplit(parts):
-    url = _urlparse.urlunsplit(parts)
+    parts = list(parts)
+    parts.insert(3, '')
+    url = _urlparse.urlunparse(tuple(parts))
     if (  parts[0] == "file"
           and url.startswith("file:/")
           and not url.startswith("file:///")):
@@ -27,68 +63,12 @@
 
 
 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
+    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
+    url = _urlparse.urljoin(base, relurl)
+    if url.startswith("file:/") and not url.startswith("file:///"):
+        url = "file://" + url[5:]
+    return url