[Zodb-checkins] CVS: Zope/lib/python/ZConfig - __init__.py:1.9.22.4 cfgparser.py:1.7.52.4 cmdline.py:1.1.44.1 info.py:1.15.12.4 loader.py:1.18.44.4 schema.py:1.20.22.4 Config.py:NONE Context.py:NONE

Fred L. Drake, Jr. fred at zope.com
Tue Oct 7 15:36:53 EDT 2003


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

Modified Files:
      Tag: Zope-2_7-branch
	__init__.py cfgparser.py cmdline.py info.py loader.py 
	schema.py 
Removed Files:
      Tag: Zope-2_7-branch
	Config.py Context.py 
Log Message:
Merge the ZConfig trunk to Zope 2.7.
This adds support for two approaches to schema extension.


=== Zope/lib/python/ZConfig/__init__.py 1.9.22.3 => 1.9.22.4 ===


=== Zope/lib/python/ZConfig/cfgparser.py 1.7.52.3 => 1.7.52.4 ===
--- Zope/lib/python/ZConfig/cfgparser.py:1.7.52.3	Fri Sep 19 17:24:51 2003
+++ Zope/lib/python/ZConfig/cfgparser.py	Tue Oct  7 15:36:21 2003
@@ -36,7 +36,7 @@
         self.file = resource.file
         self.url = resource.url
         self.lineno = 0
-        self.stack = []   # [(type, name, delegatename, prevmatcher), ...]
+        self.stack = []   # [(type, name, prevmatcher), ...]
         if defines is None:
             defines = {}
         self.defs = defines
@@ -89,33 +89,32 @@
         m = _section_start_rx.match(text)
         if not m:
             self.error("malformed section header")
-        type, name, delegatename = m.group('type', 'name', 'delegatename')
+        type, name = m.group('type', 'name')
         type = type.lower()
         if name:
             name = name.lower()
         try:
-            newsect = self.context.startSection(section, type, name,
-                                                delegatename)
+            newsect = self.context.startSection(section, type, name)
         except ZConfig.ConfigurationError, e:
             self.error(e[0])
 
         if isempty:
-            self.context.endSection(section, type, name, delegatename, newsect)
+            self.context.endSection(section, type, name, newsect)
             return section
         else:
-            self.stack.append((type, name, delegatename, section))
+            self.stack.append((type, name, section))
             return newsect
 
     def end_section(self, section, rest):
         if not self.stack:
             self.error("unexpected section end")
         type = rest.rstrip().lower()
-        opentype, name, delegatename, prevsection = self.stack.pop()
+        opentype, name, prevsection = self.stack.pop()
         if type != opentype:
             self.error("unbalanced section end")
         try:
             self.context.endSection(
-                prevsection, type, name, delegatename, section)
+                prevsection, type, name, section)
         except ZConfig.ConfigurationError, e:
             self.error(e[0])
         return prevsection
@@ -139,7 +138,7 @@
         if not m:
             self.error("missing or unrecognized directive")
         name, arg = m.group('key', 'value')
-        if name not in ("define", "include"):
+        if name not in ("define", "import", "include"):
             self.error("unknown directive: " + `name`)
         if not arg:
             self.error("missing argument to %%%s directive" % name)
@@ -147,10 +146,17 @@
             self.handle_include(section, arg)
         elif name == "define":
             self.handle_define(section, arg)
+        elif name == "import":
+            self.handle_import(section, arg)
         else:
             assert 0, "unexpected directive for " + `"%" + rest`
 
+    def handle_import(self, section, rest):
+        pkgname = self.replace(rest.strip())
+        self.context.importSchemaComponent(pkgname)
+
     def handle_include(self, section, rest):
+        rest = self.replace(rest.strip())
         newurl = ZConfig.url.urljoin(self.url, rest)
         self.context.includeConfiguration(section, newurl, self.defs)
 
@@ -179,15 +185,13 @@
 
 
 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 does not allow "(" or ")" for historical reasons.  Though
+# the restriction could be lifted, there seems no need to do so.
 _name_re = r"[^\s()]+"
 _keyvalue_rx = re.compile(r"(?P<key>%s)\s*(?P<value>[^\s].*)?$"
                           % _name_re)
 _section_start_rx = re.compile(r"(?P<type>%s)"
                                r"(?:\s+(?P<name>%s))?"
-                               r"(?:\s*[(](?P<delegatename>%s)[)])?"
                                r"$"
-                               % (_name_re, _name_re, _name_re))
+                               % (_name_re, _name_re))
 del re


