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.)
parent 8ad3a49b
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
############################################################################## ##############################################################################
"""Provide an apply-like facility that works with any mapping object """Provide an apply-like facility that works with any mapping object
""" """
import zope.publisher.publish
def default_call_object(object, args, context): def default_call_object(object, args, context):
result=object(*args) # Type s<cr> to step into published object. result=object(*args) # Type s<cr> to step into published object.
...@@ -39,27 +40,15 @@ def mapply(object, positional=(), keyword={}, ...@@ -39,27 +40,15 @@ def mapply(object, positional=(), keyword={},
if hasattr(object,'__bases__'): if hasattr(object,'__bases__'):
f, names, defaults = handle_class(object, context) f, names, defaults = handle_class(object, context)
else: else:
f=object try:
im=0 f, count = zope.publisher.publish.unwrapMethod(object)
if hasattr(f, 'im_func'): except TypeError:
im=1 if maybe:
elif not hasattr(f,'func_defaults'): return object
if hasattr(f, '__call__'): raise
f=f.__call__ code = f.func_code
if hasattr(f, 'im_func'): defaults = f.func_defaults
im=1 names = code.co_varnames[count:code.co_argcount]
elif not hasattr(f,'func_defaults') and maybe: return object
elif maybe: return object
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) nargs=len(names)
if positional: if positional:
......
##############################################################################
#
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment