[Zope3-checkins] SVN: Zope3/trunk/ Fixed bug 451, http://www.zope.org/Collectors/Zope3-dev/451. Invoke widget factories adequately. Deprecate CustomSequenceWidgetFactory because it does not satisfy IViewFactory.

Dominik Huber dominik.huber at perse.ch
Mon Nov 21 15:59:02 EST 2005


Log message for revision 40306:
  Fixed bug 451, http://www.zope.org/Collectors/Zope3-dev/451. Invoke widget factories adequately. Deprecate CustomSequenceWidgetFactory because it does not satisfy IViewFactory.

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  U   Zope3/trunk/src/zope/app/form/__init__.py
  U   Zope3/trunk/src/zope/app/form/browser/metaconfigure.py
  U   Zope3/trunk/src/zope/app/form/browser/objectwidget.py
  U   Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
  U   Zope3/trunk/src/zope/app/form/tests/test_widget.py

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2005-11-21 19:08:48 UTC (rev 40305)
+++ Zope3/trunk/doc/CHANGES.txt	2005-11-21 20:59:01 UTC (rev 40306)
@@ -154,6 +154,10 @@
 
     Bug Fixes
 
+      - Fixed bug 451, http://www.zope.org/Collectors/Zope3-dev/451
+        Invoke widget factories adequately. Deprecate 
+        CustomSequenceWidgetFactory because it does not satisfy IViewFactory.
+
       - zope.tales.expressions.simpleTraverse would raise NameError if the
         object at hand did not implement __getitem__. Changed to raise
         AttributeError instead. AttributeError is included in Undefs whereas
@@ -211,7 +215,7 @@
       Stephan Richter, Roger Ineichen, Marius Gedminas, Julien Anguenot, Benji
       York, Gary Poster, Jim Fulton, Michael Kerrin, Torsten Kurbad,
       Philipp von Weitershausen, Tarek Ziadé, Andreas Jung, Dmitry Vasiliev,
-      Juergen Kartnaller, Stefan Holek
+      Juergen Kartnaller, Stefan Holek, Dominik Huber
 
     Note: If you are not listed and contributed, please add yourself. This
           note will be deleted before the release.

Modified: Zope3/trunk/src/zope/app/form/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/form/__init__.py	2005-11-21 19:08:48 UTC (rev 40305)
+++ Zope3/trunk/src/zope/app/form/__init__.py	2005-11-21 20:59:01 UTC (rev 40306)
@@ -19,9 +19,15 @@
 
 from zope.app.form.interfaces import IWidget, InputErrors
 from zope.component.interfaces import IViewFactory
+from zope.deprecation import deprecated
 from zope.interface import implements
 from zope.i18n import translate
+from zope.schema.interfaces import IChoice, ICollection
 
+deprecated('CustomSequenceWidgetFactory',
+           'Use CustomWidgetFactory instead. '
+           'The reference will be gone in Zope 3.3.')
+
 class Widget(object):
     """Mixin class providing functionality common across widget types."""
 
@@ -62,6 +68,7 @@
     def setRenderedValue(self, value):
         self._data = value
 
+
 class InputWidget(Widget):
     """Mixin class providing some default input widget methods."""
 
@@ -81,6 +88,7 @@
         else:
             return False
 
+
 class CustomWidgetFactory(object):
     """Custom Widget Factory."""
     implements(IViewFactory)
@@ -90,25 +98,34 @@
         self.args = args
         self.kw = kw
 
-    def __call__(self, context, request):
-        args = (context, request) + self.args
+    def _create(self, args):
         instance = self._widget_factory(*args)
         for name, value in self.kw.items():
             setattr(instance, name, value)
         return instance
 
-class CustomSequenceWidgetFactory(object):
-    """Custom Widget Factory."""
-    implements(IViewFactory)
+    def __call__(self, context, request):
+        # Sequence widget factory
+        if ICollection.providedBy(context):
+            args = (context, context.value_type, request) + self.args
 
-    def __init__(self, widget_factory, *args, **kw):
-        self._widget_factory = widget_factory
-        self.args = args
-        self.kw = kw
+        # Vocabulary widget factory
+        elif IChoice.providedBy(context):
+            args = (context, context.vocabulary, request) + self.args
 
+        # Regular widget factory
+        else:
+            args = (context, request) + self.args
+
+        return self._create(args)
+
+
+# BBB: Gone in 3.3 (does not satify IViewFactory)
+class CustomSequenceWidgetFactory(CustomWidgetFactory):
+    """Custom sequence widget factory."""
+
     def __call__(self, context, field, request):
