1"""
2A Python Singleton mixin class that makes use of some of the ideas
3found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit
4from it and you have a singleton. No code is required in
5subclasses to create singleton behavior -- inheritance from
6Singleton is all that is needed.
7
8Assume S is a class that inherits from Singleton. Useful behaviors
9are:
10
111) Getting the singleton:
12
13    S.getInstance()
14
15returns the instance of S. If none exists, it is created.
16
172) The usual idiom to construct an instance by calling the class, i.e.
18
19    S()
20
21is disabled for the sake of clarity. If it were allowed, a programmer
22who didn't happen  notice the inheritance from Singleton might think he
23was creating a new instance. So it is felt that it is better to
24make that clearer by requiring the call of a class method that is defined in
25Singleton. An attempt to instantiate via S() will restult in an SingletonException
26being raised.
27
283) If S.__init__(.) requires parameters, include them in the
29first call to S.getInstance(.). If subsequent calls have parameters,
30a SingletonException is raised.
31
324) As an implementation detail, classes that inherit
33from Singleton may not have their own __new__
34methods. To make sure this requirement is followed,
35an exception is raised if a Singleton subclass includ
36es __new__. This happens at subclass instantiation
37time (by means of the MetaSingleton metaclass.
38
39By Gary Robinson, grobinson@transpose.com. No rights reserved --
40placed in the public domain -- which is only reasonable considering
41how much it owes to other people's version which are in the
42public domain. The idea of using a metaclass came from
43a  comment on Gary's blog (see
44http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
45Not guaranteed to be fit for any particular purpose.
46"""
47
48class SingletonException(Exception):
49    pass
50
51class 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
60class 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
109if __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