mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-07 07:00:49 +03:00
Singleton docs update (#288)
* Update docblocks of factory provider examples * Edit singleton docs
This commit is contained in:
parent
520945483f
commit
0bb30f91ef
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
|
@ -1,5 +1,7 @@
|
|||
.. _factory-provider:
|
||||
|
||||
Factory provider
|
||||
----------------
|
||||
================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Factory,Abstract Factory,
|
||||
|
@ -100,6 +102,8 @@ attribute of the provider that you're going to inject.
|
|||
|
||||
.. note:: Any provider has a ``.provider`` attribute.
|
||||
|
||||
.. _factory-specialize-provided-type:
|
||||
|
||||
Specializing the provided type
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -112,6 +116,8 @@ class attribute.
|
|||
:lines: 3-
|
||||
:emphasize-lines: 12-14
|
||||
|
||||
.. _abstract-factory:
|
||||
|
||||
Abstract factory
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -1,111 +1,100 @@
|
|||
Singleton providers
|
||||
-------------------
|
||||
Singleton provider
|
||||
------------------
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Singleton,Pattern,Example,
|
||||
Threads,Multithreading,Scoped
|
||||
:description: Singleton provider helps to provide a single object. This page
|
||||
demonstrates how to use a Singleton provider. It also provides the example
|
||||
of using a singleton and thread locals singleton in the multi-threaded
|
||||
environment.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Singleton` provider creates new instance of specified class on
|
||||
first call and returns same instance on every next call.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/providers/singleton.png
|
||||
:width: 80%
|
||||
:align: center
|
||||
:py:class:`Singleton` provider provides single object. It memorizes the first created object and
|
||||
returns it on the rest of the calls.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/singleton.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
Singleton providers resetting
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Created and memorized by :py:class:`Singleton` instance can be reset. Reset of
|
||||
:py:class:`Singleton`'s memorized instance is done by clearing reference to
|
||||
it. Further lifecycle of memorized instance is out of :py:class:`Singleton`
|
||||
provider's control and depends on garbage collection strategy.
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/singleton_resetting.py
|
||||
:language: python
|
||||
|
||||
Singleton providers and injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:class:`Singleton` provider has same interface as :py:class:`Factory`
|
||||
provider, so, all of the rules about injections are the same, as for
|
||||
:py:class:`Factory` provider.
|
||||
``Singleton`` provider handles an injection of the dependencies the same way like a
|
||||
:ref:`factory-provider`.
|
||||
|
||||
.. note::
|
||||
|
||||
Due that :py:class:`Singleton` provider creates specified class instance
|
||||
only on the first call, all injections are done once, during the first
|
||||
call. Every next call, while instance has been already created
|
||||
and memorized, no injections are done, :py:class:`Singleton` provider just
|
||||
returns memorized earlier instance.
|
||||
``Singleton`` provider does dependencies injection only when creates the object. When the object
|
||||
is created and memorized ``Singleton`` provider just returns it without applying the injections.
|
||||
|
||||
This may cause some problems, for example, in case of trying to bind
|
||||
:py:class:`Factory` provider with :py:class:`Singleton` provider (provided
|
||||
by dependent :py:class:`Factory` instance will be injected only once,
|
||||
during the first call). Be aware that such behaviour was made with opened
|
||||
eyes and is not a bug.
|
||||
Specialization of the provided type and abstract singletons work the same like like for the
|
||||
factories:
|
||||
|
||||
By the way, in such case, :py:class:`Delegate` or
|
||||
:py:class:`DelegatedSingleton` provider can be useful
|
||||
. It makes possible to inject providers *as is*. Please check out
|
||||
`Singleton providers delegation`_ section.
|
||||
- :ref:`factory-specialize-provided-type`
|
||||
- :ref:`abstract-factory`
|
||||
|
||||
Singleton providers delegation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Resetting memorized object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:class:`Singleton` provider could be delegated to any other provider via
|
||||
any kind of injection.
|
||||
To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton``
|
||||
provider.
|
||||
|
||||
Delegation of :py:class:`Singleton` providers is the same as
|
||||
:py:class:`Factory` providers delegation, please follow
|
||||
:ref:`factory_providers_delegation` section for examples (with exception
|
||||
of using :py:class:`DelegatedSingleton` instead of
|
||||
:py:class:`DelegatedFactory`).
|
||||
.. literalinclude:: ../../examples/providers/singleton_resetting.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 14
|
||||
|
||||
Singleton providers specialization
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. note::
|
||||
Resetting of the memorized object clears the reference to it. Further object's lifecycle is
|
||||
managed by the garbage collector.
|
||||
|
||||
:py:class:`Singleton` provider could be specialized for any kind of needs via
|
||||
declaring its subclasses.
|
||||
Using singleton with multiple threads
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Specialization of :py:class:`Singleton` providers is the same as
|
||||
:py:class:`Factory` providers specialization, please follow
|
||||
:ref:`factory_providers_specialization` section for examples.
|
||||
``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for
|
||||
using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap
|
||||
into the race condition problem: ``Singleton`` will create multiple objects.
|
||||
|
||||
Abstract singleton providers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
There are two thread-safe singleton implementations out of the box:
|
||||
|
||||
:py:class:`AbstractSingleton` provider is a :py:class:`Singleton` provider that
|
||||
must be explicitly overridden before calling.
|
||||
|
||||
Behaviour of :py:class:`AbstractSingleton` providers is the same as of
|
||||
:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers`
|
||||
section for examples (with exception of using :py:class:`AbstractSingleton`
|
||||
provider instead of :py:class:`AbstractFactory`).
|
||||
|
||||
Singleton providers and multi-threading
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:class:`Singleton` provider is NOT thread-safe and should be used in
|
||||
multi-threading applications with manually controlled locking.
|
||||
|
||||
:py:class:`ThreadSafeSingleton` is a thread-safe version of
|
||||
:py:class:`Singleton` and could be used in multi-threading applications
|
||||
without any additional locking.
|
||||
|
||||
Also there could be a need to use thread-scoped singletons and there is a
|
||||
special provider for such case - :py:class:`ThreadLocalSingleton`.
|
||||
:py:class:`ThreadLocalSingleton` provider creates instance once for each
|
||||
thread and returns it on every call.
|
||||
|
||||
Example:
|
||||
+ :py:class:`ThreadSafeSingleton` - is a thread-safe version of a ``Singleton`` provider. You can use
|
||||
in multi-threading applications without additional synchronization.
|
||||
+ :py:class:`ThreadLocalSingleton` - is a singleton provider that uses thread-locals as a storage.
|
||||
This type of singleton will manage multiple objects - the one object for the one thread.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/singleton_thread_locals.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 11,12
|
||||
|
||||
Implementing scopes
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To implement a scoped singleton provider use a ``Singleton`` provider and reset its scope when
|
||||
needed.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/singleton_scoped.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
The output should look like this (each request a ``Service`` object has a different address):
|
||||
|
||||
.. code-block::
|
||||
|
||||
* Serving Flask app "singleton_scoped" (lazy loading)
|
||||
* Environment: production
|
||||
WARNING: This is a development server. Do not use it in a production deployment.
|
||||
Use a production WSGI server instead.
|
||||
* Debug mode: off
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
<__main__.Service object at 0x1099a9d90>
|
||||
127.0.0.1 - - [25/Aug/2020 17:33:11] "GET / HTTP/1.1" 200 -
|
||||
<__main__.Service object at 0x1099a9cd0>
|
||||
127.0.0.1 - - [25/Aug/2020 17:33:17] "GET / HTTP/1.1" 200 -
|
||||
<__main__.Service object at 0x1099a9d00>
|
||||
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
|
||||
<__main__.Service object at 0x1099a9e50>
|
||||
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
|
||||
<__main__.Service object at 0x1099a9d90>
|
||||
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"""`Factory` providers example."""
|
||||
"""`Factory` provider example."""
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"""`Factory` providers delegation example."""
|
||||
"""`Factory` provider delegation example."""
|
||||
|
||||
from typing import Callable, List
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"""`Factory` providers init injections example."""
|
||||
"""`Factory` provider init injections example."""
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"""`Factory` providers - building a complex object graph with deep init injections example."""
|
||||
"""`Factory` provider - passing injections to the underlying providers example."""
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
"""`Singleton` providers example."""
|
||||
"""`Singleton` provider example."""
|
||||
|
||||
import collections
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
UsersService = collections.namedtuple('UsersService', [])
|
||||
class UserService:
|
||||
...
|
||||
|
||||
# Singleton provider creates new instance of specified class on first call
|
||||
# and returns same instance on every next call.
|
||||
users_service_provider = providers.Singleton(UsersService)
|
||||
|
||||
# Retrieving several UserService objects:
|
||||
users_service1 = users_service_provider()
|
||||
users_service2 = users_service_provider()
|
||||
user_service_provider = providers.Singleton(UserService)
|
||||
|
||||
# Making some asserts:
|
||||
assert users_service1 is users_service2
|
||||
|
||||
if __name__ == '__main__':
|
||||
user_service1 = user_service_provider()
|
||||
user_service2 = user_service_provider()
|
||||
assert user_service1 is user_service2
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
"""`Singleton` specialization for limitation to provided type example."""
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
import dependency_injector.errors as errors
|
||||
|
||||
|
||||
class BaseService:
|
||||
"""Base service class."""
|
||||
|
||||
|
||||
class UsersService(BaseService):
|
||||
"""Users service."""
|
||||
|
||||
|
||||
class PhotosService(BaseService):
|
||||
"""Photos service."""
|
||||
|
||||
|
||||
class ServiceProvider(providers.Singleton):
|
||||
"""Service provider."""
|
||||
|
||||
provided_type = BaseService
|
||||
|
||||
|
||||
# Creating several service providers with BaseService instances:
|
||||
users_service_provider = ServiceProvider(UsersService)
|
||||
photos_service_provider = ServiceProvider(PhotosService)
|
||||
|
||||
# Trying to create service provider with not a BaseService instance:
|
||||
try:
|
||||
some_service_provider = ServiceProvider(object)
|
||||
except errors.Error as exception:
|
||||
print(exception)
|
||||
# <class '__main__.ServiceProvider'> can provide only
|
||||
# <class '__main__.BaseService'> instances
|
|
@ -1,27 +1,19 @@
|
|||
"""`Singleton` providers resetting example."""
|
||||
"""`Singleton` provider resetting example."""
|
||||
|
||||
import collections
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
UsersService = collections.namedtuple('UsersService', [])
|
||||
class UserService:
|
||||
...
|
||||
|
||||
# Users service singleton provider:
|
||||
users_service_provider = providers.Singleton(UsersService)
|
||||
|
||||
# Retrieving several UsersService objects:
|
||||
users_service1 = users_service_provider()
|
||||
users_service2 = users_service_provider()
|
||||
user_service_provider = providers.Singleton(UserService)
|
||||
|
||||
# Making some asserts:
|
||||
assert users_service1 is users_service2
|
||||
|
||||
# Resetting of memorized instance:
|
||||
users_service_provider.reset()
|
||||
if __name__ == '__main__':
|
||||
user_service1 = user_service_provider()
|
||||
|
||||
# Retrieving one more UserService object:
|
||||
users_service3 = users_service_provider()
|
||||
user_service_provider.reset()
|
||||
|
||||
# Making some asserts:
|
||||
assert users_service3 is not users_service1
|
||||
users_service2 = user_service_provider()
|
||||
assert users_service2 is not user_service1
|
||||
|
|
32
examples/providers/singleton_scoped.py
Normal file
32
examples/providers/singleton_scoped.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""`Singleton` - flask request scope example."""
|
||||
|
||||
from dependency_injector import providers
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class Service:
|
||||
...
|
||||
|
||||
|
||||
service_provider = providers.Singleton(Service)
|
||||
|
||||
|
||||
def index_view():
|
||||
service_1 = service_provider()
|
||||
service_2 = service_provider()
|
||||
assert service_1 is service_2
|
||||
print(service_1)
|
||||
return 'Hello World!'
|
||||
|
||||
|
||||
def teardown_context(request):
|
||||
service_provider.reset()
|
||||
return request
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.add_url_rule('/', 'index', view_func=index_view)
|
||||
app.after_request(teardown_context)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
|
@ -1,53 +1,42 @@
|
|||
"""`ThreadLocalSingleton` providers example."""
|
||||
"""`ThreadLocalSingleton` provider example."""
|
||||
|
||||
import threading
|
||||
import queue
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
def example(example_object, queue_object):
|
||||
"""Put provided object in the provided queue."""
|
||||
def put_in_queue(example_object, queue_object):
|
||||
queue_object.put(example_object)
|
||||
|
||||
|
||||
# Create thread-local singleton provider for some object (main thread):
|
||||
thread_local_object = providers.ThreadLocalSingleton(object)
|
||||
|
||||
# Create singleton provider for thread-safe queue:
|
||||
queue_factory = providers.ThreadSafeSingleton(queue.Queue)
|
||||
|
||||
# Create callable provider for example(), inject dependencies:
|
||||
example = providers.DelegatedCallable(
|
||||
example,
|
||||
queue_provider = providers.ThreadSafeSingleton(queue.Queue)
|
||||
put_in_queue = providers.Callable(
|
||||
put_in_queue,
|
||||
example_object=thread_local_object,
|
||||
queue_object=queue_factory,
|
||||
queue_object=queue_provider,
|
||||
)
|
||||
thread_factory = providers.Factory(
|
||||
threading.Thread,
|
||||
target=put_in_queue.provider,
|
||||
)
|
||||
|
||||
# Create factory for threads that are targeted to execute example():
|
||||
thread_factory = providers.Factory(threading.Thread, target=example)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Create 10 threads for concurrent execution of example():
|
||||
threads = []
|
||||
for thread_number in range(10):
|
||||
threads.append(
|
||||
thread_factory(name='Thread{0}'.format(thread_number)),
|
||||
)
|
||||
|
||||
# Start execution of all created threads:
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
|
||||
# Wait while threads would complete their work:
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
# Making some asserts (main thread):
|
||||
all_objects = set()
|
||||
|
||||
while not queue_factory().empty():
|
||||
all_objects.add(queue_factory().get())
|
||||
while not queue_provider().empty():
|
||||
all_objects.add(queue_provider().get())
|
||||
|
||||
assert len(all_objects) == len(threads)
|
||||
# Queue contains same number of objects as number of threads where
|
||||
|
|
Loading…
Reference in New Issue
Block a user