problem with closure arguments and *args in mock object

Peter Otten __peter__ at web.de
Fri Nov 7 19:41:48 CET 2003


John J. Lee wrote:

> I'm trying define a class to act as a Mock "handler" object for
> testing urllib2.OpenerDirector.
> 
> OpenerDirector (actually, my copy of it) does dir(handler.__class__)
> to find out what methods a handler supports.  So, my mock class has to
> have appropriate methods defined on it.  To avoid the hassle of
> manually defining lots of mock classes, I wanted to have a base class
> MockHandler so that I could do
> 
> class MockHandlerSubclass(MockHandler): pass
> # create an instance of subclass, and define methods on the subclass
> h = MockHandlerSubclass(None, ["http_open", "ftp_open", "http_error_302"])
> 
> 
> Now, when the mock object's methods get called, I want to record the
> called method name along with the arguments passed to the method.
> _define_methods (below) tries to define a method that does that.  The
> problem is with this line:
> 
>             def meth(self, name=name, *args):
> 
> 
> I want to make this a closure, with name , so what I really want to
> write is this:
> 
>             def meth(self, *args, name=name):
> 
> 
> But Python's syntax won't let me.
> 
> How can I best work around this?
> 
> 
> class MockHandler:
>     def __init__(self, action, methods):
>         self.action = action
>         self._define_methods(methods)
>         self.argslist = []
>         self.names = []
>     def _define_methods(self, methods):
>         for name in methods:
>             def meth(self, name=name, *args):            # HERE!
>                 apply(self.handle, (self,)+args)
>                 self.names.append(name)
>             setattr(self.__class__, name, meth)
>     def handle(self, fn_name, *args):
>         self.argslist.append(args)
>         if self.action is None: return None
>         elif self.action == "return": return self
>         elif self.action == "error": raise urllib2.URLError()
>     def close(self): pass
>     def add_parent(self, parent): self.parent = parent
> 
> 
> John

Not very elegant, but might still serve the purpose:

class Test(object):
    def __init__(self):
        self.log = []
    def handle(self, args):
        self.log.append(args)
    def define(self, methods):
        def makeMethod(name):
            def method(self, *args):
                self.handle((name,) + args)
            return method
        for name in methods:
            setattr(self.__class__, name, makeMethod(name))

t = Test()
t.define("alpha beta gamma".split())

t.alpha("entteufelter")
t.beta("nu")
t.gamma(1,2,3)

for record in t.log:
    print record

Peter




More information about the Python-list mailing list