[Zope3-checkins] CVS: Zope3/src/zope/schema - vocabulary.py:1.2 interfaces.py:1.16

Fred L. Drake, Jr. fred@zope.com
Tue, 20 May 2003 12:11:00 -0400


Update of /cvs-repository/Zope3/src/zope/schema
In directory cvs.zope.org:/tmp/cvs-serv24680/src/zope/schema

Modified Files:
	interfaces.py 
Added Files:
	vocabulary.py 
Log Message:
Merge schema-vocabulary-branch into the trunk.

Preliminary documentation on vocabularies and schema vocabulary fields
can be found at http://dev.zope.org/Zope3/VocabularyFields.


=== Zope3/src/zope/schema/vocabulary.py 1.1 => 1.2 ===
--- /dev/null	Tue May 20 12:11:00 2003
+++ Zope3/src/zope/schema/vocabulary.py	Tue May 20 12:10:30 2003
@@ -0,0 +1,142 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""Vocabulary support for schema."""
+
+from zope.schema import Field
+from zope.schema import errornames
+from zope.schema.interfaces import ValidationError
+from zope.schema.interfaces import IAbstractVocabulary, IVocabularyRegistry
+from zope.schema.interfaces import IVocabularyField, IVocabularyMultiField
+
+try:
+    basestring  # new in Python 2.3
+except NameError:
+    from types import StringTypes as basestring
+
+
+class VocabularyField(Field):
+    """Field that adds support for use of an external vocabulary.
+
+    The value is a single value from the vocabulary.
+    """
+    __implements__ = IVocabularyField
+
+    def __init__(self, vocabulary=None, **kw):
+        # set up the vocabulary:
+        if isinstance(vocabulary, basestring):
+            self.vocabulary = None
+            self.vocabularyName = vocabulary
+        else:
+            self.vocabulary = vocabulary
+            self.vocabularyName = None
+        # call the base initializer
+        super(VocabularyField, self).__init__(**kw)
+
+    def _validate(self, value):
+        if self.vocabulary is None:
+            if self.context is not None:
+                raise ValueError("can't validate value without vocabulary")
+            # XXX can't validate without vocabulary, and can't get
+            # vocabulary without context
+            return
+        if value not in self.vocabulary:
+            raise ValidationError(errornames.ConstraintNotSatisfied,
+                                  value)
+
+    def bind(self, object):
+        clone = super(VocabularyField, self).bind(object)
+        # get registered vocabulary/presentation if needed:
+        if clone.vocabulary is None:
+            vr = getVocabularyRegistry()
+            clone.vocabulary = vr.get(object, self.vocabularyName)
+        return clone
+
+
+class VocabularyMultiField(VocabularyField):
+    """Field that adds support for use of an external vocabulary.
+
+    The value is a collection of values from the vocabulary.
+    """
+    __implements__ = IVocabularyMultiField
+
+    def _validate(self, value):
+        vocab = self.vocabulary
+        if vocab is None:
+            raise ValueError("can't validate value without vocabulary")
+        for v in value:
+            if v not in vocab:
+                raise ValidationError(errornames.ConstraintNotSatisfied, v)
+
+
+class VocabularyRegistryError(LookupError):
+    def __init__(self, name):
+        self.name = name
+        Exception.__init__(self, str(self))
+
+    def __str__(self):
+        return "unknown vocabulary: %r" % self.name
+
+
+class VocabularyRegistry(object):
+    __slots__ = '_map',
+    __implements__ = IVocabularyRegistry
+
+    def __init__(self):
+        self._map = {}
+
+    def get(self, object, name):
+        try:
+            vtype = self._map[name]
+        except KeyError:
+            raise VocabularyRegistryError(name)
+        return vtype(object)
+
+    def register(self, name, factory):
+        self._map[name] = factory
+
+
+_vocabularies = None
+
+def getVocabularyRegistry():
+    """Return the vocabulary registry.
+
+    If the registry has not been created yet, an instance of
+    VocabularyRegistry will be installed and used.
+    """
+    if _vocabularies is None:
+        setVocabularyRegistry(VocabularyRegistry())
+    return _vocabularies
+
+def setVocabularyRegistry(registry):
+    """Set the vocabulary registry."""
+    global _vocabularies
+    if _vocabularies is not None:
+        raise ValueError("vocabulary registry has already been set")
+    _vocabularies = registry
+
+def _clear():
+    """Remove the registries (for use by tests)."""
+    global _vocabularies
+    _vocabularies = None
+
+
+try:
+    from zope.testing.cleanup import addCleanUp
+except ImportError:
+    # don't have that part of Zope
+    pass
+else:
+    addCleanUp(_clear)
+    del addCleanUp


=== Zope3/src/zope/schema/interfaces.py 1.15 => 1.16 ===
--- Zope3/src/zope/schema/interfaces.py:1.15	Fri Apr 25 17:18:31 2003
+++ Zope3/src/zope/schema/interfaces.py	Tue May 20 12:10:30 2003
@@ -15,7 +15,7 @@
 
 $Id$
 """
