[Zope3-checkins] CVS: Zope3/src/zope/configuration - fields.py:1.2

Jim Fulton jim@zope.com
Tue, 29 Jul 2003 12:29:06 -0400


Update of /cvs-repository/Zope3/src/zope/configuration
In directory cvs.zope.org:/tmp/cvs-serv16114/src/zope/configuration

Modified Files:
	fields.py 
Log Message:
Added a MessageID field to indicate directive attributes that need to
be translated.

Added a Path field for directive attributes containing path names.

Removed the GlobalObjects field and added a Token field to handle
directive attributes with multiple space-separated values of some
type. Reworked the GlobalObjects example to use Tokens.


=== Zope3/src/zope/configuration/fields.py 1.1 => 1.2 ===
--- Zope3/src/zope/configuration/fields.py:1.1	Mon Jul 28 18:22:39 2003
+++ Zope3/src/zope/configuration/fields.py	Tue Jul 29 12:29:01 2003
@@ -14,6 +14,7 @@
 $Id$
 """
 
+import os
 from zope import schema
 from zope.schema.interfaces import IFromUnicode
 from zope.configuration.exceptions import ConfigurationError
@@ -21,6 +22,41 @@
 
 class GlobalObject(schema.Field):
     """An object that can be accesses as a module global
+
+    Examples:
+
+    First, we need to set up a stub name resolver:
+    >>> d = {'x': 1, 'y': 42, 'z': 'zope'}
+    >>> class fakeresolver(dict):
+    ...     def resolve(self, n):
+    ...         return self[n]
+
+    >>> fake = fakeresolver(d)
+
+
+    >>> g = GlobalObject(value_type=schema.Int())
+    >>> gg = g.bind(fake)
+    >>> gg.fromUnicode("x")
+    1
+    >>> gg.fromUnicode("   x  \\n  ")
+    1
+    >>> gg.fromUnicode("y")
+    42
+    >>> gg.fromUnicode("z")
+    Traceback (most recent call last):
+    ...
+    ValidationError: (u'Wrong type', 'zope', (<type 'int'>, <type 'long'>))
+
+    >>> g = GlobalObject(constraint=lambda x: x%2 == 0)
+    >>> gg = g.bind(fake)
+    >>> gg.fromUnicode("x")
+    Traceback (most recent call last):
+    ...
+    ValidationError: (u'Constraint not satisfied', 1)
+    >>> gg.fromUnicode("y")
+    42
+    >>> 
+
     """
 
     implements(IFromUnicode)
@@ -35,43 +71,6 @@
             self.value_type.validate(value)
 
     def fromUnicode(self, u):
-        """
-
-        Examples:
-
-        First, we need to set up a stub name resolver:
-        >>> d = {'x': 1, 'y': 42, 'z': 'zope'}
-        >>> class fakeresolver(dict):
-        ...     def resolve(self, n):
-        ...         return self[n]
-
-        >>> fake = fakeresolver(d)
-
-
-        >>> g = GlobalObject(value_type=schema.Int())
-        >>> gg = g.bind(fake)
-        >>> gg.fromUnicode("x")
-        1
-        >>> gg.fromUnicode("   x  \\n  ")
-        1
-        >>> gg.fromUnicode("y")
-        42
-        >>> gg.fromUnicode("z")
-        Traceback (most recent call last):
-        ...
-        ValidationError: (u'Wrong type', 'zope', (<type 'int'>, <type 'long'>))
-
-        >>> g = GlobalObject(constraint=lambda x: x%2 == 0)
-        >>> gg = g.bind(fake)
-        >>> gg.fromUnicode("x")
-        Traceback (most recent call last):
-        ...
-        ValidationError: (u'Constraint not satisfied', 1)
-        >>> gg.fromUnicode("y")
-        42
-        >>> 
-
-        """
         name = str(u.strip())
         try:
             value = self.context.resolve(name)
@@ -81,54 +80,129 @@
         self.validate(value)
         return value
 
-class GlobalObjects(schema.Sequence):
-    """A sequence of global objects
+class Tokens(schema.Sequence):
+    """A sequence that can be read from a spece-separated string
+
+    Consider GlobalObject tokens:
+
+    Examples:
+
+    First, we need to set up a stub name resolver:
+
+    >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'}
+    >>> class fakeresolver(dict):
+    ...     def resolve(self, n):
+    ...         return self[n]
+
+    >>> fake = fakeresolver(d)
+
+
+    >>> g = Tokens(value_type=GlobalObject())
+    >>> gg = g.bind(fake)
+    >>> gg.fromUnicode("  \\n  x y z  \\n")
+    [1, 42, 'zope']
+
+    >>> g = Tokens(value_type=
+    ...            GlobalObject(value_type=
+    ...                         schema.Int(constraint=lambda x: x%2 == 0)))
+    >>> gg = g.bind(fake)
+    >>> gg.fromUnicode("x y")
+    Traceback (most recent call last):
+    ...
+    ValidationError: Invalid token: (u'Constraint not satisfied', 1) in x y
+
+    >>> gg.fromUnicode("z y")
+    Traceback (most recent call last):
+    ...
+    ValidationError: Invalid token: (u'Wrong type', 'zope', """ \
+                             """(<type 'int'>, <type 'long'>)) in z y
+    >>> gg.fromUnicode("y y")
+    [42, 42]
+    >>> 
+
     """
 
     implements(IFromUnicode)
 
     def fromUnicode(self, u):
