problem with closure arguments and *args in mock object

J

John J. Lee

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
 
P

Peter Otten

John said:
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
 
J

John J. Lee

Peter Otten said:
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))
[...]

I forgot to say, I'm trying to do this with 1.5.2. Is this possible?
Any ideas for best alternative if not?


John
 
P

Peter Otten

John said:
I forgot to say, I'm trying to do this with 1.5.2. Is this possible?
Any ideas for best alternative if not?

The following will most probably not work as I have zero experience with
Python < 2.x.

class Method:
def __init__(self, instance, name):
self.instance = instance
self.name = name
def __call__(self, *args):
apply(self.instance.handle, ((self.name,) + args,))

class Test:
def __init__(self):
self.log = []
def handle(self, args):
self.log.append(args)
def define(self, methods):
for name in methods:
setattr(self, name, Method(self, name))

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

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

for record in t.log:
print record

The last resort would probably be code generation, but I'm sure someone who
has been around longer than me will come up with a better idea.


Peter
 
J

John J. Lee

Peter Otten said:
The following will most probably not work as I have zero experience with
Python < 2.x.

class Method:
[...]

It does! I had thought of this, but for some reason assumed it
wouldn't work.

Thanks


John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,539
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top