[Zope3-checkins] SVN: Zope3/trunk/src/zope/formlib/ Fixed a critical issue in the formlib. Adaptation of the context to the

Stephan Richter srichter at cosmos.phy.tufts.edu
Wed Aug 23 17:21:05 EDT 2006


Log message for revision 69738:
  Fixed a critical issue in the formlib. Adaptation of the context to the 
  field interface did not happen as expected. See my mail on zope3-dev as 
  well.
  
  I think this could also be ported to the 3.3 branch, but I am not sure. 
  Anyone?
  
  

Changed:
  U   Zope3/trunk/src/zope/formlib/form.py
  U   Zope3/trunk/src/zope/formlib/form.txt
  U   Zope3/trunk/src/zope/formlib/tests.py

-=-
Modified: Zope3/trunk/src/zope/formlib/form.py
===================================================================
--- Zope3/trunk/src/zope/formlib/form.py	2006-08-23 20:38:14 UTC (rev 69737)
+++ Zope3/trunk/src/zope/formlib/form.py	2006-08-23 21:21:05 UTC (rev 69738)
@@ -56,7 +56,7 @@
 
     def __init__(self, field, name=None, prefix='',
                  for_display=None, for_input=None, custom_widget=None,
-                 render_context=False, get_rendered=None,
+                 render_context=False, get_rendered=None, interface=None
                  ):
         self.field = field
         if name is None:
@@ -66,6 +66,9 @@
             name = prefix + '.' + name
         self.__name__ = name
         self.prefix = prefix
+        if interface is None:
+            interface = field.interface
+        self.interface = interface
         self.for_display = for_display
         self.for_input = for_input
         self.custom_widget = custom_widget
@@ -88,33 +91,34 @@
         for arg in args:
             if isinstance(arg, InterfaceClass):
                 for name, field in schema.getFieldsInOrder(arg):
-                    fields.append((name, field))
+                    fields.append((name, field, arg))
             elif IField.providedBy(arg):
                 name = arg.__name__
                 if not name:
                         raise ValueError(
                             "Field has no name")
 
-                fields.append((name, arg))
+                fields.append((name, arg, arg.interface))
             elif isinstance(arg, FormFields):
                 for form_field in arg:
-                    fields.append((form_field.__name__, form_field))
+                    fields.append(
+                        (form_field.__name__, form_field, form_field.interface))
             elif isinstance(arg, FormField):
-                fields.append((arg.__name__, arg))
+                fields.append((arg.__name__, arg, arg.interface))
             else:
                 raise TypeError("Unrecognized argument type", arg)
 
 
         seq = []
         byname = {}
-        for name, field in fields:
+        for name, field, iface in fields:
             if isinstance(field, FormField):
                 form_field = field
             else:
                 if field.readonly:
                     if omit_readonly and (name not in keep_readonly):
                         continue
-                form_field = FormField(field, **defaults)
+                form_field = FormField(field, interface=iface, **defaults)
                 name = form_field.__name__
 
             if name in byname:
@@ -224,7 +228,7 @@
                 adapters = {}
 
             # Adapt context, if necessary
-            interface = field.interface
+            interface = form_field.interface
             adapter = adapters.get(interface)
             if adapter is None:
                 if interface is None:
@@ -347,7 +351,7 @@
     for form_field in form_fields:
         field = form_field.field
         # Adapt context, if necessary
-        interface = field.interface
+        interface = form_field.interface
         adapter = adapters.get(interface)
         if adapter is None:
             if interface is None:
@@ -463,7 +467,7 @@
     # First, collect the data for the various schemas
     schema_data = {}
     for form_field in form_fields:
-        schema = form_field.field.interface
+        schema = form_field.interface
         if schema is None:
             continue
 
@@ -493,7 +497,7 @@
     for form_field in form_fields:
         field = form_field.field
         # Adapt context, if necessary
-        interface = field.interface
+        interface = form_field.interface
         adapter = adapters.get(interface)
         if adapter is None:
             if interface is None:
@@ -663,7 +667,7 @@
     for action in actions:
         if action.submitted():
             errors = action.validate(data)
-            if errors is None:
+            if errors is None and default_validate is not None:
                 errors = default_validate(action, data)
             return errors, action
 

Modified: Zope3/trunk/src/zope/formlib/form.txt
===================================================================
--- Zope3/trunk/src/zope/formlib/form.txt	2006-08-23 20:38:14 UTC (rev 69737)
+++ Zope3/trunk/src/zope/formlib/form.txt	2006-08-23 21:21:05 UTC (rev 69738)
@@ -1620,3 +1620,69 @@
 Therefore, a simple division such as the following should suffice.
 
 # TODO
+
+Additional Cases
+================
+
+
+Automatic Context Adaptation
+----------------------------
+
+As you may know already, the formlib will automatically adapt the context to
+find a widget and data for a particular field. In an early version of
+``zope.formlib``, it simply used ``field.interface`` to get the interface to
+adapt to. Unfortunately, this call returns the interface the field has been
+defined in and not the interface you got the field from. The following lines
+demonstrate the correct behavior:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IFoo(zope.interface.Interface):
+  ...     title = zope.schema.TextLine()
+
+  >>> class IFooBar(IFoo):
+  ...     pass
+
+Here is the unexpected behavior that caused formlib to do the wrong thing:
+
+  >>> IFooBar['title'].interface
+  <InterfaceClass __builtin__.IFoo>
+
+Note: If this behavior ever changes, the formlib can be simplified again.
+
+  >>> class FooBar(object):
+  ...     zope.interface.implements(IFooBar)
+  ...     title = u'initial'
+  >>> foobar = FooBar()
+
+  >>> class Blah(object):
+  ...     def __conform__(self, iface):
+  ...         if iface is IFooBar:
+  ...             return foobar
+  >>> blah = Blah()
+
+Let's now generate the form fields and instantiate the widgets:
+
+  >>> from zope.formlib import form
+
+  >>> form_fields = form.FormFields(IFooBar)
+
+  >>> request = TestRequest()
+  >>> widgets = form.setUpEditWidgets(form_fields, 'form', blah, request)
+  >>> print widgets.get('title')()
+  <input class="textType" id="form.title" name="form.title"
+         size="20" type="text" value="initial" />
+
+Here are some more places where the behavior was incorrect:
+
+  >>> widgets = form.setUpWidgets(form_fields, 'form', blah, request)
+  >>> print widgets.get('title')()
+  <input class="textType" id="form.title" name="form.title"
+         size="20" type="text" value="" />
+
+  >>> form.checkInvariants(form_fields, {'title': 'new'})
+  []
+
+  >>> form.applyChanges(blah, form_fields, {'title': 'new'})
+  True

Modified: Zope3/trunk/src/zope/formlib/tests.py
===================================================================
--- Zope3/trunk/src/zope/formlib/tests.py	2006-08-23 20:38:14 UTC (rev 69737)
+++ Zope3/trunk/src/zope/formlib/tests.py	2006-08-23 21:21:05 UTC (rev 69738)
@@ -546,6 +546,7 @@
         doctest.DocFileSuite(
             'form.txt',
             setUp=formSetUp, tearDown=zope.component.testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
             ),
         doctest.DocTestSuite(
             setUp=formSetUp, tearDown=zope.component.testing.tearDown,



More information about the Zope3-Checkins mailing list