-from zope.interface import Interface
+from zope.interface import Interface, Attribute
 from zope.i18n import MessageIDFactory
 
 _ = MessageIDFactory("zope")
@@ -396,3 +396,122 @@
         constraint=_fields,
         required=False,
         )
+
+
+class IAbstractVocabulary(Interface):
+    """Representation of a vocabulary.
+
+    At this most basic level, a vocabulary only need to support a test
+    for containment.  This can be implemented either by __contains__()
+    or by sequence __getitem__() (the later only being useful for
+    vocabularies which are intrinsically ordered).
+    """
+
+    def getQuery():
+        """Return an IVocabularyQuery object for this vocabulary.
+
+        Vocabularies which do not support query must return None.
+        """
+
+    def getTerm(value):
+        """Return the ITerm object for the term 'value'.
+
+        If 'value' is not a valid term, this method raises LookupError.
+        """
+
+class IVocabularyQuery(Interface):
+    """Query object for a vocabulary.
+
+    This is a marker interface for query objects; specific
+    implementations must derive a more specific interface and
+    implement that.  Widget views should be registered for the
+    specific interface.
+    """
+
+    vocabulary = Attribute("vocabulary",
+                           """The source vocabulary for this query object.
+
+                           This needs to be available for use by the
+                           query view.
+                           """)
+
+
+class ITerm(Interface):
+    """Object representing a single value in a vocabulary."""
+
+    value = Attribute(
+        "value", "The value used to represent vocabulary term in a field.")
+
+
+class IIterableVocabulary(Interface):
+    """Vocabulary which supports iteration over allowed values.
+
+    The objects iteration provides must conform to the ITerm
+    interface.
+    """
+
+    def __iter__():
+        """Return an iterator which provides the terms from the vocabulary."""
+
+    def __len__():
+        """Return the number of valid terms, or sys.maxint."""
+
+
+class ISubsetVocabulary(Interface):
+    """Vocabulary which represents a subset of another vocabulary."""
+
+    def getMasterVocabulary():
+        """Returns the vocabulary that this is a subset of."""
+
+
+class IVocabulary(IIterableVocabulary, IAbstractVocabulary):
+    """Vocabulary which is iterable."""
+
+
+class IVocabularyFieldMixin(Interface):
+    # Mix-in interface that defines interesting things common to all
+    # vocabulary fields.
+
+    vocabularyName = TextLine(
+        title=u"Vocabulary Name",
+        description=(u"The name of the vocabulary to be used.  This name\n"
+                     u"is intended to be used by the IVocabularyRegistry's\n"
+                     u"get() method."),
+        required=False,
+        default=None)
+
+    vocabulary = Attribute(
+        "vocabulary",
+        ("IAbstractVocabulary to be used, or None.\n"
+         "\n"
+         "If None, the vocabularyName should be used by an\n"
+         "IVocabularyRegistry should be used to locate an appropriate\n"
+         "IAbstractVocabulary object."))
+
+
+class IVocabularyField(IVocabularyFieldMixin, IField):
+    """Field with a vocabulary-supported value.
+
+    The value for fields of this type is a single value from the
+    vocabulary.
+    """
+
+
+class IVocabularyMultiField(IVocabularyFieldMixin, IField):
+    """Field with a value containing selections from a vocabulary..
+
+    The value for fields of this type need to support at least
+    containment checks using 'in' and iteration.
+    """
+
+
+class IVocabularyRegistry(Interface):
+    """Registry that provides IAbstractVocabulary objects for specific fields.
+    """
+
+    def get(object, name):
+        """Return the vocabulary named 'name' for the content object
+        'object'.
+
+        When the vocabulary cannot be found, LookupError is raised.
+        """