Usage¶
One of the simplest ways to use singletons
is using a factory decorator to make the return value of a function a singleton object. Create a shared.py
file:
import uuid
import singletons
@singletons.GlobalFactory
def my_uuid():
return uuid.uuid4()
Any time you want to access the instance generated by your factory, just call the my_uuid()
function.
Factory decorators include:
GlobalFactory
ProcessFactory
ThreadFactory
GreenthreadFactory
EventletFactory
GeventFactory
You can also declare a class as a singleton by using the metaclass
keyword argument:
import singletons
class SharedCache(dict, metaclass=singletons.ThreadSingleton):
pass
When SharedCache
is called (using SharedCache()
), if an object already exists for the current thread it is returned, otherwise it is constructed.
Singleton metaclasses include:
Singleton
ProcessSingleton
ThreadSingleton
GreenthreadSingleton
EventletSingleton
GeventSingleton
Writing Tests¶
A common need when working with singletons is to be able to use Mock objects for unit tests. singletons
includes a helper class for making modules easily swappable to use Mocks for everything instead of the factories/classes defined. A common usage would be to put these lines at the bottom of your shared.py
file:
class _Shared(singletons.SharedModule):
globals = globals()
sys.modules[__name__] = _Shared()
To enable the Mock object replacement, call setup_mock()
or set the environment variable SINGLETONS_SETUP_MOCK=1
. This will replace all accesses of module attributes with Mock() instances. setup_mock
can be called inside a TestCase setup()
method or as part of a pytest fixture to ensure that each test has a clean set of Mock() instances.
Example test:
class MyTestCase(unittest.TestCase):
def setup(self):
shared.setup_mock()
def test_get_documents():
c = shared.session()
# do thing
c.request.assert_called_once()
To use custom Mock objects, set them as attributes on the module after calling setup_mock
:
class MyTestCase(unittest.TestCase):
def setup(self):
shared.setup_mock()
mock_instance = mock.Mock(spec=User)
mock_instance.name = 'Jane Doe'
mock_instance.username = 'jdoe123'
shared.mock_instance = mock_instance
def test_get_userdata():
c = shared.mock_instance()
# do thing
c.request.assert_called_once()