Source code for soma.singleton
# -*- coding: utf-8 -*-
'''
Singleton pattern.
'''
import atexit
__docformat__ = 'restructuredtext en'
[docs]
class Singleton(object):
'''
Implements the singleton pattern. A class deriving from ``Singleton`` can
have only one instance. The first instantiation will create an object and
other instantiations return the same object. Note that the :meth:`__init__`
method (if any) is still called at each instantiation (on the same object).
Therefore, :class:`Singleton` derived classes should define
:meth:`__singleton_init__`
instead of :py:meth:`__init__` because the former is only called once.
Example::
from singleton import Singleton
class MyClass(Singleton):
def __singleton_init__(self):
self.attribute = 'value'
o1 = MyClass()
o2 = MyClass()
print(o1 is o2)
A Singleton subclass will inherit Singleton.
In a multiple inheritance situation, the subclass should preferably inherit
Singleton **first**, so that ``Singleton.__new__()`` will be called and the
singleton machinery will be activated.
However, in some situations another parent will ask to be inherited first,
like in Qt: QObject should be inherited first, at least in PyQt6. In that
case, ``QObject.__new__`` will be called instead, and the singleton
mechanism will be skipped: this will fail. The solution is to overload the
``__new__`` method in the subclass, and force the singleton system again,
as done in ``Singleton.__new__``, but ``calling QObject.__new__`` to
actually instantiate the object, then using :meth:`_post_new_`. The typical
example is :class:`qtThead.QtThreadedCall`.
Example::
class QtThreadCall(QObject, Singleton):
def __new__(cls, *args, **kwargs):
if '_singleton_instance' not in cls.__dict__:
cls._singleton_instance = QObject.__new__(cls)
cls._post_new_(cls, *args, **kwargs)
return cls._singleton_instance
'''
@classmethod
def get_instance(cls):
try:
return getattr(cls, '_singleton_instance')
except AttributeError:
msg = "Class %s has not been initialized" % cls.__name__
raise ValueError(msg)
def __new__(cls, *args, **kwargs):
if '_singleton_instance' not in cls.__dict__:
cls._singleton_instance = super(Singleton, cls).__new__(cls)
cls._post_new_(cls, *args, **kwargs)
return cls._singleton_instance
@staticmethod
def _post_new_(cls, *args, **kwargs):
''' This method is called from __new__. It is separated in order to
make it available for subclasses which would also overload __new__.
See the doc of Singleton for an explanation.
'''
singleton_init = getattr(cls._singleton_instance,
'__singleton_init__', None)
if singleton_init is not None:
singleton_init(*args, **kwargs)
atexit.register(cls.delete_singleton)
return cls._singleton_instance
def __init__(self, *args, **kwargs):
'''
The __init__ method of :py:class:`Singleton` derived class should do
nothing.
Derived classes must define :py:meth:`__singleton_init__` instead of
__init__.
'''
def __singleton_init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@classmethod
def delete_singleton(cls):
if hasattr(cls, '_singleton_instance'):
del cls._singleton_instance