Search code examples
pythonrecursiongetattr

Python recursion on _getattr_ in my class decorator


I write a function (Python 3.x) for use it as a class decorator than make attributes private or public. First of all I wrote my 'private' function:

def private(attrlist):
    def wrapper(obj):
        class fPrivate:
            def __init__(self,*args,**kwargs):
                self.__wrapped = obj(*args,**kwargs)
            def __getattr__(self,args):
                if attrlist(args):
                    raise TypeError('Get on a provate attr')
                else:
                    return getattr(self.__wrapped,args)
            def __setattr__(self,args,val):
                if args == '_fPrivate.__wrapped':
                    self.__dict__[args] = val
                elif attrlist(args):
                    raise TypeError('Set on private')
                else:
                    setattr(self.__wrapped,args,val)
        return fPrivate
    return wrapper

then I use two different methods to declare Private and Pubblic method, like this:

def setPriv(*lists):
    return private(attrlist=(lambda args: args in lists))
def setPub(*lists):
    return private(attrlist=(lambda args: args not in lists))

at this point I test my work:

@setPriv('name')
class t1:
    def __init__(self,name,age):
        self.name = name
        self.age = age

But when I create my first instance

a = t1('Bob',40)

I got this error:

> File "C:\Code\utils\private.py", line 11, in __getattr__
getattr(self.__wrapped,args)
File "C:\Code\utils\private.py", line 11, in __getattr__
getattr(self.__wrapped,args)
File "C:\Code\utils\private.py", line 11, in __getattr__
getattr(self.__wrapped,args)
File "C:\Code\utils\private.py", line 11, in __getattr__
getattr(self.__wrapped,args)
File "C:\Code\utils\private.py", line 8, in __getattr__
if attrlist(args):
File "C:\Code\utils\private.py", line 25, in <lambda>
return private(attrlist=(lambda args: args in lists))
RecursionError: maximum recursion depth exceeded in comparison

Thanks in advance


Solution

  • After the name of an attribute has been mangled, the rules for naming in Python are still obeyed, so attribute __wrapped will read _fPrivate__wrapped not _fPrivate.__wrapped; without dot(s).

    If you check your traceback, you'll see the error is raised in the __init__ when you first try to set self.__wrapped, which is turn calls __getattribute__ and then __getattr__, and since __getattr__ again contains an attribute access viz. self.__wrapped, it goes on and on.

    Removing that dot ensures, _fPrivate__wrapped makes it to the instance dictionary and setting the attribute calls __getattribute__ which returns the attribute cleanly and has no need to search deeper.

    a = t1('Bob',40)
    
    a.age = 7
    print(a.age)
    # 7
    a.name = 'me'
    Traceback (most recent call last):
      File "python", line 38, in <module>
      File "python", line 15, in __setattr__
    TypeError: Set on private