[Checkins] SVN: Products.Five/trunk/ Made the __call__ method of ViewMixinForAttributes have the same signature as the original attribute. This aids some pathological request parameter marshalling. Thanks to Balazs Ree for the patch.

Alec Mitchell apm13 at columbia.edu
Tue Aug 29 10:41:49 EDT 2006


Log message for revision 69839:
  Made the __call__ method of ViewMixinForAttributes have the same signature as the original attribute.  This aids some pathological request parameter marshalling.  Thanks to Balazs Ree for the patch.
  

Changed:
  U   Products.Five/trunk/CHANGES.txt
  U   Products.Five/trunk/browser/metaconfigure.py
  U   Products.Five/trunk/browser/tests/test_defaultview.py

-=-
Modified: Products.Five/trunk/CHANGES.txt
===================================================================
--- Products.Five/trunk/CHANGES.txt	2006-08-29 14:09:42 UTC (rev 69838)
+++ Products.Five/trunk/CHANGES.txt	2006-08-29 14:41:47 UTC (rev 69839)
@@ -5,6 +5,10 @@
 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/trunk/browser/metaconfigure.py
===================================================================
--- Products.Five/trunk/browser/metaconfigure.py	2006-08-29 14:09:42 UTC (rev 69838)
+++ Products.Five/trunk/browser/metaconfigure.py	2006-08-29 14:41:47 UTC (rev 69839)
@@ -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):
 

Modified: Products.Five/trunk/browser/tests/test_defaultview.py
===================================================================
--- Products.Five/trunk/browser/tests/test_defaultview.py	2006-08-29 14:09:42 UTC (rev 69838)
+++ Products.Five/trunk/browser/tests/test_defaultview.py	2006-08-29 14:41:47 UTC (rev 69839)
@@ -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 Checkins mailing list