| #!/usr/bin/env python |
| # |
| # Copyright 2007 Google Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| # Copyright 2011 Google Inc. All Rights Reserved. |
| |
| """Thread-safe implementation of a singleton decorator. |
| |
| Sometimes, only a single instance of a class should ever be created. This file |
| provides a wrapper that turns a class into a Singleton. The constructor may |
| only be called once; a static method Singleton() provides access to the |
| constructed instance. If Singleton() is called before the constructor, or if |
| the constructor is called multiple times, Errors are raised. This wrapper is |
| thread-safe; calls to the constructor and Singleton() method are protected |
| by per-class locks. |
| |
| Singletons are often associated with bad coding practices; see |
| https://wiki/Main/SingletonsConsideredDangerous and decide if you should |
| really be using this functionality. Consider alternatives, like the |
| "Borg pattern" where object state (instead of object identity) is shared. |
| |
| To make your singletons more testable, use the following idiom: |
| |
| |
| class A(...): |
| "All the complicated code goes in here, can be tested normally..." |
| .. |
| .. |
| |
| |
| @singleton.Singleton |
| class B(A): |
| "Singleton instance of A" |
| |
| |
| Example usage: |
| |
| from google.pyglib import singleton |
| |
| |
| @singleton.Singleton |
| class Foo(object): |
| "Example singleton" |
| |
| |
| a = Foo() |
| b = Foo.Singleton() |
| c = Foo.Singleton() |
| assert a == b |
| assert b == c |
| |
| |
| Note that class decorator syntax was added after python2.4. If your code |
| is python2.4 compliant, use an idiom like: |
| |
| from google.pyglib import singleton |
| |
| |
| class Foo(object): |
| "Foo class" |
| |
| Bar = singleton.Singleton(Foo) |
| |
| |
| a = Bar() |
| b = Bar.Singleton() |
| c = Bar.Singleton() |
| assert a == b |
| assert b == c |
| """ |
| |
| |
| |
| import threading |
| |
| _CLASS_LOCKS = {} # Holds the per-class locks. |
| _CLASS_LOCKS_LOCK = threading.Lock() # Lock for obtaining a per-class lock. |
| _INSTANCES = {} # Mapping from class to instantiated object. |
| |
| |
| class Error(Exception): |
| """Base error class.""" |
| |
| |
| class AlreadyHasSingletonMethodError(Error): |
| """Raised if the class already defines a Singleton() method.""" |
| |
| def __init__(self, cls): |
| Error.__init__(self) |
| self.cls = cls |
| |
| def __str__(self): |
| return 'Class already has a Singleton() method: %s' % self.cls |
| |
| |
| class NotConstructedError(Error): |
| """Raised if the constructor has not been called yet.""" |
| |
| def __init__(self, cls): |
| Error.__init__(self) |
| self.cls = cls |
| |
| def __str__(self): |
| return 'Constructor has not yet been called for class %s' % self.cls |
| |
| |
| class ConstructorCalledAgainError(Error): |
| """Raised if the constructor is called twice for a singleton.""" |
| |
| def __init__(self, cls, args, kws): |
| Error.__init__(self) |
| self.cls = cls |
| self.args = args |
| self.kws = kws |
| |
| def __str__(self): |
| return ('Constructor called (again) on class %s with args %s and kws %s' |
| % (self.cls, self.args, self.kws)) |
| |
| |
| def _GetClassLock(cls): |
| """Returns the lock associated with the class.""" |
| with _CLASS_LOCKS_LOCK: |
| if cls not in _CLASS_LOCKS: |
| _CLASS_LOCKS[cls] = threading.Lock() |
| return _CLASS_LOCKS[cls] |
| |
| |
| def Singleton(cls): |
| """Turn a class into a singleton. |
| |
| One call to the constructor is allowed. After that, all future calls |
| must be to the Singleton() static method. |
| |
| This code is multithread-safe. Shared locks are held for brief periods |
| of time; when a class is instantiated, it uses a class specific lock. |
| |
| Args: |
| cls: The class to decorate. |
| |
| Returns: |
| The singleton class. Note that this class is an extension |
| of the class it is decorating. |
| |
| Raises: |
| AlreadyHasSingletonMethodError: If the class has a Singleton method |
| defined. |
| """ |
| if hasattr(cls, 'Singleton'): |
| raise AlreadyHasSingletonMethodError(cls) |
| |
| class _Singleton(cls): |
| |
| def __init__(self, *args, **kws): |
| class_lock = _GetClassLock(cls) |
| with class_lock: |
| if cls not in _INSTANCES: |
| cls.__init__(self, *args, **kws) |
| _INSTANCES[cls] = self |
| else: |
| raise ConstructorCalledAgainError(cls, args, kws) |
| |
| @staticmethod |
| def Singleton(): |
| class_lock = _GetClassLock(cls) |
| with class_lock: |
| if cls not in _INSTANCES: |
| raise NotConstructedError(cls) |
| else: |
| return _INSTANCES[cls] |
| |
| return _Singleton |