[Zope3-checkins] SVN: Zope3/trunk/src/zope/schema/ - added context-sensitive sources with tests

Benji York benji at zope.com
Fri Sep 2 15:25:40 EDT 2005


Log message for revision 38246:
  - added context-sensitive sources with tests
  - also added a test for non-context-sensitive sources
  

Changed:
  U   Zope3/trunk/src/zope/schema/_field.py
  U   Zope3/trunk/src/zope/schema/interfaces.py
  A   Zope3/trunk/src/zope/schema/sources.txt
  U   Zope3/trunk/src/zope/schema/tests/test_docs.py

-=-
Modified: Zope3/trunk/src/zope/schema/_field.py
===================================================================
--- Zope3/trunk/src/zope/schema/_field.py	2005-09-02 14:01:19 UTC (rev 38245)
+++ Zope3/trunk/src/zope/schema/_field.py	2005-09-02 19:25:40 UTC (rev 38246)
@@ -34,6 +34,7 @@
 from zope.schema.interfaces import IPassword, IObject, IDate, ITimedelta
 from zope.schema.interfaces import IURI, IId, IFromUnicode
 from zope.schema.interfaces import ISource, IBaseVocabulary
+from zope.schema.interfaces import IContextSourceBinder
 
 from zope.schema.interfaces import ValidationError, InvalidValue
 from zope.schema.interfaces import WrongType, WrongContainedType, NotUnique
@@ -196,7 +197,6 @@
             assert source is None, (
                 "You cannot specify both source and vocabulary.")
         elif source is not None:
-            assert ISource.providedBy(source)
             vocabulary = source
 
         assert not (values is None and vocabulary is None), (
@@ -211,7 +211,8 @@
         elif isinstance(vocabulary, (unicode, str)):
             self.vocabularyName = vocabulary
         else:
-            assert ISource.providedBy(vocabulary)
+            assert (ISource.providedBy(vocabulary) or 
+                    IContextSourceBinder.providedBy(vocabulary))
             self.vocabulary = vocabulary
         # Before a default value is checked, it is validated. However, a
         # named vocabulary is usually not complete when these fields are
@@ -228,9 +229,14 @@
         """See zope.schema._bootstrapinterfaces.IField."""
         clone = super(Choice, self).bind(object)
         # get registered vocabulary if needed:
-        if clone.vocabulary is None and self.vocabularyName is not None:
+        if IContextSourceBinder.providedBy(self.vocabulary):
+            clone.vocabulary = self.vocabulary(object)
+            assert ISource.providedBy(clone.vocabulary)
+        elif clone.vocabulary is None and self.vocabularyName is not None:
             vr = getVocabularyRegistry()
             clone.vocabulary = vr.get(object, self.vocabularyName)
+            assert ISource.providedBy(clone.vocabulary)
+
         return clone
 
     def fromUnicode(self, str):

Modified: Zope3/trunk/src/zope/schema/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/schema/interfaces.py	2005-09-02 14:01:19 UTC (rev 38245)
+++ Zope3/trunk/src/zope/schema/interfaces.py	2005-09-02 19:25:40 UTC (rev 38246)
@@ -498,6 +498,11 @@
           searcing for items.
 
         """
+
+class IContextSourceBinder(Interface):
+    def __call__(context):
+        """Return a context-bound instance that implements ISource.
+        """
     
 
 # TODO, define iterable sources.  For now, we'll just use vocabularies.

Added: Zope3/trunk/src/zope/schema/sources.txt
===================================================================
--- Zope3/trunk/src/zope/schema/sources.txt	2005-09-02 14:01:19 UTC (rev 38245)
+++ Zope3/trunk/src/zope/schema/sources.txt	2005-09-02 19:25:40 UTC (rev 38246)
@@ -0,0 +1,49 @@
+=================
+Sources in Fields
+=================
+
+A choice field can be constructed with a source or source name.  When a source
+is used, it will be used as the source for valid values.
+
+Create a source for all odd numbers.
+
+    >>> from zope import interface
+    >>> from zope.schema.interfaces import ISource, IContextSourceBinder
+    >>> class MySource(object):
+    ...     interface.implements(ISource)
+    ...     divisor = 2
+    ...     def __contains__(self, value):
+    ...         return bool(value % self.divisor)
+    >>> my_source = MySource()
+    >>> 1 in my_source
+    True
+    >>> 2 in my_source
+    False
+
+    >>> from zope.schema import Choice
+    >>> choice = Choice(__name__='number', source=my_source)
+    >>> bound = choice.bind(object())
+    >>> bound.vocabulary
+    <...MySource...>
+
+If a IContextSourceBinder is passed as the `source` argument to Choice, it's
+`bind` method will be called with the context as its only argument.   The
+result must implement ISource and will be used as the source.
+
+    >>> def my_binder(context):
+    ...     print "Binder was called."
+    ...     source = MySource()
+    ...     source.divisor = context.divisor
+    ...     return source
+    >>> interface.directlyProvides(my_binder, IContextSourceBinder)
+
+    >>> class Context(object):
+    ...     divisor = 3
+
+    >>> choice = Choice(__name__='number', source=my_binder)
+    >>> bound = choice.bind(Context())
+    Binder was called.
+    >>> bound.vocabulary
+    <...MySource...>
+    >>> bound.vocabulary.divisor
+    3


Property changes on: Zope3/trunk/src/zope/schema/sources.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/schema/tests/test_docs.py
===================================================================
--- Zope3/trunk/src/zope/schema/tests/test_docs.py	2005-09-02 14:01:19 UTC (rev 38245)
+++ Zope3/trunk/src/zope/schema/tests/test_docs.py	2005-09-02 19:25:40 UTC (rev 38246)
@@ -20,6 +20,7 @@
 
 def test_suite():
     return unittest.TestSuite((
+        doctest.DocFileSuite('../sources.txt', optionflags=doctest.ELLIPSIS),
         doctest.DocFileSuite('../README.txt'),
         ))
 



More information about the Zope3-Checkins mailing list