Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A better python property decorator

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.

like image 628
jtm Avatar asked Mar 14 '26 08:03

jtm


2 Answers

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
like image 160
Thomas Wouters Avatar answered Mar 16 '26 22:03

Thomas Wouters


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.

like image 26
SamB Avatar answered Mar 16 '26 20:03

SamB



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!