[Zodb-checkins] CVS: Packages/ZConfig - datatypes.py: info.py: matcher.py: SchemaParser.py: cfgparser.py: DataTypes.py:NONE SchemaInfo.py:NONE SectionMatcher.py:NONE

Fred L. Drake, Jr. fred@zope.com
Tue, 10 Dec 2002 14:17:56 -0500

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

Modified Files:
      Tag: zconfig-schema-devel-branch
	SchemaParser.py cfgparser.py 
Added Files:
      Tag: zconfig-schema-devel-branch
	datatypes.py info.py matcher.py 
Removed Files:
      Tag: zconfig-schema-devel-branch
	DataTypes.py SchemaInfo.py SectionMatcher.py 
Log Message:
Many module renamings; the Zope 3 proposal is the Right Way.

=== Added File Packages/ZConfig/datatypes.py ===
"""Selection of standard datatypes for ZConfig."""

import re

class TrivialConversion:
    """Datatype that exposes a conversion implemented as a function."""

    def __init__(self, conversion):
        self.convert = conversion

class MemoizedConversion:
    """Conversion helper that caches the results of expensive conversions."""

    def __init__(self, conversion):
        self._memo = {}
        self._conversion = conversion

    def convert(self, value):
            return self._memo[value]
        except KeyError:
            v = self._conversion(value)
            self._memo[value] = v
            return v

class RangeCheckedConversion:
    """Conversion helper that range checks another conversion."""

    def __init__(self, conversion, min=None, max=None):
        self._min = min
        self._max = max
        self._conversion = conversion

    def convert(self, value):
        v = self._conversion(value)
        if self._min is not None and v < self._min:
            raise ValueError("%s is below lower bound (%s)"
                             % (`v`, `self._min`))
        if self._max is not None and v > self._max:
            raise ValueError("%s is above upper bound (%s)"
                             % (`v`, `self._max`))
        return v

class RegularExpressionConversion:
    def __init__(self, regex):
        self._rx = re.compile(regex)

    def convert(self, value):
        m = self._rx.match(value)
        if m and m.group() == value:
            return value
            raise ValueError("value did not match regular expression: "
                             + `value`)

