[Checkins] SVN: z3c.form/trunk/ Fix deep and unpleasant bug relating to getSpecification. Maybe a little less magic next time? ; -)

Martin Aspeli optilude at gmx.net
Tue Mar 9 09:31:11 EST 2010


Log message for revision 109870:
  Fix deep and unpleasant bug relating to getSpecification. Maybe a little less magic next time? ;-)

Changed:
  U   z3c.form/trunk/CHANGES.txt
  U   z3c.form/trunk/src/z3c/form/util.py
  U   z3c.form/trunk/src/z3c/form/util.txt

-=-
Modified: z3c.form/trunk/CHANGES.txt
===================================================================
--- z3c.form/trunk/CHANGES.txt	2010-03-09 10:32:36 UTC (rev 109869)
+++ z3c.form/trunk/CHANGES.txt	2010-03-09 14:31:11 UTC (rev 109870)
@@ -5,6 +5,15 @@
 2.3.3 (unreleased)
 ------------------
 
+- 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.
 

Modified: z3c.form/trunk/src/z3c/form/util.py
===================================================================
--- z3c.form/trunk/src/z3c/form/util.py	2010-03-09 10:32:36 UTC (rev 109869)
+++ z3c.form/trunk/src/z3c/form/util.py	2010-03-09 14:31:11 UTC (rev 109870)
@@ -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/trunk/src/z3c/form/util.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/util.txt	2010-03-09 10:32:36 UTC (rev 109869)
+++ z3c.form/trunk/src/z3c/form/util.txt	2010-03-09 14:31:11 UTC (rev 109870)
@@ -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