=== Zope/lib/python/ZConfig/cmdline.py 1.1 => 1.1.44.1 ===
--- Zope/lib/python/ZConfig/cmdline.py:1.1	Wed Feb 19 17:25:47 2003
+++ Zope/lib/python/ZConfig/cmdline.py	Tue Oct  7 15:36:21 2003
@@ -76,7 +76,6 @@
         for item in options:
             optpath, val, pos = item
             name = sectiontype.keytype(optpath[0])
-            key = None, name # section type, name
             if len(optpath) == 1:
                 self.add_value(name, val, pos)
             else:


=== Zope/lib/python/ZConfig/info.py 1.15.12.3 => 1.15.12.4 ===
--- Zope/lib/python/ZConfig/info.py:1.15.12.3	Fri Sep 19 17:24:51 2003
+++ Zope/lib/python/ZConfig/info.py	Tue Oct  7 15:36:21 2003
@@ -368,6 +368,7 @@
                  registry):
         SectionType.__init__(self, None, keytype, valuetype, datatype,
                              registry, {})
+        self._components = {}
         self.handler = handler
         self.url = url
 
@@ -411,3 +412,23 @@
         t._keymap.update(base._keymap)
         t._children.extend(base._children)
         return t
+
+    def addComponent(self, name):
+        if self._components.has_key(name):
+            raise ZConfig.SchemaError("already have component %s" % name)
+        self._components[name] = name
+
+    def hasComponent(self, name):
+        return self._components.has_key(name)
+
+
+def createDerivedSchema(base):
+    new = SchemaType(base.keytype, base.valuetype, base.datatype,
+                     base.handler, base.url, base.registry)
+    new._components.update(base._components)
+    new.description = base.description
+    new._children[:] = base._children
+    new._attrmap.update(base._attrmap)
+    new._keymap.update(base._keymap)
+    new._types.update(base._types)
+    return new


=== Zope/lib/python/ZConfig/loader.py 1.18.44.3 => 1.18.44.4 ===
--- Zope/lib/python/ZConfig/loader.py:1.18.44.3	Fri Sep 19 17:24:51 2003
+++ Zope/lib/python/ZConfig/loader.py	Tue Oct  7 15:36:21 2003
@@ -21,6 +21,7 @@
 import ZConfig
 import ZConfig.cfgparser
 import ZConfig.datatypes
+import ZConfig.info
 import ZConfig.matcher
 import ZConfig.schema
 import ZConfig.url
@@ -75,12 +76,15 @@
         if not url:
             url = _url_from_file(file)
         r = self.createResource(file, url)
-        return self.loadResource(r)
+        try:
+            return self.loadResource(r)
+        finally:
+            r.close()
 
     # utilities
 
     def loadResource(self, resource):
-        raise NotImpementedError(
+        raise NotImplementedError(
             "BaseLoader.loadResource() must be overridden by a subclass")
 
     def openResource(self, url):
@@ -130,8 +134,7 @@
         if resource.url and self._cache.has_key(resource.url):
             schema = self._cache[resource.url]
         else:
-            schema = ZConfig.schema.parseResource(resource,
-                                                  self.registry, self)
+            schema = ZConfig.schema.parseResource(resource, self)
             self._cache[resource.url] = schema
         return schema
 
@@ -142,20 +145,28 @@
         if not parts:
             raise ZConfig.SchemaError(
                 "illegal schema component name: " + `package`)
-        if len(filter(None, parts)) != len(parts):
+        if "" in parts:
             # '' somewhere in the package spec; still illegal
             raise ZConfig.SchemaError(
                 "illegal schema component name: " + `package`)
         file = file or "component.xml"
-        for dir in sys.path:
-            dirname = os.path.join(os.path.abspath(dir), *parts)
+        try:
+            __import__(package)
+        except ImportError, e:
+            raise ZConfig.SchemaError("could not load package %s: %s"
+                                      % (package, str(e)))
+        pkg = sys.modules[package]
+        if not hasattr(pkg, "__path__"):
+            raise ZConfig.SchemaError(
+                "import name does not refer to a package: " + `package`)
+        for dir in pkg.__path__:
+            dirname = os.path.abspath(dir)
             fn = os.path.join(dirname, file)
             if os.path.exists(fn):
-                break
+                return "file://" + urllib.pathname2url(fn)
         else:
             raise ZConfig.SchemaError(
                 "schema component not found: " + `package`)
-        return "file://" + urllib.pathname2url(fn)
 
 
 class ConfigLoader(BaseLoader):
@@ -165,6 +176,7 @@
                 "cannot check a configuration an abstract type")
         BaseLoader.__init__(self)
         self.schema = schema
