[Zope-Checkins] CVS: Packages/ZConfig - schema.py:1.1.2.1 SchemaParser.py:NONE

Fred L. Drake, Jr. fred@zope.com
Tue, 10 Dec 2002 16:50:07 -0500


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

Added Files:
      Tag: zconfig-schema-devel-branch
	schema.py 
Removed Files:
      Tag: zconfig-schema-devel-branch
	SchemaParser.py 
Log Message:
Rename "SchemaParser" module to "schema".

=== Added File Packages/ZConfig/schema.py ===
"""Parser for ZConfig schemas."""

import xml.sax

import ZConfig

from ZConfig import datatypes
from ZConfig import info


default_value_type = datatypes.get("str")
default_key_type = datatypes.get("basic-key")
default_name_type = default_key_type

_identifier = datatypes.get("identifier").convert


class SchemaParser(xml.sax.ContentHandler):

    _cdata_tags = "description", "metadefault", "example", "default"
    _handled_tags = "schema", "key", "section", "sectiongroup"

    def __init__(self):
        self._cdata = None
        self._locator = None
        self._prefixes = []
        self._schema = None
        self._stack = []
        self._sections = []

    def parseStream(self, stream):
        xml.sax.parse(stream, self)
        return self._schema

    # SAX 2 ContentHandler methods

    def setDocumentLocator(self, locator):
        self._locator = locator

    def startElement(self, name, attrs):
        attrs = dict(attrs)
        if name == "schema":
            if self._schema:
                self.doSchemaError("schema element improperly nested")
            self.start_schema(attrs)
        elif name in self._handled_tags:
            if not self._schema:
                self.doSchemaError(name + " element outside of schema")
            getattr(self, "start_" + name)(attrs)
        elif name in self._cdata_tags:
            if not self._schema:
                self.doSchemaError(name + " element outside of schema")
            if self._cdata is not None:
                self.doSchemaError(name + " element improperly nested")
            self._cdata = []
        else:
            self.doSchemaError("Unknown tag " + name)

    def characters(self, data):
        if self._cdata is not None:
            self._cdata.append(data)
        elif data.strip():
            self.doSchemaError("unexpected non-blank character data: "
                               + data.strip())

    def endElement(self, name):
        if name in self._handled_tags:
            getattr(self, "end_" + name)()
        else:
            data = ''.join(self._cdata).strip()
            self._cdata = None
            if name == "default":
                # value for a key
                self._stack[-1].adddefault(data)
            else:
                setattr(self._stack[-1], name, data)

    def endDocument(self):
        if not self._schema:
            self.doSchemaError("no schema found")

    # schema loading logic

    def get_classname(self, name):
        if name.startswith(".") and self._prefixes:
            return self._prefixes[-1] + name
        else:
            return name

    def push_prefix(self, attrs):
        name = attrs.get("prefix")
        self._prefixes.append(self.get_classname(name or ""))

    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
        self._schema = info.SchemaInfo(handler, keytype)
        self._sections = [self._schema]
        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_section(self, attrs):
        self.push_prefix(attrs)
        type = attrs.get("type")
        if not type:
            self.doSchemaError("section does not specify type")
        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
        maxOccurs, minOccurs, handler = self.get_common_info(attrs)
        any, names, attribute = self.get_names_info(attrs, nametype)
        if self._stack:
            parent = self._stack[-1]
        else:
            parent = self._schema
        section = info.SectionInfo(None, None, minOccurs, maxOccurs, handler,
                                   attribute, keytype, names, nametype)
        if any:
            parent.addsection(None, section)
        else:
            for n in names:
                parent.addsection(n, 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):
        self.push_prefix(attrs)
        name = attrs.get("name")
        if not name:
            self.doSchemaError("section group must be named")
        raise NotImpementedError("sectiongroup support not yet implemented")

    def end_sectiongroup(self):
        del self._prefixes[-1]

    def start_key(self, attrs):
        name = attrs.get("name")
        if not name:
            self.doSchemaError("key name may not be omitted or empty")
        # run the keytype converter to make sure this is a valid key
        name = self._stack[-1].keytype.convert(name)
        datatype = datatypes.get(attrs.get("type"))
        maxOccurs, minOccurs, handler = self.get_common_info(attrs)
        attribute = attrs.get("attribute")
        if attribute:
            attribute = _identifier(attribute)
        else:
            attribute = _identifier(name.replace("-", "_"))
        key = info.KeyInfo(name, datatype, minOccurs, maxOccurs, handler,
                           attribute)
        self._sections[-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.doSchemaError("minOccurs cannot be more than maxOccurs")
        else:
            minOccurs = None
        handler = attrs.get("handler")
        if handler:
            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")
            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"])
        else:
            aname = None

        return None, tuple(names), aname

    def doSchemaError(self, message):
        raise ZConfig.ConfigurationError(message)

=== Removed File Packages/ZConfig/SchemaParser.py ===