[Zope-Checkins] CVS: Packages/ZConfig - info.py:1.1.2.16 matcher.py:1.1.2.23 schema.py:1.1.2.22

Fred L. Drake, Jr. fred@zope.com
Wed, 18 Dec 2002 11:16:30 -0500


Update of /cvs-repository/Packages/ZConfig
In directory cvs.zope.org:/tmp/cvs-serv21256

Modified Files:
      Tag: zconfig-schema-devel-branch
	info.py matcher.py schema.py 
Log Message:
Changes to the schema language:
- The minOccurs and maxOccurs attributes are gone
- To specify minOccurs==1, use required="yes" in the schema
- To specify maxOccurs=="unbounded", use a <multikey> or <multisection>
  instead of <key> or <section>

This needs some code review and refactoring, but works.


=== Packages/ZConfig/info.py 1.1.2.15 => 1.1.2.16 ===
--- Packages/ZConfig/info.py:1.1.2.15	Fri Dec 13 16:03:49 2002
+++ Packages/ZConfig/info.py	Wed Dec 18 11:15:59 2002
@@ -82,6 +82,7 @@
 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
@@ -90,11 +91,6 @@
     def finish(self):
         if self._finished:
             raise ValueError("cannot finish KeyInfo more than once")
-        if self.minOccurs is None:
-            if self._default is not None:
-                self.minOccurs = 1
-            else:
-                self.minOccurs = 0
         self._finished = True
 
     def adddefault(self, value):


=== Packages/ZConfig/matcher.py 1.1.2.22 => 1.1.2.23 ===
--- Packages/ZConfig/matcher.py:1.1.2.22	Fri Dec 13 16:06:46 2002
+++ Packages/ZConfig/matcher.py	Wed Dec 18 11:15:59 2002
@@ -110,7 +110,7 @@
                     v = values[i] = default[:]
             if ci.ismulti():
                 if v is None:
-                    v = values[i] = []
+                    v = values[i] = ci.getdefault()[:]
                 if len(v) < ci.minOccurs:
                     raise ZConfig.ConfigurationError(
                         "not enough values for %s; %d found, %d required"
@@ -129,16 +129,16 @@
             name, ci = self.type[i]
             if ci.ismulti():
                 if ci.issection():
-                    values[i] = [s.__type__.datatype(s)
-                                 for s in values[i]]
+                    v = [s.__type__.datatype(s) for s in values[i]]
                 else:
-                    values[i] = [ci.datatype(s) for s in values[i]]
+                    v = [ci.datatype(s) for s in values[i]]
             elif ci.issection():
-                values[i] = values[i].__type__.datatype(values[i])
+                v = values[i].__type__.datatype(values[i])
             else:
-                values[i] = ci.datatype(values[i])
+                v = ci.datatype(values[i])
+            values[i] = v
             if ci.handler is not None:
-                self._handlers.append((ci.handler, values[i]))
+                self._handlers.append((ci.handler, v))
         return self.createValue(attrnames)
 
     def createValue(self, attrnames):


=== Packages/ZConfig/schema.py 1.1.2.21 => 1.1.2.22 ===
--- Packages/ZConfig/schema.py:1.1.2.21	Mon Dec 16 13:37:52 2002
+++ Packages/ZConfig/schema.py	Wed Dec 18 11:15:59 2002
@@ -21,6 +21,12 @@
 
 
 try:
+    True
+except NameError:
+    True = 1
+    False = 0
+
+try:
     dict
 except NameError:
     def dict(mapping):
@@ -33,7 +39,8 @@
 class SchemaParser(xml.sax.ContentHandler):
 
     _cdata_tags = "description", "metadefault", "example", "default"
-    _handled_tags = "schema", "key", "section", "sectiongroup", "sectiontype"
+    _handled_tags = ("schema", "sectiongroup", "sectiontype",
+                     "key", "multikey", "section", "multisection")
 
     def __init__(self, registry):
         self._registry = registry
@@ -170,21 +177,59 @@
         del self._prefixes[-1]
         self._stack.pop()
 
-    def start_section(self, attrs):
+    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")
-        sectiontype = self._schema.gettype(type)
-        maxOccurs, minOccurs, handler = self.get_common_info(attrs)
+        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 = info.SectionInfo(any or name, sectiontype,
-                                   minOccurs, maxOccurs, handler, attribute)
+        if any or not name:
+            self.error("section name may not be '*' or '+'")
+        section = info.SectionInfo(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)
+        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")
@@ -201,45 +246,48 @@
         self._group = None
         self._stack.pop().finish()
 
-    def start_key(self, attrs):
+    def get_key_info(self, attrs, element):
         name = attrs.get("name")
         if not name:
-            self.error("key name may not be omitted or empty")
+            self.error(element + " name may not be omitted or empty")
         # run the keytype converter to make sure this is a valid key
         name = self._stack[-1].keytype(name)
-        datatype = self._registry.get(attrs.get("datatype"))
-        maxOccurs, minOccurs, handler = self.get_common_info(attrs)
+        datatype = self.get_datatype(attrs, "datatype", "str")
+        handler = self.get_handler(attrs)
         attribute = attrs.get("attribute")
-        if attribute:
+        if attribute is not None:
             attribute = self._identifier(attribute)
         else:
             attribute = self._identifier(name.replace("-", "_"))
-        key = info.KeyInfo(name, datatype, minOccurs, maxOccurs, handler,
-                           attribute)
+        return name, 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())
         self._stack[-1].addkey(key)
         self._stack.append(key)
 
     def end_key(self):
         self._stack.pop().finish()
 
-    def get_common_info(self, attrs):
-        maxOccurs = attrs.get("maxOccurs")
-        if maxOccurs is not None:
-            if maxOccurs == "unbounded":
-                maxOccurs = info.Unbounded
-            else:
-                maxOccurs = int(maxOccurs)
-        else:
-            maxOccurs = 1
-        minOccurs = attrs.get("minOccurs")
-        if minOccurs:
-            minOccurs = int(minOccurs)
-            if minOccurs > maxOccurs:
-                self.error("minOccurs cannot be more than maxOccurs")
-        else:
-            minOccurs = None
-        handler = self.get_handler(attrs)
-        return maxOccurs, minOccurs, handler
+    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):
         name = attrs.get("name")