-        args = (context, field, request) + self.args
-        instance = self._widget_factory(*args)
-        for name, value in self.kw.items():
-            setattr(instance, name, value)
-        return instance
+        if not ICollection.providedBy(context):
+            raise TypeError, "Provided field does not provide ICollection."
+
+        return super(CustomSequenceWidgetFactory, self).__call__(context, request)

Modified: Zope3/trunk/src/zope/app/form/browser/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/metaconfigure.py	2005-11-21 19:08:48 UTC (rev 40305)
+++ Zope3/trunk/src/zope/app/form/browser/metaconfigure.py	2005-11-21 20:59:01 UTC (rev 40306)
@@ -22,6 +22,7 @@
 import zope.component
 from zope.security.checker import CheckerPublic
 from zope.interface import implementedBy
+from zope.component.interfaces import IViewFactory
 from zope.configuration.exceptions import ConfigurationError
 
 from zope.schema import getFieldNamesInOrder
@@ -84,8 +85,15 @@
             # attribute.  This can be used to override some of the
             # presentational attributes of the widget implementation.
             class_ = self._default_widget_factory
-        self._widgets[field+'_widget'] = CustomWidgetFactory(class_, **attrs)
+        
+        # don't wrap a factory into a factory
+        if IViewFactory.providedBy(class_):
+            factory = class_
+        else:
+            factory = CustomWidgetFactory(class_, **attrs)
 
