views:

514

answers:

2

I've inherited some python code that contains a rather cryptic decorator. This decorator sets properties in classes all over the project. The problem is that this I have traced my debugging problems to this decorator. Seems it "fubars" all debuggers I've tried and trying to speed up the code with psyco breaks everthing. (Seems psyco and this decorator dont play nice). I think it would be best to change it.

def Property(function):
    """Allow readable properties"""
    keys = 'fget', 'fset', 'fdel'
    func_locals = {'doc':function.__doc__}
    def probeFunc(frame, event, arg):
        if event == 'return':
            locals = frame.f_locals
            func_locals.update(dict((k,locals.get(k)) for k in keys))
            sys.settrace(None)
        return probeFunc
    sys.settrace(probeFunc)
    function()
    return property(**func_locals)

Used like so:

class A(object):
    @Property
    def prop():
        def fget(self):
            return self.__prop
        def fset(self, value):
            self.__prop = value
    ... ect

The errors I get say the problems are because of sys.settrace. (Perhaps this is abuse of settrace ?)

My question: Is the same decorator achievable without sys.settrace. If not I'm in for some heavy rewrites.

+2  A: 

It might be possible; it appears that this abuse of settrace is being used to catch the decorated function just before it returns for the purpose of pilfering it's local variables...

Unfortunately, the main approaches I can think of at the moment involve hacking the bytecode :-(.

However, you could replace it with one that requires every so-decorated function to call another special function rather than simply returning, or you could modify it to call sys.gettrace rather than throwing out whatever trace function might be active, and restore the same trace function afterward.

SamB
That's what I was afraid of :)
leChuck
+6  A: 

The same thing? No. You can't do what that decorator does without magic like sys.settrace. (It technically doesn't have to be sys.settrace, but using something else -- like bytecode rewriting -- wouldn't be an improvement.) You can make it a lot simpler by doing, for example:

def Property(f):  
    fget, fset, fdel = f()
    fdoc = f.__doc__
    return property(fget, fset, fdel, fdoc)

class Foo(object):
    @Property
    def myprop():
        "Property docstring"
        def fget(self):  
            return 'fget' 
        def fset(self, x):
            pass
        def fdel(self):
            pass
        return fget, fset, fdel

In Python 2.6 and later you can use a slightly different decorator, though:

def Property(cls):
    fget = cls.__dict__.get('fget')
    fset = cls.__dict__.get('fset')
    fdel = cls.__dict__.get('fdel')
    fdoc = cls.__doc__
    return property(fget, fset, fdel, fdoc)

And you would use it like so:

class Foo(object):
    @Property
    class myprop(object):
        "Property docstring"
        def fget(self):
            return 'fget'
        def fset(self, x):
            pass
        def fdel(self):
            pass

However, a more idiomatic way of doing this in Python 2.6 and later is like so:

class Foo(object):
    @property
    def myprop(self):
        "Property docstring"
        return 'fget'
    @myprop.setter
    def myprop(self, x):
            pass
    @myprop.deleter
    def myprop(self):
            pass
Thomas Wouters
I'm all for doing things the idiomatic python way, but that last example seems less readable than the "voodoo" decorator I've inherited. Maybe I just have to get used to it. (The idiomatic way, that is)
leChuck