Log message for revision 78481:
  Make mapply() support objects of the form
  
    class Foo:
        __call__ = SomeCallableObject()
  
  This is very useful for Zope 3 templates where this paradigm is used
  all over the place. In fact, mapply simply uses this part of Zope 3's
  __call__ finding logic. (I actually wish we could simply switch over to
  Zope 3's mapply, but the call signatures aren't the same. Sigh.)
  

Changed:
  U   Zope/branches/philikon-aq/lib/python/ZPublisher/mapply.py
  A   Zope/branches/philikon-aq/lib/python/ZPublisher/tests/test_mapply.py

-=-
Modified: Zope/branches/philikon-aq/lib/python/ZPublisher/mapply.py
===================================================================
--- Zope/branches/philikon-aq/lib/python/ZPublisher/mapply.py   2007-07-30 
19:36:16 UTC (rev 78480)
+++ Zope/branches/philikon-aq/lib/python/ZPublisher/mapply.py   2007-07-30 
20:59:22 UTC (rev 78481)
@@ -12,6 +12,7 @@
 ##############################################################################
 """Provide an apply-like facility that works with any mapping object
 """
+import zope.publisher.publish
 
 def default_call_object(object, args, context):
     result=object(*args) # Type s<cr> to step into published object.
@@ -39,28 +40,16 @@
     if hasattr(object,'__bases__'):
         f, names, defaults = handle_class(object, context)
     else:
-        f=object
-        im=0
-        if hasattr(f, 'im_func'):
-            im=1
-        elif not hasattr(f,'func_defaults'):
-            if hasattr(f, '__call__'):
-                f=f.__call__
-                if hasattr(f, 'im_func'):
-                    im=1
-                elif not hasattr(f,'func_defaults') and maybe: return object
-            elif maybe: return object
+        try:
+            f, count = zope.publisher.publish.unwrapMethod(object)
+        except TypeError:
+            if maybe:
+                return object
+            raise
+        code = f.func_code
+        defaults = f.func_defaults
+        names = code.co_varnames[count:code.co_argcount]
 
-        if im:
-            f=f.im_func
-            c=f.func_code
-            defaults=f.func_defaults
-            names=c.co_varnames[1:c.co_argcount]
-        else:
-            defaults=f.func_defaults
-            c=f.func_code
-            names=c.co_varnames[:c.co_argcount]
-
     nargs=len(names)
     if positional:
         positional=list(positional)

Added: Zope/branches/philikon-aq/lib/python/ZPublisher/tests/test_mapply.py
===================================================================
--- Zope/branches/philikon-aq/lib/python/ZPublisher/tests/test_mapply.py        
                        (rev 0)
+++ Zope/branches/philikon-aq/lib/python/ZPublisher/tests/test_mapply.py        
2007-07-30 20:59:22 UTC (rev 78481)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test mapply() function
+"""
+import unittest
+import ExtensionClass
+import Acquisition
+from ZPublisher.mapply import mapply
+
+class MapplyTests(unittest.TestCase):
+
+    def testMethod(self):
+        def compute(a,b,c=4):
+            return '%d%d%d' % (a, b, c)
+        values = {'a':2, 'b':3, 'c':5}
+        v = mapply(compute, (), values)
+        self.failUnlessEqual(v, '235')
+
+        v = mapply(compute, (7,), values)
+        self.failUnlessEqual(v, '735')
+
+    def testClass(self):
+        values = {'a':2, 'b':3, 'c':5}
+        class c(object):
+            a = 3
+            def __call__(self, b, c=4):
+                return '%d%d%d' % (self.a, b, c)
+            compute = __call__
+        cc = c()
+        v = mapply(cc, (), values)
+        self.failUnlessEqual(v, '335')
+
+        del values['c']
+        v = mapply(cc.compute, (), values)
+        self.failUnlessEqual(v, '334')
+
+        class c2:
+            """Must be a classic class."""
+            
+        c2inst = c2()
+        c2inst.__call__ = cc
+        v = mapply(c2inst, (), values)
+        self.failUnlessEqual(v, '334')
+
+    def testObjectWithCall(self):
+        # Make sure that the __call__ of an object can also be another
+        # callable object.  mapply will do the right thing and
+        # recursive look for __call__ attributes until it finds an
+        # actual method:
+
+        class CallableObject:
+            def __call__(self, a, b):
+                return '%s%s' % (a, b)
+
+        class Container:
+            __call__ = CallableObject()
+
+        v = mapply(Container(), (8, 3), {})
+        self.assertEqual(v, '83')
+
+    def testUncallableObject(self):
+        # Normally, mapply will raise a TypeError if it encounters an
+        # uncallable object (e.g. an interger ;))
+        self.assertRaises(TypeError, mapply, 2, (), {})
+
+        # Unless you enable the 'maybe' flag, in which case it will
+        # only maybe call the object
+        self.assertEqual(mapply(2, (), {}, maybe=True), 2)
+
+    def testNoCallButAcquisition(self):
+        # Make sure that mapply won't erroneously walk up the
+        # Acquisition chain when looking for __call__ attributes:
+
+        class Root(ExtensionClass.Base):
+            def __call__(self):
+                return 'The root __call__'
+
+        class NoCallButAcquisition(Acquisition.Implicit):
+            pass
+
+        ob = NoCallButAcquisition().__of__(Root())
+        self.assertRaises(TypeError, mapply, ob, (), {})
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(MapplyTests))
+    return suite


Property changes on: 
Zope/branches/philikon-aq/lib/python/ZPublisher/tests/test_mapply.py
___________________________________________________________________
Name: svn:eol-style
   + native

_______________________________________________
Zope-Checkins maillist  -  Zope-Checkins@zope.org
http://mail.zope.org/mailman/listinfo/zope-checkins

Reply via email to