+        self._widgets[field+'_widget'] = factory
+
     def _processWidgets(self):
         if self._widgets:
             customWidgetsObject = type('CustomWidgetsMixin', (object,),

Modified: Zope3/trunk/src/zope/app/form/browser/objectwidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/objectwidget.py	2005-11-21 19:08:48 UTC (rev 40305)
+++ Zope3/trunk/src/zope/app/form/browser/objectwidget.py	2005-11-21 20:59:01 UTC (rev 40306)
@@ -20,6 +20,7 @@
 from zope.interface import implements
 from zope.schema import getFieldNamesInOrder
 
+from zope.app import zapi
 from zope.app.form.interfaces import IInputWidget
 from zope.app.form import InputWidget
 from zope.app.form.browser.widget import BrowserWidget
@@ -105,6 +106,18 @@
             result.append(getSubwidget(name).hidden())
         return "".join(result)
 
+    def error(self):
+        if self._error:
+            errormessages = []
+            keys = self._error.keys(); keys.sort()
+            for key in keys:
+                errormessages.append(str(key) + ': ')
+                errormessages.append( zapi.getMultiAdapter((self._error[key], self.request),
+                                        IWidgetInputErrorView).snippet())
+                errormessages.append(str(key) + ', ')
+            return ''.join(errormessages[0:-1])
+        return ""
+
     def getInputValue(self):
         """Return converted and validated widget data.
 
@@ -114,11 +127,26 @@
         object (note that the default EditView calls `applyChanges`, which
         does this).
         """
+
+        errors = []
         content = self.factory()
         for name in self.names:
-            setattr(content, name, self.getSubWidget(name).getInputValue())
+            try:
+                setattr(content, name, self.getSubWidget(name).getInputValue())
+            except Exception, e:
+                errors.append(e)
+                if self._error is None:
+                    self._error = {}
+                
+                if name not in self._error:
+                    self._error[name] = e
+
+        if errors:
+            raise errors[0]
+
         return content
 
+
     def applyChanges(self, content):
         field = self.context
 

Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py	2005-11-21 19:08:48 UTC (rev 40305)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py	2005-11-21 20:59:01 UTC (rev 40306)
@@ -32,7 +32,6 @@
 from zope.app.form.interfaces import IDisplayWidget
 from zope.app.form.interfaces import IInputWidget, MissingInputError
 from zope.app.form import CustomWidgetFactory
-from zope.app.form import CustomSequenceWidgetFactory
 
 from zope.app.form.browser.tests.support import VerifyResults
 from zope.app.form.browser.tests.test_browserwidget import BrowserWidgetTest
@@ -98,16 +97,16 @@
         request = TestRequest()
 
         # set up the custom widget factory and verify that it works
-        sw = CustomSequenceWidgetFactory(ListSequenceWidget)
-        widget = sw(self.field, TextLine(), request)
+        sw = CustomWidgetFactory(ListSequenceWidget)
+        widget = sw(self.field, request)
         assert widget.subwidget is None
         assert widget.context.value_type is value_type
 
         # set up a variant that specifies the subwidget to use and verify it
         class PollOption(object) : pass
         ow = CustomWidgetFactory(ObjectWidget, PollOption)
-        sw = CustomSequenceWidgetFactory(ListSequenceWidget, subwidget=ow)
-        widget = sw(self.field, TextLine(), request)
+        sw = CustomWidgetFactory(ListSequenceWidget, subwidget=ow)
+        widget = sw(self.field, request)
         assert widget.subwidget is ow
         assert widget.context.value_type is value_type
 

Modified: Zope3/trunk/src/zope/app/form/tests/test_widget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/tests/test_widget.py	2005-11-21 19:08:48 UTC (rev 40305)
+++ Zope3/trunk/src/zope/app/form/tests/test_widget.py	2005-11-21 20:59:01 UTC (rev 40306)
@@ -24,7 +24,7 @@
 from zope.schema import Text
 
 from zope.app.form import Widget
-from zope.app.form import CustomWidgetFactory, CustomSequenceWidgetFactory
+from zope.app.form import CustomWidgetFactory
 from zope.app.form.interfaces import IWidget
 from zope.app.testing.placelesssetup import setUp, tearDown
 
@@ -139,16 +139,69 @@
     """Tests the custom widget factory.
 
     Custom widgets can be created using a custom widget factory. Factories
-    are used to assign attribute values to widgets they create:
+    are used to assign attribute values to widgets they create.
 
+    The custom widget factory can be used for three widget types:
+
+        -   Regular widgets
+        -   Sequence widgets
+        -   Vocabulary widgets
+
+    Test regular widget:
+
         >>> factory = CustomWidgetFactory(FooWidget, bar='baz')
         >>> widget = factory(context, request)
         >>> isinstance(widget, FooWidget)
         True
         >>> widget.bar
         'baz'
+
+    Test sequence widget:
+
+        >>> from zope.schema import TextLine, List
+        >>> from zope.app.form.browser import ListSequenceWidget
+        >>> value_type = TextLine(__name__=u'bar')
+        >>> field = List( __name__=u'foo', value_type=value_type )
+
+        >>> factory = CustomWidgetFactory(ListSequenceWidget, 
+        ...     subwidget=CustomWidgetFactory(FooWidget, bar='baz'))
+
+        >>> widget = factory(field, request)
+        >>> widget.context.value_type is value_type
+        True
+        >>> isinstance(widget, ListSequenceWidget)
+        True
+
+        >>> isinstance(widget.subwidget, CustomWidgetFactory)
+        True
+        >>> subwidget = widget.subwidget(context, request)
+        >>> isinstance(subwidget, FooWidget)
+        True
+        >>> subwidget.bar
+        'baz'
+
+    Test vocabulary widget:
+
+        >>> from zope.schema import Choice
+        >>> from zope.app.form.browser import RadioWidget
+        >>> field = Choice( __name__=u'foo', values=['1', '2', '3'] )
+        >>> bound = field.bind(context)
+
+        >>> factory = CustomWidgetFactory(RadioWidget, 
+        ...      orientation = 'vertical')
+
+        >>> widget = factory(bound, request)
+        >>> [term.value for term in widget.context.vocabulary]
+        ['1', '2', '3']
+
+        >>> isinstance(widget, RadioWidget)
+        True
+        >>> widget.orientation
+        'vertical'
+
     """
 
+# BBB: Gone in 3.3 (does not satify IViewFactory)
 class TestCustomSequenceWidgetFactory(object):
     """Tests the custom sequence widget factory.
 
@@ -158,10 +211,17 @@
 
         >>> from zope.schema import TextLine, List
         >>> from zope.app.form.browser import ListSequenceWidget
+        
+        
         >>> value_type = TextLine(__name__=u'bar')
         >>> field = List( __name__=u'foo', value_type=value_type )
 
         >>> ow = CustomWidgetFactory(FooWidget, bar='baz')
+        
+        >>> import zope.deprecation
+        >>> zope.deprecation.__show__.off()
+        >>> from zope.app.form import CustomSequenceWidgetFactory
+        >>> zope.deprecation.__show__.on()
         >>> sw = CustomSequenceWidgetFactory(ListSequenceWidget, subwidget=ow)
         >>> widget = sw(field, TextLine(), request)
         >>> isinstance(widget, ListSequenceWidget)



More information about the Zope3-Checkins mailing list