File: private.py

"""
Privacy for attributes fetched from class instances.
See self-test code at end of file for a usage example.
Decorator same as: Doubler = Private('data', 'size')(Doubler).
Private returns onDecorator, onDecorator returns onInstance,
and each onInstance instance embeds a Doubler instance.
"""

traceMe = False
def trace(*args):
    if traceMe: print '[' + ' '.join(map(str, args)) + ']'

def Private(*privates):                          # privates in enclosing scope
    def onDecorator(aClass):                     # aClass in enclosing scope
        class onInstance:                        # wrapped in instance attribute
            def __init__(self, *args, **kargs):
                self.wrapped = aClass(*args, **kargs)
            def __getattr__(self, attr):         # my attrs don't call getattr
                trace('get:', attr)              # others assumed in wrapped
                if attr in privates:
                    raise TypeError, 'private attribute fetch: ' + attr
                else:
                    return getattr(self.wrapped, attr)
            def __setattr__(self, attr, value):             # outside accesses
                trace('set:', attr, value)                  # others via getattr
                if attr == 'wrapped':                       # allow my attrs
                    self.__dict__[attr] = value             # avoid looping!
                elif attr in privates:
                    raise TypeError, 'private attribute change: ' + attr
                else:
                    setattr(self.wrapped, attr, value)      # wrapped obj attrs
        return onInstance                                   # or use __dict__
    return onDecorator


if __name__ == '__main__':
    traceMe = True
    
    @Private('data', 'size') 
    class Doubler:
        def __init__(self, label, start):
            self.label = label                   # access inside the subject class
            self.data  = start                   # not intercepted: run normally
        def size(self):
            return len(self.data)                # methods run with no checking
        def double(self):                        # because privacy not inherited
            for i in range(self.size()):
                self.data[i] = self.data[i] * 2
        def display(self):
            print self.label, '=>', self.data

    X = Doubler('X is', [1, 2, 3])
    Y = Doubler('Y is', [-10, -20, -30])

    # the followng all succeed
    print X.label                                # accesses outside the subject class
    X.display(); X.double(); X.display()         # intercepted:  validated, delegated 
    print Y.label
    Y.display(); Y.double()
    Y.label = 'Spam =>'
    Y.display()

    # the following all fail properly
    """
    print X.size()
    print X.data
    X.data = [1, 1, 1]
    X.size = lambda S: 0
    print Y.data
    print Y.size()
    """



[Home page] Books Code Blog Python Author Train Find ©M.Lutz