8000 Edit singleton docs · bimec/python-dependency-injector@cdb858d · GitHub
[go: up one dir, main page]

Skip to content

Commit cdb858d

Browse files
committed
Edit singleton docs
1 parent c5b4b7b commit cdb858d

File tree

7 files changed

+144
-174
lines changed

7 files changed

+144
-174
lines changed

docs/providers/factory.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
.. _factory-provider:
2+
13
Factory provider
2-
----------------
4+
================
35

46
.. meta::
57
: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.
100102

101103
.. note:: Any provider has a ``.provider`` attribute.
102104

105+
.. _factory-specialize-provided-type:
106+
103107
Specializing the provided type
104108
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105109

@@ -112,6 +116,8 @@ class attribute.
112116
:lines: 3-
113117
:emphasize-lines: 12-14
114118

119+
.. _abstract-factory:
120+
115121
Abstract factory
116122
~~~~~~~~~~~~~~~~
117123

docs/providers/singleton.rst

Lines changed: 73 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,100 @@
1-
Singleton providers
2-
-------------------
1+
Singleton provider
2+
------------------
33

4-
.. currentmodule:: dependency_injector.providers
5-
6-
:py:class:`Singleton` provider creates new instance of specified class on
7-
first call and returns same instance on every next call.
4+
.. meta::
5+
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Singleton,Pattern,Example,
6+
Threads,Multithreading,Scoped
7+
:description: Singleton provider helps to provide a single object. This page
8+
demonstrates how to use a Singleton provider. It also provides the example
9+
of using a singleton and thread locals singleton in the multi-threaded
10+
environment.
811

9-
Example:
12+
.. currentmodule:: dependency_injector.providers
1013

11-
.. image:: /images/providers/singleton.png
12-
:width: 80%
13-
:align: center
14+
:py:class:`Singleton` provider provides single object. It memorizes the first created object and
15+
returns it on the rest of the calls.
1416

1517
.. literalinclude:: ../../examples/providers/singleton.py
1618
:language: python
19+
:lines: 3-
1720

18-
Singleton providers resetting
19-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20-
21-
Created and memorized by :py:class:`Singleton` instance can be reset. Reset of
22-
:py:class:`Singleton`'s memorized instance is done by clearing reference to
23-
it. Further lifecycle of memorized instance is out of :py:class:`Singleton`
24-
provider's control and depends on garbage collection strategy.
25-
26-
Example:
27-
28-
.. literalinclude:: ../../examples/providers/singleton_resetting.py
29-
:language: python
30-
31-
Singleton providers and injections
32-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33-
34-
:py:class:`Singleton` provider has same interface as :py:class:`Factory`
35-
provider, so, all of the rules about injections are the same, as for
36-
:py:class:`Factory` provider.
21+
``Singleton`` provider handles an injection of the dependencies the same way like a
22+
:ref:`factory-provider`.
3723

3824
.. note::
3925

40-
Due that :py:class:`Singleton` provider creates specified class instance
41-
only on the first call, all injections are done once, during the first
42-
call. Every next call, while instance has been already created
43-
and memorized, no injections are done, :py:class:`Singleton` provider just
44-
returns memorized earlier instance.
26+
``Singleton`` provider does dependencies injection only when creates the object. When the object
27+
is created and memorized ``Singleton`` provider just returns it without applying the injections.
4528

46-
This may cause some problems, for example, in case of trying to bind
47-
:py:class:`Factory` provider with :py:class:`Singleton` provider (provided
48-
by dependent :py:class:`Factory` instance will be injected only once,
49-
during the first call). Be aware that such behaviour was made with opened
50-
eyes and is not a bug.
29+
Specialization of the provided type and abstract singletons work the same like like for the
30+
factories:
5131

52-
By the way, in such case, :py:class:`Delegate` or
53-
:py:class:`DelegatedSingleton` provider can be useful
54-
. It makes possible to inject providers *as is*. Please check out
55-
`Singleton providers delegation`_ section.
32+
- :ref:`factory-specialize-provided-type`
33+
- :ref:`abstract-factory`
5634

57-
Singleton providers delegation
58-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35+
Resetting memorized object
36+
~~~~~~~~~~~~~~~~~~~~~~~~~~
5937

60-
:py:class:`Singleton` provider could be delegated to any other provider via
61-
any kind of injection.
38+
To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton``
39+
provider.
6240

63-
Delegation of :py:class:`Singleton` providers is the same as
64-
:py:class:`Factory` providers delegation, please follow
65-
:ref:`factory_providers_delegation` section for examples (with exception
66-
of using :py:class:`DelegatedSingleton` instead of
67-
:py:class:`DelegatedFactory`).
68-
69-
Singleton providers specialization
70-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
71-
72-
:py:class:`Singleton` provider could be specialized for any kind of needs via
73-
declaring its subclasses.
74-
75-
Specialization of :py:class:`Singleton` providers is the same as
76-
:py:class:`Factory` providers specialization, please follow
77-
:ref:`factory_providers_specialization` section for examples.
41+
.. literalinclude:: ../../examples/providers/singleton_resetting.py
42+
:language: python
43+
:lines: 3-
44+
:emphasize-lines: 14
7845

79-
Abstract singleton providers
80-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46+
.. note::
47+
Resetting of the memorized object clears the reference to it. Further object's lifecycle is
48+
managed by the garbage collector.
8149

82-
:py:class:`AbstractSingleton` provider is a :py:class:`Singleton` provider that
83-
must be explicitly overridden before calling.
50+
Using singleton with multiple threads
51+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8452

