[Checkins] SVN: z3c.form/branches/fieldsandcontentproviders/ merged trunk from r108949 until r109920

Godefroid Chapelle gotcha at bubblenet.be
Fri Mar 12 06:06:02 EST 2010


Log message for revision 109921:
  merged trunk from r108949 until r109920
  

Changed:
  _U  z3c.form/branches/fieldsandcontentproviders/
  U   z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt
  U   z3c.form/branches/fieldsandcontentproviders/CHANGES.txt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml
  A   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml
  A   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py
  U   z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt

-=-

Property changes on: z3c.form/branches/fieldsandcontentproviders
___________________________________________________________________
Modified: svn:ignore
   - coverage
develop-eggs
eggs
parts
.installed.cfg
build
dist
bin
docs

   + coverage
develop-eggs
eggs
parts
.installed.cfg
build
dist
bin
docs
.project
.pydevproject


Modified: z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -18,4 +18,5 @@
 Martijn Faassen
 Michael Howitz
 Michael Kerrin
-Paul Carduner
\ No newline at end of file
+Paul Carduner
+Martin Aspeli
\ No newline at end of file

Modified: z3c.form/branches/fieldsandcontentproviders/CHANGES.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/CHANGES.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/CHANGES.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -5,9 +5,32 @@
 2.3.3 (unreleased)
 ------------------
 
+- The last discriminator of the 'message' IValue adapter used in the
+  ErrorViewSnippet is called 'content', but it was looked up as the error view
+  itself. It is now looked up on the form's context.
+
+- Don't let util.getSpecification() generate an interface more than once.
+  This causes strange effects when used in value adapters: if two adapters
+  use e.g. ISchema['some_field'] as a "discriminator" for 'field', with one
+  adapter being more specific on a discriminator that comes later in the
+  discriminator list (e.g. 'form' for an ErrorViewMessage), then depending on
+  the order in which these two were set up, the adapter specialisation may
+  differ, giving unexpected results that make it look like the adapter 
+  registry is picking the wrong adapter.
+
+- Fix trivial test failures on Python 2.4 stemming from differences in
+  pprint's sorting of dicts.
+
+- Don't invoke render() when publishing the form as a view if the HTTP status
+  code has been set to one in the 3xx range (e.g. a redirect or not-modified
+  response) - the response body will be ignored by the browser anyway.
+
+- Handle Invalid exceptions from constraints and field validators.
+
 - Don't create unnecessary self.items in update() method of
   SelectWidget in DISPLAY_MODE. Now items is a property.
 
+- Add hidden widget templates for radio buttons and checkboxes.
 
 2.3.2 (2010-01-21)
 ------------------

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -110,7 +110,27 @@
   </span>
   <input name="widget.name-empty-marker" type="hidden" value="1" />
 
+Check HIDDEN_MODE:
 
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'checkbox_hidden.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='hidden')
+
+  >>> widget.value = 'true'
+  >>> widget.mode = interfaces.HIDDEN_MODE
+  >>> print widget.render()
+  <span class="option">
+    <input type="hidden" id="widget-id-0" name="widget.name:list"
+           class="checkbox-widget" value="true" />
+  </span><span class="option">
+    <input type="hidden" id="widget-id-1" name="widget.name:list"
+           class="checkbox-widget" value="false" />
+  </span>
+
+
 Single Checkbox Widget
 ----------------------
 
@@ -198,3 +218,21 @@
   <input name="widget.name-empty-marker" type="hidden"
          value="1" />
 
+Check HIDDEN_MODE:
+
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'checkbox_hidden.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='hidden')
+
+  >>> widget.value = 'true'
+  >>> widget.mode = interfaces.HIDDEN_MODE
+  >>> print widget.render()
+  <span class="option">
+    <input type="hidden" id="widget-id-0"
+           name="widget.name:list"
+           class="single-checkbox-widget" value="selected" />
+  </span>
+

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml	2010-03-12 11:06:00 UTC (rev 109921)
@@ -35,4 +35,11 @@
       template="checkbox_input.pt"
       />
 