class Locale:
    def convert(self, value):
        locale = self._get_locale_module()
        # get current setting of locale
        prev = locale.setlocale(locale.LC_ALL)
                locale.setlocale(locale.LC_ALL, value)
                locale.setlocale(locale.LC_ALL, prev)
        except locale.Error:
            raise ValueError(
                'The specified locale "%s" is not supported by your system.\n'
                'See your operating system documentation for more\n'
                'information on locale support.' % value
        return value

    def _get_locale_module(self):
            return self._locale
        except AttributeError:
                import locale
            except ImportError:
                raise ValueError(
                    'The locale module could not be imported.\n'
                    'To use localization options, you must ensure\n'
                    'that the locale module is compiled into your\n'
                    'Python installation.'
            Locale._locale = locale
            return locale

class BasicKeyConversion(RegularExpressionConversion):
    def __init__(self):
        RegularExpressionConversion.__init__(self, "[_a-zA-Z][-._a-zA-Z0-9]*")

    def convert(self, value):
        return RegularExpressionConversion.convert(self, value).lower()

class IdentifierConversion(RegularExpressionConversion):
    def __init__(self):
        RegularExpressionConversion.__init__(self, "[_a-zA-Z][_a-zA-Z0-9]*")

stock_datatypes = {
    "int":         TrivialConversion(int),
    "float":       TrivialConversion(float),
    "str":         TrivialConversion(str),
    "locale":      MemoizedConversion(Locale().convert),
    "port-number": RangeCheckedConversion(int, min=1, max=0xffff),
    "basic-key":   BasicKeyConversion(),
    "identifier":  IdentifierConversion(),

class Registry:
    def __init__(self):
        self._stock = stock_datatypes.copy()
        self._other = {}

    def get(self, name):
        if name is None:
            name = "str"
        t = self._stock.get(name)
        if t is None:
            t = self._other.get(name)
            if t is None:
                t = self.search(name)
        return t

    def register(self, name, datatype):
        if self._stock.has_key(name):
            raise ValueError("datatype name conflicts with built-in types: "
                             + `name`)
        if self._other.has_key(name):
            raise ValueError("datatype name already registered:" + `name`)
        self._other[name] = datatype

    def search(self, name):
        if not "." in name:
            raise ValueError("unloadable datatype name: " + `name`)
        components = name.split('.')
        start = components[0]
        g = globals()
        package = __import__(start, g, g)
        modulenames = [start]
        for component in components[1:]:
                package = getattr(package, component)
            except AttributeError:
                n = '.'.join(modulenames)
                package = __import__(n, g, g, component)
        datatype = package()
        self._other[name] = datatype
        return datatype

_r = Registry()
get = _r.get
register = _r.register

=== Added File Packages/ZConfig/info.py ===
"""Objects that can describe a ZConfig schema."""

class UnboundedThing:
    def __lt__(self, other):
        return False

    def __le__(self, other):
        return isinstance(other, self.__class__)

    def __gt__(self, other):
        return True

    def __ge__(self, other):
        return True

    def __eq__(self, other):
        return isinstance(other, self.__class__)

    def __ne__(self, other):
        return not isinstance(other, self.__class__)

    def __repr__(self):
        return "<Unbounded>"

Unbounded = UnboundedThing()

class KeyInfo:
    """Information about a single configuration key."""

    def __init__(self, name, datatype, minOccurs, maxOccurs, handler,
        if maxOccurs is not None and maxOccurs < 1:
            if maxOccurs < 1:
                raise ValueError("maxOccurs must be at least 1")
            if minOccurs is not None and minOccurs < maxOccurs:
                raise ValueError("minOccurs must be at least maxOccurs")
        self.name = name
        self.datatype = datatype
        self.minOccurs = minOccurs
        self.maxOccurs = maxOccurs
        self.handler = handler
        self.attribute = attribute
        self._default = None
        self._finished = False

    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
                self.minOccurs = 0
        self._finished = True

    def adddefault(self, value):
        if self._finished:
            raise ValueError("cannot add default values to finished KeyInfo")
        if self.maxOccurs > 1:
            if self._default is None:
                self._default = [value]
        elif self._default is not None:
            raise ValueError("cannot set more than one default to key with"
                             " maxOccurs == 1")
            self._default = value

    def getdefault(self):
        if not self._finished:
            raise ValueError("cannot get default value of key before KeyInfo"
                             " has been completely initialized")
        if self._default is None and self.maxOccurs > 1:
            return []
            return self._default

    def hasdefault(self):
        return self._default is not None

    def ismulti(self):
        return self.maxOccurs > 1

    def isoptional(self):
        return self.minOccurs == 0

    def issection(self):
        return False

class SectionInfo(KeyInfo):
    def __init__(self, name, datatype, minOccurs, maxOccurs, handler,
                 attribute, keytype, names, nametype):
        assert keytype is not None
        self.keytype = keytype
        self.names = names   # '*', '+', or [name1, ...]
        self.nametype = nametype
        self._children = []  # [(name, info), ...]
        KeyInfo.__init__(self, name, datatype, minOccurs, maxOccurs,
                         handler, attribute)

    def _add_child(self, name, thing):
        for n, info in self._children:
            if name and n == name:
                raise ValueError("child name %s already used" % name)
            if info.attribute and info.attribute == thing.attribute:
                raise ValueError("child attribute name %s already used"
                                 % thing.attribute)
        self._children.append((name, thing))

    def addkey(self, keyinfo):
        self._add_child(keyinfo.name, keyinfo)

    def addsection(self, name, sectinfo):
        assert name not in ("*", "+")
        self._add_child(name, sectinfo)

    def getinfo(self, name):
        if not name:
            return None
        for n, info in self._children:
            if n == name:
                return info
            return None

    def getchildnames(self):
        return [n for (n,info) in self._children]

    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 == "*"
            return name in self.names

=== Added File Packages/ZConfig/matcher.py ===
"""Utility that manages the binding of configuration data to a section."""

import ZConfig

class SectionMatcher:
    def __init__(self, info, name=None):
        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`))
        elif info.allowUnnamed():
            self.name = None
            raise ZConfig.ConfigurationError(
                `info.name` + " sections may not be unnamed")
        self.info = info
        self._values = {}

    def addValue(self, key, value):
        keyinfo = self.info.getinfo(key)
        if keyinfo.issection():
            if keyinfo.name:
                extra = " in %s sections" % `self.info.name`
                extra = ""
            raise ZConfig.ConfigurationError(
                "%s is not a valid key name%s" % (`key`, extra))
        L = self._values.get(key)
        if L is None:
            L = []
        if keyinfo.maxOccurs is None:
        elif len(L) = keyinfo.maxOccurs:
            raise ZConfig.ConfigurationError(
                "too many values for " + `name`)
        self._values[key] = L

    def finish(self):
        """Check the constraints of the section and convert to an application
        values = {}
        for key in self.info.getchildnames():
            ci = self.info.getinfo(key)
            if self._values.has_key(key):
                L = self._values[key]
                if len(L) < ci.minOccurs:
                    raise ZConfig.ConfigurationError(
                        "not enough values for %s; %d found, %d required"
                        % (`key`, len(L), ci.minOccurs))
                L = ci.getdefault()

    def constuct(self):
        keys = self.info.getchildnames()
        values = [None] * len(keys)
        for i in range(len(keys)):
            name = keys[i]
            ci = self.info.getinfo(name)
            keys[i] = ci.attribute
            if self._values.has_key(name):
                L = self._values[key]
            elif ci.ismulti():
                L = ci.getdefault()
                L = [ci.getdefault()]
            if ci.ismulti():
                values[i] = [ci.datatype.convert(s) for s in L]
                assert len(L) == 1
                values[i] = ci.datatype.convert(L[0])
        v = SectionValue(keys, values)
        # 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:
            v = self.info.handler(v)
        return v

class SectionValue:
    """Generic 'bag-of-values' object for a section."""

    def __init__(self, keys, values):
        self._keys = keys
        self._values = values

    def __len__(self):
        return len(self._keys)

    def __getitem__(self, index):
        if isinstance(index, slice):
            raise TypeError("SectionValue does not support slicing")
        return self._values[index]

    def __getattr__(self, name):
            i = self._keys.index(name)
        except ValueError:
            raise AttributeError, name
        return self._values[i]

=== Packages/ZConfig/SchemaParser.py => ===
--- Packages/ZConfig/SchemaParser.py:	Tue Dec 10 11:30:47 2002
+++ Packages/ZConfig/SchemaParser.py	Tue Dec 10 14:17:55 2002
@@ -1,19 +1,18 @@
 """Parser for ZConfig schemas."""
-import types
 import xml.sax
 import ZConfig
-import ZConfig.DataTypes
-from ZConfig.SchemaInfo import SectionInfo, KeyInfo, Unbounded
+from ZConfig import datatypes
+from ZConfig import info
-default_value_type = ZConfig.DataTypes.get("str")
-default_key_type = ZConfig.DataTypes.get("basic-key")
+default_value_type = datatypes.get("str")
+default_key_type = datatypes.get("basic-key")
 default_name_type = default_key_type
-_identifier = ZConfig.DataTypes.get("identifier").convert
+_identifier = datatypes.get("identifier").convert
 class SchemaParser(xml.sax.ContentHandler):
@@ -98,11 +97,11 @@
         if handler:
             handler = self.get_classname(handler)
         if attrs.has_key("keytype"):
-            keytype = ZConfig.DataTypes.get(attrs["keytype"])
+            keytype = datatypes.get(attrs["keytype"])
             keytype = default_key_type
-        self._schema = SectionInfo(None, None, 1, 1, handler, None,
-                                   keytype, (), None)
+        self._schema = info.SectionInfo(None, None, 1, 1, handler, None,
+                                        keytype, (), None)
         self._sections = [self._schema]
         self._stack = [self._schema]
@@ -118,11 +117,11 @@
         if not type:
             self.doSchemaError("section does not specify type")
         if attrs.has_key("keytype"):
-            keytype = ZConfig.DataTypes.get(attrs["keytype"])
+            keytype = datatypes.get(attrs["keytype"])
             keytype = default_key_type
         if attrs.has_key("nametype"):
-            nametype = ZConfig.DataTypes.get(attrs["nametype"])
+            nametype = datatypes.get(attrs["nametype"])
             nametype = default_name_type
         maxOccurs, minOccurs, handler = self.get_common_info(attrs)
@@ -131,8 +130,8 @@
             parent = self._stack[-1]
             parent = self._schema
-        section = SectionInfo(None, None, minOccurs, maxOccurs, handler,
-                              attribute, keytype, names, nametype)
+        section = info.SectionInfo(None, None, minOccurs, maxOccurs, handler,
+                                   attribute, keytype, names, nametype)
         if any:
             parent.addsection(None, section)
@@ -162,15 +161,15 @@
             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 = ZConfig.DataTypes.get(attrs.get("type"))
+        datatype = datatypes.get(attrs.get("type"))
         maxOccurs, minOccurs, handler = self.get_common_info(attrs)
         attribute = attrs.get("attribute")
         if attribute:
             attribute = _identifier(attribute)
             attribute = _identifier(name.replace("-", "_"))
-        key = KeyInfo(name, datatype, minOccurs, maxOccurs, handler,
-                      attribute)
+        key = info.KeyInfo(name, datatype, minOccurs, maxOccurs, handler,
+                           attribute)
@@ -181,7 +180,7 @@
         maxOccurs = attrs.get("maxOccurs")
         if maxOccurs is not None:
             if maxOccurs == "unbounded":
-                maxOccurs = Unbounded
+                maxOccurs = info.Unbounded
                 maxOccurs = int(maxOccurs)

=== Packages/ZConfig/cfgparser.py => ===
--- Packages/ZConfig/cfgparser.py:	Tue Dec 10 13:50:11 2002
+++ Packages/ZConfig/cfgparser.py	Tue Dec 10 14:17:55 2002
@@ -31,11 +31,8 @@
     def parse(self, section):
         done, line = self.nextline()
         while not done:
-            if not line:
-                # blank line
-                pass
-            elif line[0] == "#":
-                # comment
+            if line[:1] in ("", "#"):
+                # blank line or comment
             elif line[:2] == "</":

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

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

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