Skip to content

Instantly share code, notes, and snippets.

@herczy
Created September 3, 2011 16:54
Show Gist options
  • Select an option

  • Save herczy/1191456 to your computer and use it in GitHub Desktop.

Select an option

Save herczy/1191456 to your computer and use it in GitHub Desktop.
Want to have protected and private? :-)
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