[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)