[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