[Zope3-checkins] CVS: Packages/ZConfig - datatypes.py:1.1.2.6 info.py:1.1.2.7 loader.py:1.1.2.9 matcher.py:1.1.2.12 schema.py:1.1.2.8
Fred L. Drake, Jr.
fred@zope.com
Thu, 12 Dec 2002 13:11:21 -0500
Update of /cvs-repository/Packages/ZConfig
In directory cvs.zope.org:/tmp/cvs-serv15000
Modified Files:
Tag: zconfig-schema-devel-branch
datatypes.py info.py loader.py matcher.py schema.py
Log Message:
Support the sectiontype element in schemas.
This requires separation of the SectionInfo and SectionType information;
the schema itself is only represented as type info.
(The tests pass, but new tests are definately needed.)
=== Packages/ZConfig/datatypes.py 1.1.2.5 => 1.1.2.6 ===
--- Packages/ZConfig/datatypes.py:1.1.2.5 Wed Dec 11 16:06:44 2002
+++ Packages/ZConfig/datatypes.py Thu Dec 12 13:11:20 2002
@@ -22,6 +22,11 @@
False = 0
+class NullConversion:
+ def convert(self, value):
+ return value
+
+
class TrivialConversion:
"""Datatype that exposes a conversion implemented as a function."""
=== Packages/ZConfig/info.py 1.1.2.6 => 1.1.2.7 ===
--- Packages/ZConfig/info.py:1.1.2.6 Thu Dec 12 00:03:08 2002
+++ Packages/ZConfig/info.py Thu Dec 12 13:11:20 2002
@@ -114,21 +114,59 @@
class SectionInfo(KeyInfo):
- def __init__(self, typename, datatype, minOccurs, maxOccurs, handler,
- attribute, keytype, names, nametype):
- assert keytype is not None
- self.typename = typename
+ def __init__(self, name, sectiontype, minOccurs, maxOccurs, handler,
+ attribute):
+ # name - name of the section; one of '*', '+', or name1
+ # sectiontype - SectionType instance
+ # minOccurs - minimum number of occurances of the section
+ # maxOccurs - maximum number of occurances; if > 1, name
+ # must be '*' or '+'
+ # handler - handler function called when value(s) must take
+ # effect
+ # attribute - name of the attribute on the SectionValue object
+ if maxOccurs > 1:
+ if name not in ('*', '+'):
+ raise ZConfig.ConfigurationError(
+ "sections which can occur more than once must"
+ " use a name of '*' or '+'")
+ if not attribute:
+ raise ZConfig.ConfigurationError(
+ "sections which can occur more than once must"
+ " specify a target attribute name")
+ KeyInfo.__init__(self, name, sectiontype.datatype,
+ minOccurs, maxOccurs, handler, attribute)
+ self.sectiontype = sectiontype
+
+ def issection(self):
+ return True
+
+ def allowUnnamed(self):
+ return self.name == "*"
+
+ def isAllowedName(self, name):
+ if name == "*" or name == "+":
+ return False
+ elif self.name == "+":
+ return bool(name)
+ elif not name:
+ return self.name == "*"
+ else:
+ return name == self.name
+
+
+class SectionType:
+ def __init__(self, name, keytype, valuetype, datatype):
+ # name -
+ # datatype - type for the section itself
+ # keytype - type for the keys themselves
+ # valuetype - default type for key values
+ self.name = name
+ self.datatype = datatype
self.keytype = keytype
- self.names = names # '*', '+', or [name1, ...]
- self.nametype = nametype
+ self.valuetype = valuetype
self._children = [] # [info, ...]
self._attrmap = {} # {attribute: index, ...}
self._keymap = {} # {key: index, ...}
- self._secttypemap = {} # {section type: info, ...}
- if datatype is None:
- datatype = _null_conversion
- KeyInfo.__init__(self, None, datatype, minOccurs, maxOccurs,
- handler, attribute)
def __len__(self):
return len(self._children)
@@ -156,15 +194,7 @@
def addsection(self, name, sectinfo):
assert name not in ("*", "+")
- # Check that this isn't a different definition of the section type,
- # since once _add_child() returns we must succeed, or undo a whole
- # bunch of stuff. This is the easier alternative. ;-)
- oldinfo = self._secttypemap.get(sectinfo.typename)
- if oldinfo not in (None, sectinfo):
- raise ZConfig.ConfigurationError(
- "duplicate definition of section type " + `name`)
self._add_child(name, sectinfo)
- self._secttypemap[sectinfo.typename] = sectinfo
def getinfo(self, key):
if not key:
@@ -188,10 +218,10 @@
if not info.issection():
raise ZConfig.ConfigurationError(
"section name %s already in use for key" % key)
- if not info.typename == type:
+ if not info.sectiontype.name == type:
raise ZConfig.ConfigurationError(
"name %s must be used for a %s section"
- % (`name`, `info.typename`))
+ % (`name`, `info.sectiontype.name`))
return index
elif info.typename == type:
if not (name or info.allowUnnamed()):
@@ -204,27 +234,24 @@
i = self.getsectionindex(type, name)
return self._children[i][1]
- def issection(self):
- return True
- def allowUnnamed(self):
- return self.names == "*"
-
- def isAllowedName(self, name):
- if name == "*" or name == "+":
- return False
- elif self.names == "+":
- return bool(name)
- elif not name:
- return self.names == "*"
- else:
- return name in self.names
-
-
-class SchemaInfo(SectionInfo):
- def __init__(self, datatype, handler, keytype):
- SectionInfo.__init__(self, None, datatype, 1, 1, handler, None,
- keytype, (), None)
+class SchemaType(SectionType):
+ def __init__(self, keytype, valuetype, datatype, handler):
+ SectionType.__init__(self, None, keytype, valuetype, datatype)
+ self.handler = handler
+ self._types = {}
+
+ def addtype(self, typeinfo):
+ if self._types.has_key(typeinfo.name):
+ raise ZConfig.ConfigurationError("type name cannot be redefined: "
+ + `typeinfo.name`)
+ self._types[typeinfo.name] = typeinfo
+
+ def gettype(self, name):
+ try:
+ return self._types[name]
+ except KeyError:
+ raise ZConfig.ConfigurationError("unknown type name: " + `name`)
def allowUnnamed(self):
return True
@@ -232,9 +259,5 @@
def isAllowedName(self, name):
return False
-
-class NullConversion:
- def convert(self, value):
- return value
-
-_null_conversion = NullConversion()
+ def issection(self):
+ return True
=== Packages/ZConfig/loader.py 1.1.2.8 => 1.1.2.9 ===
--- Packages/ZConfig/loader.py:1.1.2.8 Wed Dec 11 15:18:15 2002
+++ Packages/ZConfig/loader.py Thu Dec 12 13:11:20 2002
@@ -98,7 +98,13 @@
def startSection(self, parent, type, name, delegatename):
if delegatename:
raise NotImpementedError("section delegation is not yet supported")
- ci = parent.info.getsectioninfo(type, name)
+ info = parent.info
+ info = getattr(info, "sectiontype", info)
+ ci = info.getsectioninfo(type, name)
+ if not ci.isAllowedName(name):
+ raise ZConfig.ConfigurationError(
+ "%s is not an allowed name for %s sections"
+ % (`name`, `ci.sectiontype.name`))
return matcher.SectionMatcher(ci, name)
def endSection(self, parent, type, name, delegatename, matcher):
=== Packages/ZConfig/matcher.py 1.1.2.11 => 1.1.2.12 ===
--- Packages/ZConfig/matcher.py:1.1.2.11 Thu Dec 12 00:03:08 2002
+++ Packages/ZConfig/matcher.py Thu Dec 12 13:11:20 2002
@@ -19,24 +19,21 @@
class SectionMatcher:
- def __init__(self, info, name=None):
+ def __init__(self, info, name):
if name is not None:
- self.name = info.nametype.convert(name)
- if not info.isAllowedName(self.name):
- raise ZConfig.ConfigurationError(
- "%s is not an allowed name for %s sections"
- % (`name`, `info.name`))
+ self.name = name
elif info.allowUnnamed():
self.name = None
else:
raise ZConfig.ConfigurationError(
`info.name` + " sections may not be unnamed")
self.info = info
- self._values = [None] * len(info)
+ self.type = info.sectiontype
+ self._values = [None] * len(self.type)
def addSection(self, type, name, sectvalue):
- i = self.info.getsectionindex(type, name)
- ci = self.info.getsectioninfo(type, name)
+ i = self.type.getsectionindex(type, name)
+ ci = self.type.getsectioninfo(type, name)
v = self._values[i]
if v is None and ci.ismulti():
v = [sectvalue]
@@ -47,9 +44,9 @@
self._values[i] = sectvalue
def addValue(self, key, value):
- length = len(self.info)
+ length = len(self.type)
for i in range(length):
- k, ci = self.info[i]
+ k, ci = self.type[i]
if k == key:
break
else:
@@ -57,7 +54,7 @@
`key` + " is not a known key name")
if ci.issection():
if ci.name:
- extra = " in %s sections" % `self.info.name`
+ extra = " in %s sections" % `self.type.name`
else:
extra = ""
raise ZConfig.ConfigurationError(
@@ -83,11 +80,11 @@
def finish(self):
"""Check the constraints of the section and convert to an application
object."""
- length = len(self.info)
+ length = len(self.type)
values = self._values
attrnames = [None] * length
for i in range(length):
- key, ci = self.info[i]
+ key, ci = self.type[i]
attrnames[i] = ci.attribute or key
v = values[i]
if v is None and ci.minOccurs:
@@ -119,7 +116,7 @@
def constuct(self, attrnames):
values = self._values
for i in range(len(values)):
- name, ci = self.info[i]
+ name, ci = self.type[i]
dt = ci.datatype
if ci.ismulti():
values[i] = [dt.convert(s) for s in values[i]]
@@ -129,20 +126,22 @@
# XXX Really should delay this until after all the
# XXX sibling SectionValue instances have been created and
# XXX we're ready to construct the parent.
- if self.info.handler:
+ if self.info.handler is not None:
v = self.info.handler(v)
return v
class SchemaMatcher(SectionMatcher):
def __init__(self, info):
- SectionMatcher.__init__(self, info, None)
+ self.info = info
+ self.type = info
+ self._values = [None] * len(info)
def finish(self):
# Since there's no outer container to call datatype.convert()
# for the schema, we convert on the way out.
v = SectionMatcher.finish(self)
- return self.info.datatype.convert(v)
+ return self.type.datatype.convert(v)
class SectionValue:
=== Packages/ZConfig/schema.py 1.1.2.7 => 1.1.2.8 ===
--- Packages/ZConfig/schema.py:1.1.2.7 Thu Dec 12 00:03:08 2002
+++ Packages/ZConfig/schema.py Thu Dec 12 13:11:20 2002
@@ -34,6 +34,7 @@
default_value_type = datatypes.get("str")
default_key_type = datatypes.get("basic-key")
default_name_type = default_key_type
+default_section_type = datatypes.NullConversion()
_identifier = datatypes.get("identifier").convert
@@ -41,7 +42,7 @@
class SchemaParser(xml.sax.ContentHandler):
_cdata_tags = "description", "metadefault", "example", "default"
- _handled_tags = "schema", "key", "section", "sectiongroup"
+ _handled_tags = "schema", "key", "section", "sectiongroup", "sectiontype"
def __init__(self):
self._cdata = None
@@ -49,7 +50,6 @@
self._prefixes = []
self._schema = None
self._stack = []
- self._sections = []
def parseStream(self, stream):
xml.sax.parse(stream, self)
@@ -111,67 +111,69 @@
return name
def push_prefix(self, attrs):
- name = attrs.get("prefix")
- self._prefixes.append(self.get_classname(name or ""))
+ name = attrs.get("prefix", "")
+ prefix = self.get_classname(name)
+ if prefix[:1] == ".":
+ raise ZConfig.ConfigurationError("prefix may not begin with '.'")
+ self._prefixes.append(prefix)
+
+ def get_datatype(self, attrs, key, default):
+ if attrs.has_key(key):
+ dtname = self.get_classname(attrs[key])
+ return datatypes.get(dtname)
+ else:
+ return default
+
+ def get_sect_typeinfo(self, attrs):
+ keytype = self.get_datatype(attrs, "keytype", default_key_type)
+ valuetype = self.get_datatype(attrs, "valuetype", default_value_type)
+ datatype = self.get_datatype(attrs, "type", default_section_type)
+ return keytype, valuetype, datatype
def start_schema(self, attrs):
- self._prefixes.append(attrs.get("prefix", ""))
- handler = attrs.get("handler")
- if handler:
- handler = self.get_classname(handler)
- if attrs.has_key("keytype"):
- keytype = datatypes.get(attrs["keytype"])
- else:
- keytype = default_key_type
- datatype = attrs.get("type")
- if datatype:
- datatype = datatypes.get(self.get_classname(datatype))
- self._schema = info.SchemaInfo(datatype, handler, keytype)
- self._sections = [self._schema]
+ self.push_prefix(attrs)
+ handler = self.get_classname(attrs.get("handler", "")) or None
+ keytype, valuetype, datatype = self.get_sect_typeinfo(attrs)
+ self._schema = info.SchemaType(keytype, valuetype, datatype, handler)
self._stack = [self._schema]
def end_schema(self):
del self._prefixes[-1]
assert not self._prefixes
- del self._sections[-1]
- assert not self._sections
+
+ def start_sectiontype(self, attrs):
+ name = attrs.get("name")
+ if not name:
+ raise ZConfig.ConfigurationError(
+ "sectiontype name must not be omitted or empty")
+ name = _identifier(name)
+ keytype, valuetype, datatype = self.get_sect_typeinfo(attrs)
+ sectinfo = info.SectionType(name, keytype, valuetype, datatype)
+ self._schema.addtype(sectinfo)
+ self._stack.append(sectinfo)
+
+ def end_sectiontype(self):
+ self._stack.pop()
def start_section(self, attrs):
self.push_prefix(attrs)
- type = attrs.get("sectiontype")
+ type = attrs.get("type")
if not type:
- self.doSchemaError("section must specify sectiontype")
- if attrs.has_key("keytype"):
- keytype = datatypes.get(attrs["keytype"])
- else:
- keytype = default_key_type
- if attrs.has_key("nametype"):
- nametype = datatypes.get(attrs["nametype"])
- else:
- nametype = default_name_type
- datatype = attrs.get("type")
- if datatype:
- datatype = datatypes.get(self.get_classname(datatype))
+ self.doSchemaError("section must specify type")
+ sectiontype = self._schema.gettype(type)
maxOccurs, minOccurs, handler = self.get_common_info(attrs)
- any, names, attribute = self.get_names_info(attrs, nametype)
+ any, name, attribute = self.get_name_info(attrs)
if self._stack:
parent = self._stack[-1]
else:
parent = self._schema
- section = info.SectionInfo(type, datatype, minOccurs, maxOccurs,
- handler,
- attribute, keytype, names, nametype)
- if any:
- parent.addsection(None, section)
- else:
- for n in names:
- parent.addsection(n, section)
+ section = info.SectionInfo(name, sectiontype, minOccurs, maxOccurs,
+ handler, attribute)
+ parent.addsection(name, section)
self._stack.append(section)
- self._sections.append(section)
def end_section(self):
del self._prefixes[-1]
- del self._sections[-1]
self._stack.pop().finish()
def start_sectiongroup(self, attrs):
@@ -199,7 +201,7 @@
attribute = _identifier(name.replace("-", "_"))
key = info.KeyInfo(name, datatype, minOccurs, maxOccurs, handler,
attribute)
- self._sections[-1].addkey(key)
+ self._stack[-1].addkey(key)
self._stack.append(key)
def end_key(self):
@@ -226,44 +228,21 @@
handler = self.get_classname(handler)
return maxOccurs, minOccurs, handler
- def get_names_info(self, attrs, nametype):
- if not attrs.get("names"):
- self.doSchemaError("allowed section names must be specified")
- anyname = None
- names = []
- s = attrs["names"]
- if s in ("*", "+"):
- aname = attrs.get("attribute")
+ def get_name_info(self, attrs):
+ name = attrs.get("name")
+ if not name:
+ self.doSchemaError("section name must be specified and non-empty")
+ aname = attrs.get("attribute")
+ if aname:
+ aname = _identifier(aname)
+ if name in ("*", "+"):
if not aname:
self.doSchemaError(
- "container attribute must be specified when using"
- " '*' or '+' for section names")
- return s, (), _identifier(aname)
-
- for s in s.split("|"):
- s = s.strip()
- if not s:
- self.doSchemaError("empty section name not allowed")
- if s in ("*", "+"):
- self.doSchemaError(
- "'+' and '*' cannot be combined with other names")
- else:
- s = nametype.convert(s)
- if s in ("*", "+"):
- self.doSchemaError("nametypes may not convert to"
- " '*' or '+'")
- names.append(s)
-
- if attrs.has_key("attribute"):
- if len(names) > 1:
- self.doSchemaError(
- "cannot give target attribute name with"
- " multiple named sections")
- aname = _identifier(attrs["attribute"])
+ "container attribute must be specified and non-empty"
+ " when using '*' or '+' for a section name")
+ return name, None, aname
else:
- aname = None
-
- return None, tuple(names), aname
+ return None, _identifier(name), aname
def doSchemaError(self, message):
raise ZConfig.ConfigurationError(message)