views:

160

answers:

3

I'm developing a documentation testing framework -- basically unit tests for PDFs. Tests are (decorated) methods of instances of classes defined by the framework, and these are located and instantiated at runtime and the methods are invoked to execute the tests.

My goal is to cut down on the amount of quirky Python syntax that the people who will write tests need to be concerned about, as these people may or may not be Python programmers, or even very much programmers at all. So I would like them to be able to write "def foo():" instead of "def foo(self):" for methods, but still be able to use "self" to access members.

In an ordinary program I would consider this a horrible idea, but in a domain-specific-languagey kind of program like this one, it seems worth a try.

I have successfully eliminated the self from the method signature by using a decorator (actually, since I am using a decorator already for the test cases, I would just roll it into that), but "self" does not then refer to anything in the test case method.

I have considered using a global for self, and even come up with an implementation that more or less works, but I'd rather pollute the smallest namespace possible, which is why I would prefer to inject the variable directly into the test case method's local namespace. Any thoughts?

+2  A: 

This might be a use case for decorators - you give them a small set of lego bricks to build functions with, and the complicated framework stuff is piped in via @testcase or somesuch.

Edit: You didn't post any code, so this is going to be sketchy, but they don't need to write methods. They can write ordinary functions without "self", and you could use decorators like in this example from the article I linked:

class myDecorator(object):

    def __init__(self, f):
        print "inside myDecorator.__init__()"
        f() # Prove that function definition has completed

    def __call__(self):
        print "inside myDecorator.__call__()"

@myDecorator
def aFunction():
    print "inside aFunction()"
chryss
Yes, I already do use decorators to mark methods as test cases, because I need to know 1) which methods to run and 2) what order to run them in. The bulk of the framework is working; I just want to get rid of that pesky "self."
kindall
Edited, gave example how to use decorators without the need to use `self` in the function.
chryss
but self needs to refer to something within the defined functions. this doesn't accomplish that
aaronasterling
That works great for eliminating the self in the method signature, and I came up with something similar (using a function-style decorator, though) but of course there is no access to self inside the function. What I'd like is to still be able to access the instance in the method _without_ passing it in explicitly. I will edit the question to make this clearer.
kindall
I fully see that this doesn't solve the entire problem, but without seeing how the OP constructed the framework, and what exactly the users are supposed to supply, it's hard to be more specific. MAybe looking into `@contextmanager` and something along the lines of this: http://code.activestate.com/recipes/534150/ would be a good idea, too -- again a different approach.
chryss
Thanks for thinking outside the box; I will have a look and see if I can figure out a way to use a context manager for my needs.
kindall
+3  A: 

The trick is to add 'self' to f.func_globals. This works in python2.6. I really should get around to installing other versions to test stuff like this on. Sorry for the wall of code but I cover two cases: doing it with a metaclass and doing it with a decorator. For your usecase, I think the metaclass is better since the whole point of this exercise is to shield users from syntax.

import new, functools

class TestMeta(type):
    def __new__(meta, classname, bases, classdict):
        for item in classdict:
            if hasattr(classdict[item], '__call__'):
                classdict[item] = wrap(classdict[item])
        return type.__new__(meta, classname, bases, classdict)

def wrap(f):
    @functools.wraps(f)
    def wrapper(self):
        f.func_globals['self'] = self        
        return f()
    return wrapper

def testdec(f):
    @functools.wraps(f)
    def wrapper():
        return f()
    return wrapper

class Test(object):
    __metaclass__ = TestMeta
    message = 'You can do anything in python'
    def test():
        print self.message

    @testdec
    def test2():
        print self.message + ' but the wrapper funcion can\'t take a self argument either or you get a TypeError'

class Test2(object):
    message = 'It also works as a decorator but (to me at least) feels better as a metaclass'
    @wrap
    def test():
        print self.message


t = Test()
t2 = Test2()
t.test()
t.test2()
t2.test()
aaronasterling
Thanks, that looks like pretty much exactly what I needed to know!
kindall
Nice... or rather it makes my head hurt. In a good way.
chryss
+3  A: 

little upgrade for aaronasterling's solution( i haven't enough reputation to comment it ):

def wrap(f):
    @functools.wraps(f)
    def wrapper(self,*arg,**kw):
        f.func_globals['self'] = self        
        return f(*arg,**kw)
    return wrapper

but both this solutions will work unpredictable if f function will be called recursively for different instance, so you have to clone it like this:

import types
class wrap(object):
    def __init__(self,func):
        self.func = func
    def __get__(self,obj,type):
        new_globals = self.func.func_globals.copy()
        new_globals['self'] = obj
        return types.FunctionType(self.func.func_code,new_globals)
class C(object):
    def __init__(self,word):
        self.greeting = word
    @wrap
    def greet(name):
        print(self.greeting+' , ' + name+ '!')
C('Hello').greet('kindall')
Odomontois
Nice embellishment, thanks. Too bad you can only mark one best answer.
kindall