+  <z3c:widgetTemplate
+      mode="hidden"
+      widget="z3c.form.interfaces.ICheckBoxWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="checkbox_hidden.pt"
+      />
+      
 </configure>

Copied: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt (from rev 109920, z3c.form/trunk/src/z3c/form/browser/checkbox_hidden.pt)
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt	                        (rev 0)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal"
+     tal:omit-tag="">
+<span class="option"
+      tal:repeat="item view/items">     
+  <input id="" name="" value="" class="hidden-widget" title=""
+         tabindex="" accesskey=""
+         type="hidden"
+         tal:attributes="id item/id;
+                         name item/name;
+                         class view/klass;
+                         value item/value;
+                         style view/style;
+                         title view/title;
+                         lang view/lang;
+                         onclick view/onclick;
+                         ondblclick view/ondblclick;
+                         onmousedown view/onmousedown;
+                         onmouseup view/onmouseup;
+                         onmouseover view/onmouseover;
+                         onmousemove view/onmousemove;
+                         onmouseout view/onmouseout;
+                         onkeypress view/onkeypress;
+                         onkeydown view/onkeydown;
+                         onkeyup view/onkeyup;
+                         disabled view/disabled;
+                         tabindex view/tabindex;
+                         onfocus view/onfocus;
+                         onblur view/onblur;
+                         onchange view/onchange;
+                         readonly view/readonly;
+                         alt view/alt;
+                         accesskey view/accesskey;
+                         onselect view/onselect" />         
+</span>                       
+</html>

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -105,3 +105,18 @@
     </label>
   </span>
   <input name="widget.name-empty-marker" type="hidden" value="1" />
+
+Check HIDDEN_MODE:
+
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'radio_hidden.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='hidden')
+
+  >>> widget.value = 'true'
+  >>> widget.mode = interfaces.HIDDEN_MODE
+  >>> print widget.render()
+  <input id="widget-id" name="widget.name" value="true" class="hidden-widget" type="hidden" />
+  
\ No newline at end of file

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml	2010-03-12 11:06:00 UTC (rev 109921)
@@ -30,4 +30,11 @@
       template="radio_input.pt"
       />
 
+  <z3c:widgetTemplate
+      mode="hidden"
+      widget="z3c.form.interfaces.IRadioWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="radio_hidden.pt"
+      />
+
 </configure>

Copied: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt (from rev 109920, z3c.form/trunk/src/z3c/form/browser/radio_hidden.pt)
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt	                        (rev 0)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -0,0 +1,13 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal"
+     tal:omit-tag="">
+<input id="" name="" value="" class="hidden-widget" title=""
+       tabindex="" accesskey=""
+       type="hidden"
+       tal:attributes="id view/id;
+                       name view/name;
+                       title view/title;
+                       tabindex view/tabindex;
+                       accesskey view/accesskey;
+                       value view/value" />
+</div>

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py	2010-03-12 11:06:00 UTC (rev 109921)
@@ -68,7 +68,7 @@
     def update(self):
         value = zope.component.queryMultiAdapter(
             (self.context, self.request, self.widget,
-             self.field, self.form, self),
+             self.field, self.form, self.content),
             interfaces.IValue, name='message')
         if value is not None:
             self.message = value.get()

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py	2010-03-12 11:06:00 UTC (rev 109921)
@@ -280,7 +280,7 @@
                 self.hasRequiredFields = True
             uniqueOrderedKeys.append(shortName)
             if newWidget:
-            #    self._data_values.append(widget)
+                self._data_values.append(widget)
                 self._data[shortName] = widget
                 zope.location.locate(widget, self, shortName)
             # allways ensure that we add all keys and keep the order given from
@@ -307,7 +307,7 @@
                      getattr(widget, 'field', None),
                      widget),
                     interfaces.IValidator).validate(value)
