1 """ 2 A Python Singleton mixin class that makes use of some of the ideas 3 found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit 4 from it and you have a singleton. No code is required in 5 subclasses to create singleton behavior -- inheritance from 6 Singleton is all that is needed. 7 8 Assume S is a class that inherits from Singleton. Useful behaviors 9 are: 10 11 1) Getting the singleton: 12 13 S.getInstance() 14 15 returns the instance of S. If none exists, it is created. 16 17 2) The usual idiom to construct an instance by calling the class, i.e. 18 19 S() 20 21 is disabled for the sake of clarity. If it were allowed, a programmer 22 who didn't happen notice the inheritance from Singleton might think he 23 was creating a new instance. So it is felt that it is better to 24 make that clearer by requiring the call of a class method that is defined in 25 Singleton. An attempt to instantiate via S() will restult in an SingletonException 26 being raised. 27 28 3) If S.__init__(.) requires parameters, include them in the 29 first call to S.getInstance(.). If subsequent calls have parameters, 30 a SingletonException is raised. 31 32 4) As an implementation detail, classes that inherit 33 from Singleton may not have their own __new__ 34 methods. To make sure this requirement is followed, 35 an exception is raised if a Singleton subclass includ 36 es __new__. This happens at subclass instantiation 37 time (by means of the MetaSingleton metaclass. 38 39 By Gary Robinson, grobinson (at] transpose.com. No rights reserved -- 40 placed in the public domain -- which is only reasonable considering 41 how much it owes to other people's version which are in the 42 public domain. The idea of using a metaclass came from 43 a comment on Gary's blog (see 44 http://www.garyrobinson.net/2004/03/python_singleto.html#comments). 45 Not guaranteed to be fit for any particular purpose. 46 """ 47 48 class SingletonException(Exception): 49 pass 50 51 class MetaSingleton(type): 52 def __new__(metaclass, strName, tupBases, dict): 53 if '__new__' in dict: 54 raise SingletonException, 'Can not override __new__ in a Singleton' 55 return super(MetaSingleton,metaclass).__new__(metaclass, strName, tupBases, dict) 56 57 def __call__(cls, *lstArgs, **dictArgs): 58 raise SingletonException, 'Singletons may only be instantiated through getInstance()' 59 60 class Singleton(object): 61 __metaclass__ = MetaSingleton 62 63 def getInstance(cls, *lstArgs): 64 """ 65 Call this to instantiate an instance or retrieve the existing instance. 66 If the singleton requires args to be instantiated, include them the first 67 time you call getInstance. 68 """ 69 if cls._isInstantiated(): 70 if len(lstArgs) != 0: 71 raise SingletonException, 'If no supplied args, singleton must already be instantiated, or __init__ must require no args' 72 else: 73 if len(lstArgs) != cls._getConstructionArgCountNotCountingSelf(): 74 raise SingletonException, 'If the singleton requires __init__ args, supply them on first instantiation' 75 instance = cls.__new__(cls) 76 instance.__init__(*lstArgs) 77 cls.cInstance = instance 78 return cls.cInstance 79 getInstance = classmethod(getInstance) 80 81 def _isInstantiated(cls): 82 return hasattr(cls, 'cInstance') 83 _isInstantiated = classmethod(_isInstantiated) 84 85 def _getConstructionArgCountNotCountingSelf(cls): 86 return cls.__init__.im_func.func_code.co_argcount - 1 87 _getConstructionArgCountNotCountingSelf = classmethod(_getConstructionArgCountNotCountingSelf) 88 89 def _forgetClassInstanceReferenceForTesting(cls): 90 """ 91 This is designed for convenience in testing -- sometimes you 92 want to get rid of a singleton during test code to see what 93 happens when you call getInstance() under a new situation. 94 95 To really delete the object, all external references to it 96 also need to be deleted. 97 """ 98 try: 99 delattr(cls,'cInstance') 100 except AttributeError: 101 # run up the chain of base classes until we find the one that has the instance 102 # and then delete it there 103 for baseClass in cls.__bases__: 104 if issubclass(baseClass, Singleton): 105 baseClass._forgetClassInstanceReferenceForTesting() 106 _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting) 107 108 109 if __name__ == '__main__': 110 import unittest 111 112 class PublicInterfaceTest(unittest.TestCase): 113 def testReturnsSameObject(self): 114 """ 115 Demonstrates normal use -- just call getInstance and it returns a singleton instance 116 """ 117 118 class A(Singleton): 119 def __init__(self): 120 super(A, self).__init__() 121 122 a1 = A.getInstance() 123 a2 = A.getInstance() 124 self.assertEquals(id(a1), id(a2)) 125 126 def testInstantiateWithMultiArgConstructor(self): 127 """ 128 If the singleton needs args to construct, include them in the first 129 call to get instances. 130 """ 131 132 class B(Singleton): 133 134 def __init__(self, arg1, arg2): 135 super(B, self).__init__() 136 self.arg1 = arg1 137 self.arg2 = arg2 138 139 b1 = B.getInstance('arg1 value', 'arg2 value') 140 b2 = B.getInstance() 141 self.assertEquals(b1.arg1, 'arg1 value') 142 self.assertEquals(b1.arg2, 'arg2 value') 143 self.assertEquals(id(b1), id(b2)) 144 145 146 def testTryToInstantiateWithoutNeededArgs(self): 147 148 class B(Singleton): 149 150 def __init__(self, arg1, arg2): 151 super(B, self).__init__() 152 self.arg1 = arg1 153 self.arg2 = arg2 154 155 self.assertRaises(SingletonException, B.getInstance) 156 157 def testTryToInstantiateWithoutGetInstance(self): 158 """ 159 Demonstrates that singletons can ONLY be instantiated through 160 getInstance, as long as they call Singleton.__init__ during construction. 161 162 If this check is not required, you don't need to call Singleton.__init__(). 163 """ 164 165 class A(Singleton): 166 def __init__(self): 167 super(A, self).__init__() 168 169 self.assertRaises(SingletonException, A) 170 171 def testDontAllowNew(self): 172 173 def instantiatedAnIllegalClass(): 174 class A(Singleton): 175 def __init__(self): 176 super(A, self).__init__() 177 178 def __new__(metaclass, strName, tupBases, dict): 179 return super(MetaSingleton,metaclass).__new__(metaclass, strName, tupBases, dict) 180 181 self.assertRaises(SingletonException, instantiatedAnIllegalClass) 182 183 184 def testDontAllowArgsAfterConstruction(self): 185 class B(Singleton): 186 187 def __init__(self, arg1, arg2): 188 super(B, self).__init__() 189 self.arg1 = arg1 190 self.arg2 = arg2 191 192 b1 = B.getInstance('arg1 value', 'arg2 value') 193 self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value') 194 195 def test_forgetClassInstanceReferenceForTesting(self): 196 class A(Singleton): 197 def __init__(self): 198 super(A, self).__init__() 199 class B(A): 200 def __init__(self): 201 super(B, self).__init__() 202 203 # check that changing the class after forgetting the instance produces 204 # an instance of the new class 205 a = A.getInstance() 206 assert a.__class__.__name__ == 'A' 207 A._forgetClassInstanceReferenceForTesting() 208 b = B.getInstance() 209 assert b.__class__.__name__ == 'B' 210 211 # check that invoking the 'forget' on a subclass still deletes the instance 212 B._forgetClassInstanceReferenceForTesting() 213 a = A.getInstance() 214 B._forgetClassInstanceReferenceForTesting() 215 b = B.getInstance() 216 assert b.__class__.__name__ == 'B' 217 218 unittest.main() 219 220 221