+        self._private_schema = False
 
     def loadResource(self, resource):
         sm = self.createSchemaMatcher()
@@ -177,10 +189,7 @@
 
     # config parser support API
 
-    def startSection(self, parent, type, name, delegatename):
-        if delegatename:
-            raise NotImpementedError(
-                "section delegation is not yet supported")
+    def startSection(self, parent, type, name):
         t = self.schema.gettype(type)
         if t.isabstract():
             raise ZConfig.ConfigurationError(
@@ -188,15 +197,35 @@
                 " found abstract type " + `type`)
         return parent.createChildMatcher(t, name)
 
-    def endSection(self, parent, type, name, delegatename, matcher):
-        assert not delegatename
+    def endSection(self, parent, type, name, matcher):
         sectvalue = matcher.finish()
         parent.addSection(type, name, sectvalue)
 
+    def importSchemaComponent(self, pkgname):
+        schema = self.schema
+        if not self._private_schema:
+            # replace the schema with an extended schema on the first %import
+            self._loader = SchemaLoader(self.schema.registry)
+            schema = ZConfig.info.createDerivedSchema(self.schema)
+            self._private_schema = True
+            self.schema = schema
+        url = self._loader.schemaComponentSource(pkgname, '')
+        if schema.hasComponent(url):
+            return
+        resource = self.openResource(url)
+        schema.addComponent(url)
+        try:
+            ZConfig.schema.parseComponent(resource, self._loader, schema)
+        finally:
+            resource.close()
+
     def includeConfiguration(self, section, url, defines):
         url = self.normalizeURL(url)
         r = self.openResource(url)
-        self._parse_resource(section, r, defines)
+        try:
+            self._parse_resource(section, r, defines)
+        finally:
+            r.close()
 
     # internal helper
 


=== Zope/lib/python/ZConfig/schema.py 1.20.22.3 => 1.20.22.4 ===
--- Zope/lib/python/ZConfig/schema.py:1.20.22.3	Fri Sep 19 17:24:51 2003
+++ Zope/lib/python/ZConfig/schema.py	Tue Oct  7 15:36:21 2003
@@ -38,12 +38,17 @@
         return d
 
 
-def parseResource(resource, registry, loader):
-    parser = SchemaParser(registry, loader, resource.url)
+def parseResource(resource, loader):
+    parser = SchemaParser(loader, resource.url)
     xml.sax.parse(resource.file, parser)
     return parser._schema
 
 
+def parseComponent(resource, loader, schema):
+    parser = ComponentParser(loader, resource.url, schema)
+    xml.sax.parse(resource.file, parser)
+
+
 def _srepr(ob):
     if isinstance(ob, type(u'')):
         # drop the leading "u" from a unicode repr
@@ -74,18 +79,17 @@
         "multisection": ["schema", "sectiontype"],
         }
 
-    def __init__(self, registry, loader, url):
-        self._registry = registry
+    def __init__(self, loader, url):
+        self._registry = loader.registry
         self._loader = loader
-        self._basic_key = registry.get("basic-key")
-        self._identifier = registry.get("identifier")
+        self._basic_key = self._registry.get("basic-key")
+        self._identifier = self._registry.get("identifier")
         self._cdata = None
         self._locator = None
         self._prefixes = []
         self._schema = None
         self._stack = []
         self._url = url
-        self._components = {}
         self._elem_stack = []
 
     # SAX 2 ContentHandler methods
@@ -299,16 +303,14 @@
         else:
             if os.path.dirname(file):
                 self.error("file may not include a directory part")
-            src = self._loader.schemaComponentSource(pkg, file)
-            if not self._components.has_key(src):
-                self._components[pkg] = src
+            if not self._schema.hasComponent(pkg):
+                src = self._loader.schemaComponentSource(pkg, file)
+                self._schema.addComponent(src)
                 self.loadComponent(src)
 
     def loadComponent(self, src):
         r = self._loader.openResource(src)
-        parser = ComponentParser(self._registry, self._loader, src,
-                                 self._schema)
-        parser._components = self._components
+        parser = ComponentParser(self._loader, src, self._schema)
         try:
             xml.sax.parse(r.file, parser)
         finally:
@@ -327,8 +329,6 @@
         if attrs.has_key("extends"):
             basename = self.basic_key(attrs["extends"])
             base = self._schema.gettype(basename)
-            if not self._localtypes.has_key(basename):
-                self.error("cannot extend type derived outside component")
             if base.isabstract():
                 self.error("sectiontype cannot extend an abstract type")
             if attrs.has_key("keytype"):