85-
Behaviour of :py:class:`AbstractSingleton` providers is the same as of
86-
:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers`
87-
section for examples (with exception of using :py:class:`AbstractSingleton`
88-
provider instead of :py:class:`AbstractFactory`).
53+
``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for
54+
using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap
55+
into the race condition problem: ``Singleton`` will create multiple objects.
8956

90-
Singleton providers and multi-threading
91-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57+
There are two thread-safe singleton implementations out of the box:
9258

93-
:py:class:`Singleton` provider is NOT thread-safe and should be used in
94-
multi-threading applications with manually controlled locking.
59+
+ :py:class:`ThreadSafeSingleton` - is a thread-safe version of a ``Singleton`` provider. You can use
60+
in multi-threading applications without additional synchronization.
61+
+ :py:class:`ThreadLocalSingleton` - is a singleton provider that uses thread-locals as a storage.
62+
This type of singleton will manage multiple objects - the one object for the one thread.
9563

96-
:py:class:`ThreadSafeSingleton` is a thread-safe version of
97-
:py:class:`Singleton` and could be used in multi-threading applications
98-
without any additional locking.
64+
.. literalinclude:: ../../examples/providers/singleton_thread_locals.py
65+
:language: python
66+
:lines: 3-
67+
:emphasize-lines: 11,12
9968

100-
Also there could be a need to use thread-scoped singletons and there is a
101-
special provider for such case - :py:class:`ThreadLocalSingleton`.
102-
:py:class:`ThreadLocalSingleton` provider creates instance once for each
103-
thread and returns it on every call.
69+
Implementing scopes
70+
~~~~~~~~~~~~~~~~~~~
10471

105-
Example:
72+
To implement a scoped singleton provider use a ``Singleton`` provider and reset its scope when
73+
needed.
10674

107-
.. literalinclude:: ../../examples/providers/singleton_thread_locals.py
75+
.. literalinclude:: ../../examples/providers/singleton_scoped.py
10876
:language: python
109-
77+
:lines: 3-
78+
79+
The output should look like this (each request a ``Service`` object has a different address):
80+
81+
.. code-block::
82+
83+
* Serving Flask app "singleton_scoped" (lazy loading)
84+
* Environment: production
85+
WARNING: This is a development server. Do not use it in a production deployment.
86+
Use a production WSGI server instead.
87+
* Debug mode: off
88+
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
89+
<__main__.Service object at 0x1099a9d90>
90+
127.0.0.1 - - [25/Aug/2020 17:33:11] "GET / HTTP/1.1" 200 -
91+
<__main__.Service object at 0x1099a9cd0>
92+
127.0.0.1 - - [25/Aug/2020 17:33:17] "GET / HTTP/1.1" 200 -
93+
<__main__.Service object at 0x1099a9d00>
94+
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
95+
<__main__.Service object at 0x1099a9e50>
96+
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
97+
<__main__.Service object at 0x1099a9d90>
98+
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
11099
111100
.. disqus::

examples/providers/singleton.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
"""`Singleton` providers example."""
1+
"""`Singleton` provider example."""
22

3-
import collections
3+
from dependency_injector import providers
44

5-
import dependency_injector.providers as providers
65

6+
class UserService:
7+
...
78

8-
UsersService = collections.namedtuple('UsersService', [])
99

10-
# Singleton provider creates new instance of specified class on first call
11-
# and returns same instance on every next call.
12-
users_service_provider = providers.Singleton(UsersService)
10+
user_service_provider = providers.Singleton(UserService)
1311

14-
# Retrieving several UserService objects:
15-
users_service1 = users_service_provider()
16-
users_service2 = users_service_provider()
1712

18-
# Making some asserts:
19-
assert users_service1 is users_service2
13+
if __name__ == '__main__':
14+
user_service1 = user_service_provider()
15+
user_service2 = user_service_provider()
16+
assert user_service1 is user_service2

examples/providers/singleton_provided_type.py

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
1-
"""`Singleton` providers resetting example."""
1+
"""`Singleton` provider resetting example."""
22

3-
import collections
3+
from dependency_injector import providers
44

5-
import dependency_injector.providers as providers
65

6+
class UserService:
7+
...
78

8-
UsersService = collections.namedtuple('UsersService', [])
99

10-
# Users service singleton provider:
11-
users_service_provider = providers.Singleton(UsersService)
10+
user_service_provider = providers.Singleton(UserService)
1211

13-
# Retrieving several UsersService objects:
14-
users_service1 = users_service_provider()
15-
users_service2 = users_service_provider()
1612

17-
# Making some asserts:
18-
assert users_service1 is users_service2
13+
if __name__ == '__main__':
14+
user_service1 = user_service_provider()
1915

20-
# Resetting of memorized instance:
21-
users_service_provider.reset()
16+
user_service_provider.reset()
2217

23-
# Retrieving one more UserService object:
24-
users_service3 = users_service_provider()
25-
26-
# Making some asserts:
27-
assert users_service3 is not users_service1
18+
users_service2 = user_service_provider()
19+
assert users_service2 is not user_service1
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""`Singleton` - flask request scope example."""
2+
3+
from dependency_injector import providers
4+
from flask import Flask
5+
6+
7+
class Service:
8+
...
9+
10+
11+
service_provider = providers.Singleton(Service)
12+
13+
14+
def index_view():
15+
service_1 = service_provider()
16+
service_2 = service_provider()
17+
assert service_1 is service_2
18+
print(service_1)
19+
return 'Hello World!'
20+
21+
22+
def teardown_context(request):
23+
service_provider.reset()
24+
return request
25+
26+
27+
app = Flask(__name__)
28+
app.add_url_rule('/', 'index', view_func=index_view)
29+
app.after_request(teardown_context)
30+
31+
if __name__ == '__main__':
32+
app.run()

0 commit comments

Comments
 (0)
0