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()
"""