-        """
-        Examples:
-
-        First, we need to set up a stub name resolver:
-        >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'}
-        >>> class fakeresolver(dict):
-        ...     def resolve(self, n):
-        ...         return self[n]
-
-        >>> fake = fakeresolver(d)
-
-
-        >>> g = GlobalObjects()
-        >>> gg = g.bind(fake)
-        >>> gg.fromUnicode("  \\n  x y z  \\n")
-        [1, 42, 'zope']
-
-        >>> g = GlobalObjects(value_type=
-        ...                   schema.Int(constraint=lambda x: x%2 == 0))
-        >>> gg = g.bind(fake)
-        >>> gg.fromUnicode("x y")
-        Traceback (most recent call last):
-        ...
-        ValidationError: (u'Wrong contained type', """ \
-                                    """[Constraint not satisfied 1])
-        >>> gg.fromUnicode("z y")
-        Traceback (most recent call last):
-        ...
-        ValidationError: (u'Wrong contained type', """ \
-                     """[Wrong type zope (<type 'int'>, <type 'long'>)])
-        >>> gg.fromUnicode("y y")
-        [42, 42]
-        >>> 
-
-        """
-        values = [self.context.resolve(name)
-                  for name in str(u.strip()).split()]
+        u = u.strip()
+        if u:
+            vt = self.value_type.bind(self.context)
+            values = []
+            for s in u.split():
+                try:
+                    v = vt.fromUnicode(s)
+                except schema.ValidationError, v:
+                    raise schema.ValidationError("Invalid token: %s in %s"
+                                                 % (v, u))
+                else:
+                    values.append(v)
+        else:
+            values = []
+            
         self.validate(values)
+
         return values
 
+class Path(schema.Text):
+    """A file path name, which may be input as a relative path
+
+    Input paths are converted to absolute paths and normalized.
+
+    Let's look at an example:
+
+    First, we need a "context" for the field that has a path
+    function for converting relative path to an absolute path.
+
+    We'll be careful to do this in an os-independent fashion.
+
+    >>> class FauxContext:
+    ...    def path(self, p):
+    ...       return os.path.join(os.sep, 'faux', 'context', p)
+    
+    >>> context = FauxContext()
+    >>> field = Path().bind(context)
+
+    Lets try an absolute path first:
+
+    >>> p = unicode(os.path.join(os.sep, 'a', 'b'))
+    >>> n = field.fromUnicode(p)
+    >>> n.split(os.sep)
+    [u'', u'a', u'b']
+
+    Now try a relative path:
+
+    >>> p = unicode(os.path.join('a', 'b'))
+    >>> n = field.fromUnicode(p)
+    >>> n.split(os.sep)
+    [u'', u'faux', u'context', u'a', u'b']
+    
+
+    """
+
+    implements(IFromUnicode)
+
+    def fromUnicode(self, u):
+        if os.path.isabs(u):
+            return os.path.normpath(u)
+        
+        return self.context.path(u)
+
 class Bool(schema.Bool):
+    """A boolean value
+
+    Values may be input (in upper or lower case) as any of:
+       yes, no, y, n, true, false, t, or f.
+
+    >>> Bool().fromUnicode(u"yes")
+    1
+    >>> Bool().fromUnicode(u"y")
+    1
+    >>> Bool().fromUnicode(u"true")
+    1
+    >>> Bool().fromUnicode(u"no")
+    0
+    """
 
     implements(IFromUnicode)
 
@@ -139,3 +213,72 @@
         if u in ('0', 'false', 'no', 'f', 'n'):
             return False
         raise schema.ValidationError
+
+class MessageID(schema.Text):
+    """Text string that should be translated.
+
+    When a string is converted to a message ID, it is also
+    recorded in the context.
+
+    >>> class FauxContext:
+    ...     i18n_strings = {} 
+
+    >>> context = FauxContext()
+    >>> field = MessageID().bind(context)
+
+    We can't do anything if we don't have a domain:
+
+    >>> i = field.fromUnicode("Hello world!")
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: You must specify a an i18n translation domain
+
+    With the domain specified:
+
+    >>> context.domain = 'testing'
+
+    We can get a message id:
+
+    >>> i = field.fromUnicode(u"Hello world!")
+    >>> i
+    u'Hello world!'
+    >>> i.domain
+    'testing'
+
+    In addition, the string has been registered with the context:
+
+    >>> context.i18n_strings
+    {'testing': {u'Hello world!': 1}}
+
+    >>> i = field.fromUnicode(u"Foo Bar")
+    >>> i = field.fromUnicode(u"Hello world!")
+    >>> context.i18n_strings
+    {'testing': {u'Foo Bar': 1, u'Hello world!': 1}}
+    """
+
+    implements(IFromUnicode)
+
+    __factories = {}
+
+    def fromUnicode(self, u):
+        domain = getattr(self.context, 'domain', '')
+        if not domain:
+            raise ConfigurationError(
+                "You must specify a an i18n translation domain")
+        v = super(MessageID, self).fromUnicode(u)
+
+        # Record the string we got for the domain
+        i18n_strings = self.context.i18n_strings
+        strings = i18n_strings.get(domain)
+        if strings is None:
+            strings = i18n_strings[domain] = {}
+        strings[v] = 1
+
+        # Convert to a message id, importing the factory, if necessary
+        factory = self.__factories.get(domain)
+        if factory is None:
+            import zope.i18n.messageid
+            factory = zope.i18n.messageid.MessageIDFactory(domain)
+            self.__factories[domain] = factory
+
+        return factory(v)