Created
September 3, 2011 16:54
-
-
Save herczy/1191456 to your computer and use it in GitHub Desktop.
Want to have protected and private? :-)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| import types | |
| class AccessError(Exception): | |
| '''Object access violation''' | |
| def get_caller_instance(skip = 2): | |
| try: | |
| raise RuntimeError | |
| except RuntimeError: | |
| etype, evalue, trace = sys.exc_info() | |
| if not trace: | |
| return | |
| myframe = trace.tb_frame | |
| try: | |
| while skip and myframe: | |
| skip -= 1 | |
| myframe = myframe.f_back | |
| if skip != 0 or not myframe: | |
| return None | |
| if not myframe.f_code.co_varnames: | |
| return None | |
| self = myframe.f_locals[myframe.f_code.co_varnames[0]] | |
| if not isinstance(self, object): | |
| return None | |
| return self | |
| finally: | |
| if myframe: | |
| del myframe | |
| def check_exact_class(skip): | |
| def __func(obj): | |
| caller = get_caller_instance(skip = skip) | |
| if caller is None or caller.__class__ != obj.__class__: | |
| raise AccessError("Caller instance is not of class '%s'" % (obj.__class__.__name__, )) | |
| return __func | |
| def check_subclass(skip): | |
| def __func(obj): | |
| caller = get_caller_instance(skip = skip) | |
| if not caller or not issubclass(caller.__class__, obj.__class__): | |
| raise AccessError("Caller instance is not a subclass of class '%s'" % (obj.__class__.__name__, )) | |
| return __func | |
| class MemberCheck(object): | |
| def __init__(self, check_fun, initval = None): | |
| self._initval = initval | |
| self._check_fun = check_fun | |
| @property | |
| def key(self): | |
| return '__entry_%s__' % id(self) | |
| def __get__(self, instance, owner): | |
| self._check_fun(instance) | |
| if not hasattr(instance, self.key): | |
| setattr(instance, self.key, self._initval) | |
| return getattr(instance, self.key) | |
| def __set__(self, instance, value): | |
| self._check_fun(instance) | |
| setattr(instance, self.key, value) | |
| def __delete__(self, instance): | |
| self._check_fun(instance) | |
| delattr(instance, self.key, self._initval) | |
| class MethodCheck(object): | |
| def __init__(self, check_fun, function): | |
| self._function = function | |
| self._check_fun = check_fun | |
| def __get__(self, instance, owner): | |
| self._check_fun(instance) | |
| return types.MethodType(self._function, instance) | |
| def __set__(self, instance, value): | |
| raise AccessError("Cannot overwrite a method") | |
| def __delete__(self, instance): | |
| raise AccessError("Cannot delete a method") | |
| def check_private(skip): | |
| return check_exact_class(skip) | |
| def check_protected(skip): | |
| return check_subclass(skip) | |
| def wrap(func, to): | |
| func.__doc__ = to.__doc__ | |
| func.__name__ = to.__name__ | |
| return func | |
| def access(member, check): | |
| if hasattr(member, "__call__"): | |
| return MethodCheck(check, member) | |
| else: | |
| return MemberCheck(check, member) | |
| def private(member): | |
| return access(member, check_private(3)) | |
| def protected(member): | |
| return access(member, check_protected(3)) | |
| if __name__ == '__main__': | |
| class Test(object): | |
| x = private(None) | |
| @private | |
| def test_private(self): | |
| print self | |
| self.x = 0 if self.x is None else self.x + 1 | |
| print self.x | |
| @protected | |
| def test_protected(self): | |
| self.test_private() | |
| def test_public(self): | |
| self.test_protected() | |
| def expect_error(exc, fun, *args, **kwargs): | |
| try: | |
| fun(*args, **kwargs) | |
| except exc, e: | |
| print "Exception: '%s' (type %s)" % (e, e.__class__.__name__) | |
| t = Test() | |
| expect_error(AccessError, getattr, t, 'x') | |
| expect_error(AccessError, getattr, t, 'test_private') | |
| expect_error(AccessError, getattr, t, 'test_protected') | |
| t.test_public() | |
| t.test_public() | |
| t.test_public() | |
| t.test_public() | |
| Test().test_public() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment