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 | Singleton creation is threadsafe.
|
---|
9 |
|
---|
10 | USAGE:
|
---|
11 |
|
---|
12 | Just inherit from Singleton. If you need a constructor, include
|
---|
13 | an __init__() method in your class as you usually would. However,
|
---|
14 | if your class is S, you instantiate the singleton using S.getInstance()
|
---|
15 | instead of S(). Repeated calls to S.getInstance() return the
|
---|
16 | originally-created instance.
|
---|
17 |
|
---|
18 | For example:
|
---|
19 |
|
---|
20 | class S(Singleton):
|
---|
21 |
|
---|
22 | def __init__(self, a, b=1):
|
---|
23 | pass
|
---|
24 |
|
---|
25 | S1 = S.getInstance(1, b=3)
|
---|
26 |
|
---|
27 |
|
---|
28 | Most of the time, that's all you need to know. However, there are some
|
---|
29 | other useful behaviors. Read on for a full description:
|
---|
30 |
|
---|
31 | 1) Getting the singleton:
|
---|
32 |
|
---|
33 | S.getInstance()
|
---|
34 |
|
---|
35 | returns the instance of S. If none exists, it is created.
|
---|
36 |
|
---|
37 | 2) The usual idiom to construct an instance by calling the class, i.e.
|
---|
38 |
|
---|
39 | S()
|
---|
40 |
|
---|
41 | is disabled for the sake of clarity.
|
---|
42 |
|
---|
43 | For one thing, the S() syntax means instantiation, but getInstance()
|
---|
44 | usually does not cause instantiation. So the S() syntax would
|
---|
45 | be misleading.
|
---|
46 |
|
---|
47 | Because of that, if S() were allowed, a programmer who didn't
|
---|
48 | happen to notice the inheritance from Singleton (or who
|
---|
49 | wasn't fully aware of what a Singleton pattern
|
---|
50 | does) might think he was creating a new instance,
|
---|
51 | which could lead to very unexpected behavior.
|
---|
52 |
|
---|
53 | So, overall, it is felt that it is better to make things clearer
|
---|
54 | by requiring the call of a class method that is defined in
|
---|
55 | Singleton. An attempt to instantiate via S() will result
|
---|
56 | in a SingletonException being raised.
|
---|
57 |
|
---|
58 | 3) Use __S.__init__() for instantiation processing,
|
---|
59 | since S.getInstance() runs S.__init__(), passing it the args it has received.
|
---|
60 |
|
---|
61 | If no data needs to be passed in at instantiation time, you don't need S.__init__().
|
---|
62 |
|
---|
63 | 4) If S.__init__(.) requires parameters, include them ONLY in the
|
---|
64 | first call to S.getInstance(). If subsequent calls have arguments,
|
---|
65 | a SingletonException is raised by default.
|
---|
66 |
|
---|
67 | If you find it more convenient for subsequent calls to be allowed to
|
---|
68 | have arguments, but for those argumentsto be ignored, just include
|
---|
69 | 'ignoreSubsequent = True' in your class definition, i.e.:
|
---|
70 |
|
---|
71 | class S(Singleton):
|
---|
72 |
|
---|
73 | ignoreSubsequent = True
|
---|
74 |
|
---|
75 | def __init__(self, a, b=1):
|
---|
76 | pass
|
---|
77 |
|
---|
78 | 5) For testing, it is sometimes convenient for all existing singleton
|
---|
79 | instances to be forgotten, so that new instantiations can occur. For that
|
---|
80 | reason, a forgetAllSingletons() function is included. Just call
|
---|
81 |
|
---|
82 | forgetAllSingletons()
|
---|
83 |
|
---|
84 | and it is as if no earlier instantiations have occurred.
|
---|
85 |
|
---|
86 | 6) As an implementation detail, classes that inherit
|
---|
87 | from Singleton may not have their own __new__
|
---|
88 | methods. To make sure this requirement is followed,
|
---|
89 | an exception is raised if a Singleton subclass includ
|
---|
90 | es __new__. This happens at subclass instantiation
|
---|
91 | time (by means of the MetaSingleton metaclass.
|
---|
92 |
|
---|
93 |
|
---|
94 | By Gary Robinson, grobinson@flyfi.com. No rights reserved --
|
---|
95 | placed in the public domain -- which is only reasonable considering
|
---|
96 | how much it owes to other people's code and ideas which are in the
|
---|
97 | public domain. The idea of using a metaclass came from
|
---|
98 | a comment on Gary's blog (see
|
---|
99 | http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
|
---|
100 | Other improvements came from comments and email from other
|
---|
101 | people who saw it online. (See the blog post and comments
|
---|
102 | for further credits.)
|
---|
103 |
|
---|
104 | Not guaranteed to be fit for any particular purpose. Use at your
|
---|
105 | own risk.
|
---|
106 | """
|
---|
107 |
|
---|
108 | import threading
|
---|
109 |
|
---|
110 | class SingletonException(Exception):
|
---|
111 | pass
|
---|
112 |
|
---|
113 | _stSingletons = set()
|
---|
114 | _lockForSingletons = threading.RLock()
|
---|
115 | _lockForSingletonCreation = threading.RLock() # Ensure only one instance of each Singleton
|
---|
116 | # class is created. This is not bound to the
|
---|
117 | # individual Singleton class since we need to
|
---|
118 | # ensure that there is only one mutex for each
|
---|
119 | # Singleton class, which would require having
|
---|
120 | # a lock when setting up the Singleton class,
|
---|
121 | # which is what this is anyway. So, when any
|
---|
122 | # Singleton is created, we lock this lock and
|
---|
123 | # then we don't need to lock it again for that
|
---|
124 | # class.
|
---|
125 |
|
---|
126 | def _createSingletonInstance(cls, lstArgs, dctKwArgs):
|
---|
127 | _lockForSingletonCreation.acquire()
|
---|
128 | try:
|
---|
129 | if cls._isInstantiated(): # some other thread got here first
|
---|
130 | return
|
---|
131 |
|
---|
132 | instance = cls.__new__(cls)
|
---|
133 | try:
|
---|
134 | instance.__init__(*lstArgs, **dctKwArgs)
|
---|
135 | except TypeError, e:
|
---|
136 | if e.message.find('__init__() takes') != -1:
|
---|
137 | raise SingletonException, 'If the singleton requires __init__ args, supply them on first call to getInstance().'
|
---|
138 | else:
|
---|
139 | raise
|
---|
140 | cls.cInstance = instance
|
---|
141 | _addSingleton(cls)
|
---|
142 | finally:
|
---|
143 | _lockForSingletonCreation.release()
|
---|
144 |
|
---|
145 | def _addSingleton(cls):
|
---|
146 | _lockForSingletons.acquire()
|
---|
147 | try:
|
---|
148 | assert cls not in _stSingletons
|
---|
149 | _stSingletons.add(cls)
|
---|
150 | finally:
|
---|
151 | _lockForSingletons.release()
|
---|
152 |
|
---|
153 | def _removeSingleton(cls):
|
---|
154 | _lockForSingletons.acquire()
|
---|
155 | try:
|
---|
156 | if cls in _stSingletons:
|
---|
157 | _stSingletons.remove(cls)
|
---|
158 | finally:
|
---|
159 | _lockForSingletons.release()
|
---|
160 |
|
---|
161 | def forgetAllSingletons():
|
---|
162 | '''This is useful in tests, since it is hard to know which singletons need to be cleared to make a test work.'''
|
---|
163 | _lockForSingletons.acquire()
|
---|
164 | try:
|
---|
165 | for cls in _stSingletons.copy():
|
---|
166 | cls._forgetClassInstanceReferenceForTesting()
|
---|
167 |
|
---|
168 | # Might have created some Singletons in the process of tearing down.
|
---|
169 | # Try one more time - there should be a limit to this.
|
---|
170 | iNumSingletons = len(_stSingletons)
|
---|
171 | if len(_stSingletons) > 0:
|
---|
172 | for cls in _stSingletons.copy():
|
---|
173 | cls._forgetClassInstanceReferenceForTesting()
|
---|
174 | iNumSingletons -= 1
|
---|
175 | assert iNumSingletons == len(_stSingletons), 'Added a singleton while destroying ' + str(cls)
|
---|
176 | assert len(_stSingletons) == 0, _stSingletons
|
---|
177 | finally:
|
---|
178 | _lockForSingletons.release()
|
---|
179 |
|
---|
180 | class MetaSingleton(type):
|
---|
181 | def __new__(metaclass, strName, tupBases, dct):
|
---|
182 | if dct.has_key('__new__'):
|
---|
183 | raise SingletonException, 'Can not override __new__ in a Singleton'
|
---|
184 | return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
|
---|
185 |
|
---|
186 | def __call__(cls, *lstArgs, **dictArgs):
|
---|
187 | raise SingletonException, 'Singletons may only be instantiated through getInstance()'
|
---|
188 |
|
---|
189 | class Singleton(object):
|
---|
190 | __metaclass__ = MetaSingleton
|
---|
191 |
|
---|
192 | def getInstance(cls, *lstArgs, **dctKwArgs):
|
---|
193 | """
|
---|
194 | Call this to instantiate an instance or retrieve the existing instance.
|
---|
195 | If the singleton requires args to be instantiated, include them the first
|
---|
196 | time you call getInstance.
|
---|
197 | """
|
---|
198 | if cls._isInstantiated():
|
---|
199 | if (lstArgs or dctKwArgs) and not hasattr(cls, 'ignoreSubsequent'):
|
---|
200 | raise SingletonException, 'Singleton already instantiated, but getInstance() called with args.'
|
---|
201 | else:
|
---|
202 | _createSingletonInstance(cls, lstArgs, dctKwArgs)
|
---|
203 |
|
---|
204 | return cls.cInstance
|
---|
205 | getInstance = classmethod(getInstance)
|
---|
206 |
|
---|
207 | def _isInstantiated(cls):
|
---|
208 | # Don't use hasattr(cls, 'cInstance'), because that screws things up if there is a singleton that
|
---|
209 | # extends another singleton. hasattr looks in the base class if it doesn't find in subclass.
|
---|
210 | return 'cInstance' in cls.__dict__
|
---|
211 | _isInstantiated = classmethod(_isInstantiated)
|
---|
212 |
|
---|
213 | # This can be handy for public use also
|
---|
214 | isInstantiated = _isInstantiated
|
---|
215 |
|
---|
216 | def _forgetClassInstanceReferenceForTesting(cls):
|
---|
217 | """
|
---|
218 | This is designed for convenience in testing -- sometimes you
|
---|
219 | want to get rid of a singleton during test code to see what
|
---|
220 | happens when you call getInstance() under a new situation.
|
---|
221 |
|
---|
222 | To really delete the object, all external references to it
|
---|
223 | also need to be deleted.
|
---|
224 | """
|
---|
225 | try:
|
---|
226 | if hasattr(cls.cInstance, '_prepareToForgetSingleton'):
|
---|
227 | # tell instance to release anything it might be holding onto.
|
---|
228 | cls.cInstance._prepareToForgetSingleton()
|
---|
229 | del cls.cInstance
|
---|
230 | _removeSingleton(cls)
|
---|
231 | except AttributeError:
|
---|
232 | # run up the chain of base classes until we find the one that has the instance
|
---|
233 | # and then delete it there
|
---|
234 | for baseClass in cls.__bases__:
|
---|
235 | if issubclass(baseClass, Singleton):
|
---|
236 | baseClass._forgetClassInstanceReferenceForTesting()
|
---|
237 | _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting)
|
---|
238 |
|
---|
239 |
|
---|
240 | if __name__ == '__main__':
|
---|
241 |
|
---|
242 | import unittest
|
---|
243 | import time
|
---|
244 |
|
---|
245 | class singletonmixin_Public_TestCase(unittest.TestCase):
|
---|
246 | def testReturnsSameObject(self):
|
---|
247 | """
|
---|
248 | Demonstrates normal use -- just call getInstance and it returns a singleton instance
|
---|
249 | """
|
---|
250 |
|
---|
251 | class A(Singleton):
|
---|
252 | def __init__(self):
|
---|
253 | super(A, self).__init__()
|
---|
254 |
|
---|
255 | a1 = A.getInstance()
|
---|
256 | a2 = A.getInstance()
|
---|
257 | self.assertEquals(id(a1), id(a2))
|
---|
258 |
|
---|
259 | def testInstantiateWithMultiArgConstructor(self):
|
---|
260 | """
|
---|
261 | If the singleton needs args to construct, include them in the first
|
---|
262 | call to get instances.
|
---|
263 | """
|
---|
264 |
|
---|
265 | class B(Singleton):
|
---|
266 |
|
---|
267 | def __init__(self, arg1, arg2):
|
---|
268 | super(B, self).__init__()
|
---|
269 | self.arg1 = arg1
|
---|
270 | self.arg2 = arg2
|
---|
271 |
|
---|
272 | b1 = B.getInstance('arg1 value', 'arg2 value')
|
---|
273 | b2 = B.getInstance()
|
---|
274 | self.assertEquals(b1.arg1, 'arg1 value')
|
---|
275 | self.assertEquals(b1.arg2, 'arg2 value')
|
---|
276 | self.assertEquals(id(b1), id(b2))
|
---|
277 |
|
---|
278 | def testInstantiateWithKeywordArg(self):
|
---|
279 |
|
---|
280 | class B(Singleton):
|
---|
281 |
|
---|
282 | def __init__(self, arg1=5):
|
---|
283 | super(B, self).__init__()
|
---|
284 | self.arg1 = arg1
|
---|
285 |
|
---|
286 | b1 = B.getInstance('arg1 value')
|
---|
287 | b2 = B.getInstance()
|
---|
288 | self.assertEquals(b1.arg1, 'arg1 value')
|
---|
289 | self.assertEquals(id(b1), id(b2))
|
---|
290 |
|
---|
291 | def testTryToInstantiateWithoutNeededArgs(self):
|
---|
292 |
|
---|
293 | class B(Singleton):
|
---|
294 |
|
---|
295 | def __init__(self, arg1, arg2):
|
---|
296 | super(B, self).__init__()
|
---|
297 | self.arg1 = arg1
|
---|
298 | self.arg2 = arg2
|
---|
299 |
|
---|
300 | self.assertRaises(SingletonException, B.getInstance)
|
---|
301 |
|
---|
302 | def testPassTypeErrorIfAllArgsThere(self):
|
---|
303 | """
|
---|
304 | Make sure the test for capturing missing args doesn't interfere with a normal TypeError.
|
---|
305 | """
|
---|
306 | class B(Singleton):
|
---|
307 |
|
---|
308 | def __init__(self, arg1, arg2):
|
---|
309 | super(B, self).__init__()
|
---|
310 | self.arg1 = arg1
|
---|
311 | self.arg2 = arg2
|
---|
312 | raise TypeError, 'some type error'
|
---|
313 |
|
---|
314 | self.assertRaises(TypeError, B.getInstance, 1, 2)
|
---|
315 |
|
---|
316 | def testTryToInstantiateWithoutGetInstance(self):
|
---|
317 | """
|
---|
318 | Demonstrates that singletons can ONLY be instantiated through
|
---|
319 | getInstance, as long as they call Singleton.__init__ during construction.
|
---|
320 |
|
---|
321 | If this check is not required, you don't need to call Singleton.__init__().
|
---|
322 | """
|
---|
323 |
|
---|
324 | class A(Singleton):
|
---|
325 | def __init__(self):
|
---|
326 | super(A, self).__init__()
|
---|
327 |
|
---|
328 | self.assertRaises(SingletonException, A)
|
---|
329 |
|
---|
330 | def testDontAllowNew(self):
|
---|
331 |
|
---|
332 | def instantiatedAnIllegalClass():
|
---|
333 | class A(Singleton):
|
---|
334 | def __init__(self):
|
---|
335 | super(A, self).__init__()
|
---|
336 |
|
---|
337 | def __new__(metaclass, strName, tupBases, dct):
|
---|
338 | return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
|
---|
339 |
|
---|
340 | self.assertRaises(SingletonException, instantiatedAnIllegalClass)
|
---|
341 |
|
---|
342 |
|
---|
343 | def testDontAllowArgsAfterConstruction(self):
|
---|
344 | class B(Singleton):
|
---|
345 |
|
---|
346 | def __init__(self, arg1, arg2):
|
---|
347 | super(B, self).__init__()
|
---|
348 | self.arg1 = arg1
|
---|
349 | self.arg2 = arg2
|
---|
350 |
|
---|
351 | B.getInstance('arg1 value', 'arg2 value')
|
---|
352 | self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value')
|
---|
353 |
|
---|
354 | def test_forgetClassInstanceReferenceForTesting(self):
|
---|
355 | class A(Singleton):
|
---|
356 | def __init__(self):
|
---|
357 | super(A, self).__init__()
|
---|
358 | class B(A):
|
---|
359 | def __init__(self):
|
---|
360 | super(B, self).__init__()
|
---|
361 |
|
---|
362 | # check that changing the class after forgetting the instance produces
|
---|
363 | # an instance of the new class
|
---|
364 | a = A.getInstance()
|
---|
365 | assert a.__class__.__name__ == 'A'
|
---|
366 | A._forgetClassInstanceReferenceForTesting()
|
---|
367 | b = B.getInstance()
|
---|
368 | assert b.__class__.__name__ == 'B'
|
---|
369 |
|
---|
370 | # check that invoking the 'forget' on a subclass still deletes the instance
|
---|
371 | B._forgetClassInstanceReferenceForTesting()
|
---|
372 | a = A.getInstance()
|
---|
373 | B._forgetClassInstanceReferenceForTesting()
|
---|
374 | b = B.getInstance()
|
---|
375 | assert b.__class__.__name__ == 'B'
|
---|
376 |
|
---|
377 | def test_forgetAllSingletons(self):
|
---|
378 | # Should work if there are no singletons
|
---|
379 | forgetAllSingletons()
|
---|
380 |
|
---|
381 | class A(Singleton):
|
---|
382 | ciInitCount = 0
|
---|
383 | def __init__(self):
|
---|
384 | super(A, self).__init__()
|
---|
385 | A.ciInitCount += 1
|
---|
386 |
|
---|
387 | A.getInstance()
|
---|
388 | self.assertEqual(A.ciInitCount, 1)
|
---|
389 |
|
---|
390 | A.getInstance()
|
---|
391 | self.assertEqual(A.ciInitCount, 1)
|
---|
392 |
|
---|
393 | forgetAllSingletons()
|
---|
394 | A.getInstance()
|
---|
395 | self.assertEqual(A.ciInitCount, 2)
|
---|
396 |
|
---|
397 | def test_threadedCreation(self):
|
---|
398 | # Check that only one Singleton is created even if multiple
|
---|
399 | # threads try at the same time. If fails, would see assert in _addSingleton
|
---|
400 | class Test_Singleton(Singleton):
|
---|
401 | def __init__(self):
|
---|
402 | super(Test_Singleton, self).__init__()
|
---|
403 |
|
---|
404 | class Test_SingletonThread(threading.Thread):
|
---|
405 | def __init__(self, fTargetTime):
|
---|
406 | super(Test_SingletonThread, self).__init__()
|
---|
407 | self._fTargetTime = fTargetTime
|
---|
408 | self._eException = None
|
---|
409 |
|
---|
410 | def run(self):
|
---|
411 | try:
|
---|
412 | fSleepTime = self._fTargetTime - time.time()
|
---|
413 | if fSleepTime > 0:
|
---|
414 | time.sleep(fSleepTime)
|
---|
415 | Test_Singleton.getInstance()
|
---|
416 | except Exception, e:
|
---|
417 | self._eException = e
|
---|
418 |
|
---|
419 | fTargetTime = time.time() + 0.1
|
---|
420 | lstThreads = []
|
---|
421 | for _ in xrange(100):
|
---|
422 | t = Test_SingletonThread(fTargetTime)
|
---|
423 | t.start()
|
---|
424 | lstThreads.append(t)
|
---|
425 | eException = None
|
---|
426 | for t in lstThreads:
|
---|
427 | t.join()
|
---|
428 | if t._eException and not eException:
|
---|
429 | eException = t._eException
|
---|
430 | if eException:
|
---|
431 | raise eException
|
---|
432 |
|
---|
433 | def testNoInit(self):
|
---|
434 | """
|
---|
435 | Demonstrates use with a class not defining __init__
|
---|
436 | """
|
---|
437 |
|
---|
438 | class A(Singleton):
|
---|
439 | pass
|
---|
440 |
|
---|
441 | #INTENTIONALLY UNDEFINED:
|
---|
442 | #def __init__(self):
|
---|
443 | # super(A, self).__init__()
|
---|
444 |
|
---|
445 | A.getInstance() #Make sure no exception is raised
|
---|
446 |
|
---|
447 | def testMultipleGetInstancesWithArgs(self):
|
---|
448 |
|
---|
449 | class A(Singleton):
|
---|
450 |
|
---|
451 | ignoreSubsequent = True
|
---|
452 |
|
---|
453 | def __init__(self, a, b=1):
|
---|
454 | pass
|
---|
455 |
|
---|
456 | a1 = A.getInstance(1)
|
---|
457 | a2 = A.getInstance(2) # ignores the second call because of ignoreSubsequent
|
---|
458 |
|
---|
459 | class B(Singleton):
|
---|
460 |
|
---|
461 | def __init__(self, a, b=1):
|
---|
462 | pass
|
---|
463 |
|
---|
464 | b1 = B.getInstance(1)
|
---|
465 | self.assertRaises(SingletonException, B.getInstance, 2) # No ignoreSubsequent included
|
---|
466 |
|
---|
467 | class C(Singleton):
|
---|
468 |
|
---|
469 | def __init__(self, a=1):
|
---|
470 | pass
|
---|
471 |
|
---|
472 | c1 = C.getInstance(a=1)
|
---|
473 | self.assertRaises(SingletonException, C.getInstance, a=2) # No ignoreSubsequent included
|
---|
474 |
|
---|
475 | def testInheritance(self):
|
---|
476 | """
|
---|
477 | It's sometimes said that you can't subclass a singleton (see, for instance,
|
---|
478 | http://steve.yegge.googlepages.com/singleton-considered-stupid point e). This
|
---|
479 | test shows that at least rudimentary subclassing works fine for us.
|
---|
480 | """
|
---|
481 |
|
---|
482 | class A(Singleton):
|
---|
483 |
|
---|
484 | def setX(self, x):
|
---|
485 | self.x = x
|
---|
486 |
|
---|
487 | def setZ(self, z):
|
---|
488 | raise NotImplementedError
|
---|
489 |
|
---|
490 | class B(A):
|
---|
491 |
|
---|
492 | def setX(self, x):
|
---|
493 | self.x = -x
|
---|
494 |
|
---|
495 | def setY(self, y):
|
---|
496 | self.y = y
|
---|
497 |
|
---|
498 | a = A.getInstance()
|
---|
499 | a.setX(5)
|
---|
500 | b = B.getInstance()
|
---|
501 | b.setX(5)
|
---|
502 | b.setY(50)
|
---|
503 | self.assertEqual((a.x, b.x, b.y), (5, -5, 50))
|
---|
504 | self.assertRaises(AttributeError, eval, 'a.setY', {}, locals())
|
---|
505 | self.assertRaises(NotImplementedError, b.setZ, 500)
|
---|
506 |
|
---|
507 | unittest.main()
|
---|
508 |
|
---|