Welcome, guest | Sign In | My Account | Store | Cart
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

History