1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| from functools import wraps
import inspect
import types
def access_wrap(f):
"decorator which checks accessibility of a method by calling the access method of the object"
@wraps(f)
def wrapper(self, *args):
if self.access(get_lexical_call_class()):
return f(self, *args)
else:
raise AttributeError("Scope Error")
return wrapper
class safe_descriptor(object):
"base class for accessibility-constrained descriptors"
def __init__(self, value=None):
self._value = {} # dictionary which maps instances id's to values
self._default = value
self._owner = None # filled-in by the metaclass
@access_wrap
def __get__(self, instance, owner):
val = self._value.get(id(instance), self._default)
if callable(val):
# if val is a function, bind it to the instance
return types.MethodType(val, instance)
else:
return val
@access_wrap
def __set__(self, instance, value):
self._value[id(instance)] = value
@access_wrap
def __delete__(self, instance):
if id(instance) in self._value:
del self._value[id(instance)]
class public(safe_descriptor):
def access(self, caller):
return True
class private(safe_descriptor):
def access(self, caller):
return caller and caller == self._owner
class protected(safe_descriptor):
def access(self, caller):
return caller and issubclass(caller, self._owner)
class SafeMeta(type):
def __new__(cls, name, bases, attrs):
kls = type.__new__(cls, name, bases, attrs)
for v in attrs.values():
if isinstance(v, safe_descriptor):
v._owner = kls
return kls
class safe_object(object):
__metaclass__ = SafeMeta
def get_lexical_call_class(depth=1):
depth += 1
stack = inspect.stack()
if stack and len(stack) > depth and stack[depth]:
frame,_,_,name,_,_ = stack[depth]
cls = get_frame_class(frame)
return cls and get_definition_class(name, cls)
return None
def get_frame_class(obj):
args, _, _, value_dict = inspect.getargvalues(obj)
if len(args) and args[0] == 'self':
instance = value_dict.get('self', None)
if instance:
return getattr(instance, '__class__', None)
return None
def get_definition_class(name, cls):
for c in inspect.getmro(cls):
if name in c.__dict__:
return c
return None
# tests
if __name__ == '__main__':
class MyClass(safe_object):
pub = public()
prot = protected()
priv = private()
def __init__(self):
self.pub = 42
self.prot = 42
self.priv = 42
def assign(self):
self.pub = 666
self.prot = 666
self.priv = 666
@protected
def prot_method(self):
return 42
@private
def priv_method(self):
return 666
@public
def pub_method(self, n):
return n + self.prot_method() + self.priv_method()
class MySubClass(MyClass):
def bad_assign(self):
self.pub = 666
self.prot = 666
self.priv = 666 # erreur
def pub_method(self):
return self.prot_method()
def bad_method(self):
return self.priv_method() #erreur |
Partager