import sys
import inspect
import textwrap
import collections
class PrivateImplicitThisSingleton(type):
def __new__(metaclass, name, bases, attrs):
noop = lambda *args, **kwargs: None
func_type = type(noop)
private = {'object': None}
class Context(collections.MutableMapping, dict):
def __init__(self):
self.globals = globals()
def __delitem__(self, key):
del self.globals[key]
def __getitem__(self, key):
return self.globals[key]
def __setitem__(self, key, val):
self.globals[key] = val
context = Context()
for func_name, old_func in attrs.iteritems():
if isinstance(old_func, func_type):
try:
save = context[func_name]
restore = True
except KeyError:
restore = False
exec textwrap.dedent(inspect.getsource(old_func)).replace('(', '(this,', 1) in context
attrs[func_name] = context[func_name]
if restore:
context[func_name] = save
else:
del context[func_name]
def new_object(cls, *args, **kwargs):
if private['object'] is None:
private['object'] = bases[0].__new__(cls)
private['object'].__init__(*args, **kwargs)
cls.__init__ = noop
private['attrs'] = private['object'].__dict__
del private['object'].__dict__
def check_access():
if sys._getframe(2).f_globals is not context:
raise AttributeError('private')
def get_attr(self, key):
try:
val = private['attrs'][key]
check_access()
return val
except KeyError:
return super(cls, self).__getattribute__(key)
def set_attr(self, key, val):
check_access()
private['attrs'][key] = val
cls.__getattribute__ = get_attr
cls.__setattr__ = set_attr
return private['object']
attrs['__new__'] = new_object
return type(name, bases, attrs)
class RockStarEnterpriseClass(object):
__metaclass__ = PrivateImplicitThisSingleton
def __init__(val):
this.val = val
def getVal():
return this.val
def setVal(val):
this.val = val
Diff to Previous Revision
--- revision 2 2010-05-08 08:16:50
+++ revision 3 2010-05-10 08:34:32
@@ -1,165 +1,73 @@
-#!/usr/bin/env python
-
import sys
import inspect
import textwrap
+import collections
-class ReadOnlyPrivateSingletonImplicitThisMetaClass(type):
-
- """Make your classes suitable for Enterprise deployment"""
+class PrivateImplicitThisSingleton(type):
def __new__(metaclass, name, bases, attrs):
- private = {'instance': None}
noop = lambda *args, **kwargs: None
- context = globals()
-
- # this recompiles all the methods to add implicit "this" statement to function.
- # it is "sort of" a hack in the same way water is "sort of" wet
+ func_type = type(noop)
+ private = {'object': None}
+ class Context(collections.MutableMapping, dict):
+ def __init__(self):
+ self.globals = globals()
+ def __delitem__(self, key):
+ del self.globals[key]
+ def __getitem__(self, key):
+ return self.globals[key]
+ def __setitem__(self, key, val):
+ self.globals[key] = val
+ context = Context()
for func_name, old_func in attrs.iteritems():
- if isinstance(old_func, type(noop)):
- # we need to compile in the global context if we want this to be identical
- # to the original method, but we need to be careful we don't clobber existing
- # namespace. to work around this, we store existing vars/functions if they
- # exist, and restore after recompiling
+ if isinstance(old_func, func_type):
try:
- saved_global = context[func_name]
- restore_global = True
+ save = context[func_name]
+ restore = True
except KeyError:
- restore_global = False
+ restore = False
exec textwrap.dedent(inspect.getsource(old_func)).replace('(', '(this,', 1) in context
attrs[func_name] = context[func_name]
- if restore_global:
- context[func_name] = saved_global
+ if restore:
+ context[func_name] = save
else:
del context[func_name]
-
- def create_new_class(cls, *args, **kwargs):
- """Only instantiate once (the so-called Singleton design pattern)"""
- if private['instance'] is None:
- private['instance'] = super(cls).__new__.__new__(cls)
- private['instance'].__init__(*args, **kwargs)
+ def new_object(cls, *args, **kwargs):
+ if private['object'] is None:
+ private['object'] = bases[0].__new__(cls)
+ private['object'].__init__(*args, **kwargs)
cls.__init__ = noop
-
- # get rid of regular attribute access and replace getter/setter
- # with methods that (a) enforce read-only and (b) only allow
- # attribute access from the base level of methods. you will find
- # this makes it necessary to create getters and setters for everything.
- private['attrs'] = private['instance'].__dict__
- del private['instance'].__dict__
-
+ private['attrs'] = private['object'].__dict__
+ del private['object'].__dict__
+ def check_access():
+ if sys._getframe(2).f_globals is not context:
+ raise AttributeError('private')
def get_attr(self, key):
try:
val = private['attrs'][key]
+ check_access()
+ return val
except KeyError:
return super(cls, self).__getattribute__(key)
- code = sys._getframe(1).f_code
- if code.co_stacksize != 1 or not code.co_varnames or code.co_varnames[0] != 'this':
- raise AttributeError('private access denied')
- return val
-
def set_attr(self, key, val):
- raise AttributeError('read-only')
-
+ check_access()
+ private['attrs'][key] = val
cls.__getattribute__ = get_attr
cls.__setattr__ = set_attr
-
- return private['instance']
-
- attrs['__new__'] = create_new_class
- return super(ReadOnlyPrivateSingletonImplicitThisMetaClass, metaclass).__new__(metaclass, name, bases, attrs)
-
-class BaseA(object):
-
- """These are just here to prove multiple-inheritence works"""
-
- call_count = 0
-
- def __init__(self, *args, **kwargs):
- BaseA.call_count += 1
- super(BaseA, self).__init__(*args, **kwargs)
+ return private['object']
+ attrs['__new__'] = new_object
+ return type(name, bases, attrs)
-class BaseB(object):
+class RockStarEnterpriseClass(object):
- """These are just here to prove multiple-inheritence works"""
+ __metaclass__ = PrivateImplicitThisSingleton
- call_count = 0
+ def __init__(val):
+ this.val = val
- def __init__(self, *args, **kwargs):
- BaseB.call_count += 1
- super(BaseB, self).__init__(*args, **kwargs)
+ def getVal():
+ return this.val
-
-class EnterpriseRockStarClass(BaseA, BaseB):
-
- """Example usage"""
-
- __metaclass__ = ReadOnlyPrivateSingletonImplicitThisMetaClass
-
- def __init__(foo):
- """This only gets called once"""
- this.foo = foo
- super(EnterpriseRockStarClass, this).__init__()
-
- def getFoo():
- """Public accessor. Go ahead and try to read foo from outside these methods, I dare you"""
- return this.foo
-
-def getFoo():
- return 'the global foo'
-
-
-def main():
- """Test this actually works"""
-
- obj = EnterpriseRockStarClass('foo')
-
- print 'Testing public attribute access is allowed:',
- try:
- if obj.getFoo() != 'foo':
- raise ValueError
- print 'OK'
- except (AttributeError, ValueError):
- print 'FAIL'
-
- print 'Testing private attribute access is protected:',
- try:
- obj.foo
- print 'FAIL'
- except AttributeError:
- print 'OK'
-
- print 'Testing read-only status:',
- try:
- obj.foo = 1
- obj.bar = 2
- print 'FAIL'
- except AttributeError:
- print 'OK'
-
- print 'Testing singleton:',
- other = EnterpriseRockStarClass('xyz')
- if obj is other and obj.getFoo() == other.getFoo() == 'foo':
- print 'OK'
- else:
- print 'FAIL'
-
- print 'Testing multiple inheritance:',
- if BaseA.call_count == BaseB.call_count == 1:
- print 'OK'
- else:
- print 'FAIL'
-
- print "Testing global function didn't get clobbered:",
- try:
- if getFoo() != 'the global foo':
- raise ValueError
- print 'OK'
- except:
- print 'FAIL'
-
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(main())
+ def setVal(val):
+ this.val = val