@@ -338,7 +338,6 @@
         else:
             sectinfo = self._schema.createSectionType(
                 name, keytype, valuetype, datatype)
-        self._localtypes[name] = sectinfo
         if attrs.has_key("implements"):
             ifname = self.basic_key(attrs["implements"])
             interface = self._schema.gettype(ifname)
@@ -458,15 +457,76 @@
     _handled_tags = BaseParser._handled_tags + ("schema",)
     _top_level = "schema"
 
+    def __init__(self, loader, url, extending_parser=None):
+        BaseParser.__init__(self, loader, url)
+        self._extending_parser = extending_parser
+        self._base_keytypes = []
+        self._base_datatypes = []
+
     def start_schema(self, attrs):
         self.push_prefix(attrs)
         handler = self.get_handler(attrs)
         keytype, valuetype, datatype = self.get_sect_typeinfo(attrs)
-        self._schema = info.SchemaType(keytype, valuetype, datatype,
-                                       handler, self._url, self._registry)
-        self._localtypes = self._schema._types
+
+        if self._extending_parser is None:
+            # We're not being inherited, so we need to create the schema
+            self._schema = info.SchemaType(keytype, valuetype, datatype,
+                                           handler, self._url, self._registry)
+        else:
+            # Parse into the extending ("subclass") parser's schema
+            self._schema = self._extending_parser._schema
+
         self._stack = [self._schema]
 
+        if attrs.has_key("extends"):
+            sources = attrs["extends"].split()
+            sources.reverse()
+
+            for src in sources:
+                src = url.urljoin(self._url, src)
+                src, fragment = url.urldefrag(src)
+                if fragment:
+                    self.error("schema extends many not include"
+                               " a fragment identifier")
+                self.extendSchema(src)
+
+            # Inherit keytype from bases, if unspecified and not conflicting
+            if self._base_keytypes and not attrs.has_key("keytype"):
+                keytype = self._base_keytypes[0]
+                for kt in self._base_keytypes[1:]:
+                    if kt is not keytype:
+                        self.error("base schemas have conflicting keytypes,"
+                                   " but no keytype was specified in the"
+                                   " extending schema")
+
+            # Inherit datatype from bases, if unspecified and not conflicting
+            if self._base_datatypes and not attrs.has_key("datatype"):
+                datatype = self._base_datatypes[0]
+                for dt in self._base_datatypes[1:]:
+                    if dt is not datatype:
+                        self.error("base schemas have conflicting datatypes,"
+                                   " but no datatype was specified in the"
+                                   " extending schema")
+
+        # Reset the schema types to our own, while we parse the schema body
+        self._schema.keytype = keytype
+        self._schema.valuetype = valuetype
+        self._schema.datatype = datatype
+
+        # Update base key/datatypes for the "extending" parser
+        if self._extending_parser is not None:
+            self._extending_parser._base_keytypes.append(keytype)
+            self._extending_parser._base_datatypes.append(datatype)
+
+
+    def extendSchema(self,src):
+        parser = SchemaParser(self._loader, src, self)
+        r = self._loader.openResource(src)
+        try:
+            xml.sax.parse(r.file, parser)
+        finally:
+            r.close()
+
     def end_schema(self):
         del self._stack[-1]
         assert not self._stack
@@ -474,12 +534,13 @@
         assert not self._prefixes
 
 
+class ComponentParser(BaseParser):
 
-class BaseComponentParser(BaseParser):
+    _handled_tags = BaseParser._handled_tags + ("component",)
+    _top_level = "component"
 
-    def __init__(self, registry, loader, url, schema, localtypes):
-        BaseParser.__init__(self, registry, loader, url)
-        self._localtypes = localtypes
+    def __init__(self, loader, url, schema):
+        BaseParser.__init__(self, loader, url)
         self._parent = schema
 
     def characters_description(self, data):
@@ -502,23 +563,14 @@
         self._check_not_toplevel("multisection")
         BaseParser.start_multisection(self, attrs)
 
-    def _check_not_toplevel(self, what):
-        if not self._stack:
-            self.error("cannot define top-level %s in a schema %s"
-                       % (what, self._top_level))
-
-
-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, schema, {})
-
     def start_component(self, attrs):
         self._schema = self._parent
         self.push_prefix(attrs)
 
     def end_component(self):
         self.pop_prefix()
+
+    def _check_not_toplevel(self, what):
+        if not self._stack:
+            self.error("cannot define top-level %s in a schema %s"
+                       % (what, self._top_level))

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

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




More information about the Zodb-checkins mailing list