[Zope-Checkins] SVN: Products.Five/branches/1.5/ Backport fix for argument signature of synthesized '__call__'.

Tres Seaver tseaver at palladion.com
Wed Oct 4 12:09:30 EDT 2006


Log message for revision 70531:
  Backport fix for argument signature of synthesized '__call__'.

Changed:
  U   Products.Five/branches/1.5/CHANGES.txt
  U   Products.Five/branches/1.5/browser/metaconfigure.py
  A   Products.Five/branches/1.5/browser/tests/classes.py
  U   Products.Five/branches/1.5/browser/tests/test_defaultview.py

-=-
Modified: Products.Five/branches/1.5/CHANGES.txt
===================================================================
--- Products.Five/branches/1.5/CHANGES.txt	2006-10-04 15:18:44 UTC (rev 70530)
+++ Products.Five/branches/1.5/CHANGES.txt	2006-10-04 16:09:30 UTC (rev 70531)
@@ -3,8 +3,12 @@
 ============
 
 Five 1.5.1 (unreleased)
-=====================
+=======================
 
+* Made the __call__ method of ViewMixinForAttributes have the same signature
+  as the original attribute.  This aids some pathological request parameter
+  marshalling.
+
 * Fixed #2168: Missing import
 
 Five 1.5 (2006-08-13)

Modified: Products.Five/branches/1.5/browser/metaconfigure.py
===================================================================
--- Products.Five/branches/1.5/browser/metaconfigure.py	2006-10-04 15:18:44 UTC (rev 70530)
+++ Products.Five/branches/1.5/browser/metaconfigure.py	2006-10-04 16:09:30 UTC (rev 70531)
@@ -386,10 +386,7 @@
     # this is technically not needed because ZPublisher finds our
     # attribute through __browser_default__; but we also want to be
     # able to call pages from python modules, PythonScripts or ZPT
-    def __call__(self, *args, **kw):
-        attr = self.__page_attribute__
-        meth = getattr(self, attr)
-        return meth(*args, **kw)
+    __call__ = property(lambda self: getattr(self, self.__page_attribute__))
 
 class ViewMixinForTemplates(BrowserView):
 

Copied: Products.Five/branches/1.5/browser/tests/classes.py (from rev 70530, Products.Five/trunk/browser/tests/classes.py)

Modified: Products.Five/branches/1.5/browser/tests/test_defaultview.py
===================================================================
--- Products.Five/branches/1.5/browser/tests/test_defaultview.py	2006-10-04 15:18:44 UTC (rev 70530)
+++ Products.Five/branches/1.5/browser/tests/test_defaultview.py	2006-10-04 16:09:30 UTC (rev 70531)
@@ -114,6 +114,98 @@
       >>> tearDown()
     """
 
+def test_default_method_args_marshalling():
+    """\
+    Test the default call method of a view, with respect to possible
+    breakage of argument marshalling from other components
+
+    This is not directly a bug in Five, just a change that enables
+    components have simpler code to imitate ZPublisher's arguments
+    marshalling strategy on default view methods.
+
+    The ZPublisher marshalls arguments to called methods from the
+    request based on the method's signature. This however assumes
+    that it finds the real callable method. However in case of the
+    autogenerated __call__ method of a view, the real method is
+    wrapped. Although the publisher correctly handles this by
+    looking at the __browser_default__ and applying the request on
+    the real method, Plone's portal factory does not do this
+    correctly, thus causing these method calls fail with TypeError,
+    since no parameters will be marshalled to the browser default
+    methods if within the portal factory.
+
+    The applied fix changes the __call__ in such a way that it is
+    not wrapper any more, but yields the original callable instead.
+    This test simply checks that this is so, in other words this is
+    a check that would have failed with the original version.
+
+    First, we load the configuration file:
+
+      >>> import Products.Five.tests
+      >>> from Products.Five import zcml
+      >>> zcml.load_config('meta.zcml', Products.Five)
+      >>> zcml.load_config("permissions.zcml", Products.Five)
+      >>> zcml.load_config('directives.zcml', Products.Five.tests)
+
+    Define a view, with a single attribute and the name of the view
+    is the same as the attribute. Important is that we will use the
+    default browser view.
+
+      >>> zcml.load_string('''
+      ...   <configure xmlns="http://namespaces.zope.org/zope"
+      ...              xmlns:browser="http://namespaces.zope.org/browser">
+      ...        <browser:page
+      ...            for="Products.Five.browser.tests.classes.IOne"
+      ...            class="Products.Five.browser.tests.classes.ViewOne"
+      ...            attribute="my_method"
+      ...            name="my_method"
+      ...            permission="zope2.Public"
+      ...        />
+      ...   </configure>
+      ...   ''')
+
+    Create a context object and a request. Provide parameters on the
+    request.
+
+      >>> from Products.Five.browser.tests.classes import One
+      >>> context = One()
+      >>> from zope.publisher.browser import TestRequest
+      >>> request = TestRequest(form={'arg1': 'A', 'arg2': 'B', 'kw1': 'C'})
+
+    Create the view.
+
+      >>> from zope.component import getMultiAdapter
+      >>> from zope.interface import Interface
+      >>> view = getMultiAdapter((context, request), Interface, 'my_method')
+
+    Check that the __call__ method's signature equals to the real
+    method's signature. They both should yield the four parameters.
+
+      >>> def args(method):
+      ...     f = method.im_func
+      ...     c = f.func_code
+      ...     defaults = f.func_defaults
+      ...     names = c.co_varnames[1:c.co_argcount]
+      ...     return names
+      >>> args(view.my_method)
+      ('arg1', 'arg2', 'kw1', 'kw2')
+      >>> args(view.__call__)
+      ('arg1', 'arg2', 'kw1', 'kw2')
+
+    Finally, call the view's default method. Important is, if this
+    gives a TypeError then the portal factory will fail. This is in
+    effect the same as the previous argument check was.
+
+      >>> from ZPublisher.mapply import mapply
+      >>> mapply(view.__call__, (), request)
+      CALLED A B C D
+
+    Clean up adapter registry and others:
+
+      >>> from zope.testing.cleanup import cleanUp
+      >>> cleanUp()
+    """
+
 def test_suite():
     from Testing.ZopeTestCase import FunctionalDocTestSuite
     return FunctionalDocTestSuite()



More information about the Zope-Checkins mailing list