-            except (zope.schema.ValidationError,
+            except (zope.interface.Invalid,
                     ValueError, MultipleErrors), error:
                 view = zope.component.getMultiAdapter(
                     (error, self.request, widget, widget.field,

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -330,7 +330,12 @@
 
   >>> class LastNameTooShort(zope.schema.interfaces.ValidationError):
   ...     """The last name is too short."""
-
+  
+  >>> def lastNameConstraint(value):
+  ...     if value and value == value.lower():
+  ...         raise zope.interface.Invalid(u"Name must have at least one capital letter")
+  ...     return True
+  
   >>> class IPerson(zope.interface.Interface):
   ...     id = zope.schema.TextLine(
   ...         title=u'ID',
@@ -342,7 +347,8 @@
   ...         title=u'Last Name',
   ...         description=u"The person's last name.",
   ...         default=u'',
-  ...         required=True)
+  ...         required=True,
+  ...         constraint=lastNameConstraint)
   ...
   ...     firstName = zope.schema.TextLine(
   ...         title=u'First Name',
@@ -698,15 +704,20 @@
   >>> manager.ignoreContext = True
   >>> manager.update()
 
-  >>> from pprint import pprint
-  >>> pprint(manager.extract())
-  ({'firstName': u'Stephan', 'lastName': u'Richter'}, ())
+  >>> data, errors = manager.extract()
+  >>> data['firstName']
+  u'Stephan'
+  >>> data['lastName']
+  u'Richter'
+  >>> errors
+  ()
 
 Since all errors are immediately converted to error view snippets, we have to
 provide the adapter from a validation error to an error view snippet first:
 
   >>> from z3c.form import error
   >>> zope.component.provideAdapter(error.ErrorViewSnippet)
+  >>> zope.component.provideAdapter(error.InvalidErrorViewSnippet)
 
 Let's now cause a widget-level error by not submitting the required last
 name:
@@ -719,6 +730,24 @@
   >>> manager.extract()
   ({'firstName': u'Stephan'}, (<ErrorViewSnippet for RequiredMissing>,))
 
+Or, we could violate a constraint. This constraint raises Invalid, which is
+a convenient way to raise errors where we mainly care about providing a custom
+error message.
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan', 
+  ...     'form.widgets.lastName': u'richter',
+  ...     'form.widgets.id': u'srichter'})
+  >>> manager = field.FieldWidgets(personForm, request, context)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> extracted = manager.extract()
+  >>> extracted
+  ({'firstName': u'Stephan'}, (<InvalidErrorViewSnippet for Invalid>,))
+  
+  >>> extracted[1][0].createMessage()
+  u'Name must have at least one capital letter'
+
 Finally, let's ensure that invariant failures are also caught:
 
   >>> request = TestRequest(form={

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py	2010-03-12 11:06:00 UTC (rev 109921)
@@ -211,6 +211,11 @@
 
     def __call__(self):
         self.update()
+        
+        # Don't render anything if we are doing a redirect
+        if self.request.response.getStatus() in (300, 301, 302, 303, 304, 305, 307,):
+            return u''
+        
         return self.render()
 
 

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -341,7 +341,62 @@
     </body>
   </html>
 
+The update()/render() cycle is what happens when the form is called, i.e.
+when it is published:
 
+  >>> print addForm()
+  <html xmlns="http://www.w3.org/1999/xhtml">
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-id">ID</label>
+          <input type="text" id="form-widgets-id"
+                 name="form.widgets.id"
+                 class="text-widget required textline-field"
+                 value="" />
+        </div>
+        <div class="row">
+          <label for="form-widgets-name">Name</label>
+          <input type="text" id="form-widgets-name" name="form.widgets.name"
+                 class="text-widget required textline-field"
+                 value="" />
+        </div>
+        <div class="row">
+          <label for="form-widgets-gender">Gender</label>
+          <select id="form-widgets-gender" name="form.widgets.gender:list"
+                  class="select-widget choice-field" size="1">
+            <option id="form-widgets-gender-novalue"
+                    value="--NOVALUE--">no value</option>
+            <option id="form-widgets-gender-0" value="male">male</option>
+            <option id="form-widgets-gender-1" value="female">female</option>
+          </select>
+          <input name="form.widgets.gender-empty-marker" type="hidden"
+                 value="1" />
+        </div>
+        <div class="row">
+          <label for="form-widgets-age">Age</label>
+          <input type="text" id="form-widgets-age" name="form.widgets.age"
+                 class="text-widget int-field" value="20" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form-buttons-add" name="form.buttons.add"
+                 class="submit-widget button-field" value="Add" />
+        </div>
+      </form>
+    </body>
+  </html>  
+
+Note that we don't actually call render if the response has been set to a 3xx
+type status code (e.g. a redirect or not modified response), since the browser
+would not render it anyway:
+
+  >>> request.response.setStatus(304)
+  >>> print addForm()
+  
+Let's go back to a normal status to continue the test.
+
+  >>> request.response.setStatus(200)
+
 Submitting an add form successfully
 -----------------------------------
 
@@ -1064,12 +1119,16 @@
   >>> editForm = PersonDictEditForm(None, request)
   >>> editForm.update()
 
-  >>> from pprint import pprint
-  >>> pprint(personDict)
-  {'age': 5,
-   'gender': 'male',
-   'id': u'rineichen',
-   'name': u'Jesse Ineichen'}
+  >>> len(personDict)
+  4
+  >>> personDict['age']
+  5
+  >>> personDict['gender']
+  'male'
+  >>> personDict['id']
+  u'rineichen'
+  >>> personDict['name']
+  u'Jesse Ineichen'
 
 
 Creating a Display Form

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py	2010-03-12 11:06:00 UTC (rev 109921)
@@ -56,13 +56,24 @@
         (spec is not None and
          not zope.interface.interfaces.ISpecification.providedBy(spec)
          and not isinstance(spec, classTypes)) ):
-        # Step 1: Create an interface
-        iface = zope.interface.interface.InterfaceClass(
-            'IGeneratedForObject_%i' %hash(spec))
-        # Step 2: Directly-provide the interface on the specification
-        zope.interface.alsoProvides(spec, iface)
-        # Step 3: Make the new interface the specification for this instance
-        spec = iface
+        
+        # Step 1: Calculate an interface name
+        ifaceName = 'IGeneratedForObject_%i' %hash(spec)
+        
+        # Step 2: Find out if we already have such an interface
+        existingInterfaces = [
+                i for i in zope.interface.directlyProvidedBy(spec)
+                    if i.__name__ == ifaceName
+            ]
+        
+        # Step 3a: Return an existing interface if there is one
+        if len(existingInterfaces) > 0:
+            spec = existingInterfaces[0]
+        # Step 3b: Create a new interface if not
+        else:
+            iface = zope.interface.interface.InterfaceClass(ifaceName)
+            zope.interface.alsoProvides(spec, iface)
+            spec = iface
     return spec
 
 

Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt	2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt	2010-03-12 11:06:00 UTC (rev 109921)
@@ -396,3 +396,50 @@
   True
 
 That's all.
+
+`getSpecification()` function
+-----------------------------
+
+This function is capable of returning an `ISpecification` for any object,
+including instances.
+
+For an interface, it simply returns the interface:
+
+  >>> import zope.interface
+  >>> class IFoo(zope.interface.Interface):
+  ...     pass
+  
+  >>> util.getSpecification(IFoo) == IFoo
+  True
+  
+Ditto for a class:
+  
+  >>> class Bar(object):
+  ...     pass
+  
+  >>> util.getSpecification(Bar) == Bar
+  True
+
+For an instance, it will create a marker interface on the fly if necessary:
+
+  >>> bar = Bar()
+  >>> util.getSpecification(bar) # doctest: +ELLIPSIS
+  <InterfaceClass z3c.form.util.IGeneratedForObject_...>
+
+The ellipsis represents a hash of the object.
+
+If the function is called twice on the same object, it will not create a new
+marker each time:
+
+  >>> baz = Bar()
+  >>> barMarker = util.getSpecification(bar)
+  >>> bazMarker1 = util.getSpecification(baz)
+  >>> bazMarker2 = util.getSpecification(baz)
+
+  >>> barMarker is bazMarker1
+  False
+
+  >>> bazMarker1 == bazMarker2
+  True  
+  >>> bazMarker1 is bazMarker2
+  True



More information about the checkins mailing list