mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +03:00
Asyncio daemon tutorial (#278)
* Fix a typo in the docblock of the Configuration provider * Update the changelog * Add tutorial sections * Switch to use https for httpbin.org requests * Add what we are going to build section * Fix ``Makefile`` to run ``aiohttp`` integration tests on Python 3.5+ * Add prerequisities and prepare the env sections * Add logging, config and the dispacher sections * Change logging * Fix multiple typos in the ``flask`` and ``aiohttp`` tutorials * Add the initial and dirty version * Fix multiple typos in the ``flask`` and ``aiohttp`` tutorials * Fix the 3.27.0 changelog * Finish all the parts before the dispatcher * Finish dispatcher section * Update http monitor logging format * Finish the tutorial * Fix docblock in the dispatcher module * Update changelog * Add meta keywords and description
This commit is contained in:
parent
2ff36b44ab
commit
3a61457be7
2
Makefile
2
Makefile
|
@ -55,7 +55,7 @@ test-py2: build
|
|||
test-py3: build
|
||||
# Unit tests with coverage report
|
||||
coverage erase
|
||||
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*py3.py
|
||||
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*py3*.py
|
||||
coverage report --rcfile=./.coveragerc
|
||||
coverage html --rcfile=./.coveragerc
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ Dependency Injector --- Dependency injection framework for Python
|
|||
:keywords: Python,Dependency injection,DI,Inversion of Control,IoC,
|
||||
IoC Container,Factory, Singleton, Design Patterns
|
||||
:description: Dependency Injector is a dependency injection framework
|
||||
for Python. It was designed to be unified, developer-friendly
|
||||
for Python. It helps to maintain you application structure.
|
||||
It was designed to be unified, developer-friendly
|
||||
tool that helps to implement dependency injection design
|
||||
pattern in formal, pretty, Pythonic way. Dependency Injector
|
||||
provides implementations of such popular design patterns
|
||||
|
|
|
@ -131,8 +131,10 @@ What's next?
|
|||
|
||||
Choose one of the following as a next step:
|
||||
|
||||
+ Pass the dependency injection :ref:`flask-tutorial`.
|
||||
+ Look at the other dependency injection :ref:`tutorials`.
|
||||
+ Pass one of the dependency injection tutorials:
|
||||
+ :ref:`flask-tutorial`
|
||||
+ :ref:`aiohttp-tutorial`
|
||||
+ :ref:`asyncio-daemon-tutorial`
|
||||
+ Know more about the :ref:`providers`.
|
||||
+ Go to the :ref:`contents`.
|
||||
|
||||
|
|
|
@ -7,9 +7,18 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
Development version
|
||||
-------------------
|
||||
- Add ``asyncio`` + ``Dependency Injector`` example ``monitoring-daemon-asyncio``.
|
||||
- Add ``asyncio`` + ``Dependency Injector`` monitoring daemon tutorial.
|
||||
- Fix a typo in the docblock of the ``Configuration`` provider.
|
||||
- Fix multiple typos in the ``flask`` and ``aiohttp`` tutorials.
|
||||
- Fix ``Makefile`` to run ``aiohttp`` integration tests on Python 3.5+.
|
||||
|
||||
3.27.0
|
||||
------
|
||||
- Add deep init injections overriding for ``Factory`` provider.
|
||||
- Add ``asyncio`` monitoring daemon example.
|
||||
|
||||
3.26.0
|
||||
------
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
.. _aiohttp-tutorial:
|
||||
|
||||
Aiohttp tutorial
|
||||
================
|
||||
|
||||
.. _aiohttp-tutorial:
|
||||
.. meta::
|
||||
:keywords: Python,Aiohttp,Tutorial,Education,Web,API,REST API,Example,DI,Dependency injection,
|
||||
IoC,Inversion of control,Refactoring,Tests,Unit tests,Pytest,py.test,Bootstrap,
|
||||
HTML,CSS
|
||||
:description: This tutorial shows how to build an aiohttp application following the dependency
|
||||
injection principle. You will create the REST API application, connect to the
|
||||
Giphy API, cover it with the unit test and make some refactoring.
|
||||
|
||||
This tutorials shows how to build an ``aiohttp`` REST API application following the dependency
|
||||
This tutorial shows how to build an ``aiohttp`` REST API application following the dependency
|
||||
injection principle.
|
||||
|
||||
Start from the scratch or jump to the section:
|
||||
|
@ -908,9 +916,9 @@ You should see:
|
|||
Conclusion
|
||||
----------
|
||||
|
||||
In this tutorial we've build an ``aiohttp`` REST API application following the dependency
|
||||
In this tutorial we've built an ``aiohttp`` REST API application following the dependency
|
||||
injection principle.
|
||||
We've used ``Dependency Injector`` as a dependency injection framework.
|
||||
We've used the ``Dependency Injector`` as a dependency injection framework.
|
||||
|
||||
The benefit you get with the ``Dependency Injector`` is the container. It starts to payoff
|
||||
when you need to understand or change your application structure. It's easy with the container,
|
||||
|
|
1085
docs/tutorials/asyncio-daemon.rst
Normal file
1085
docs/tutorials/asyncio-daemon.rst
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
|||
Asyncio tutorial
|
||||
================
|
||||
|
||||
Coming soon...
|
||||
|
||||
.. disqus::
|
BIN
docs/tutorials/asyncio_images/class_1.png
Normal file
BIN
docs/tutorials/asyncio_images/class_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
docs/tutorials/asyncio_images/class_2.png
Normal file
BIN
docs/tutorials/asyncio_images/class_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
docs/tutorials/asyncio_images/diagram.png
Normal file
BIN
docs/tutorials/asyncio_images/diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
|
@ -3,7 +3,15 @@
|
|||
Flask tutorial
|
||||
==============
|
||||
|
||||
This tutorials shows how to build ``Flask`` application following the dependency injection
|
||||
.. meta::
|
||||
:keywords: Python,Flask,Tutorial,Education,Web,Example,DI,Dependency injection,IoC,
|
||||
Inversion of control,Refactoring,Tests,Unit tests,Pytest,py.test,Bootstrap,
|
||||
HTML,CSS
|
||||
:description: This tutorial shows how to build a Flask application following the dependency
|
||||
injection principle. You will create the web application, connect to the Github
|
||||
API, cover it with unit the test and make some refactoring.
|
||||
|
||||
This tutorial shows how to build a ``Flask`` application following the dependency injection
|
||||
principle.
|
||||
|
||||
Start from the scratch or jump to the section:
|
||||
|
@ -1085,8 +1093,8 @@ Conclusion
|
|||
|
||||
We are done.
|
||||
|
||||
In this tutorial we've build ``Flask`` application following the dependency injection principle.
|
||||
We've used ``Dependency Injector`` as a dependency injection framework.
|
||||
In this tutorial we've built a ``Flask`` application following the dependency injection principle.
|
||||
We've used the ``Dependency Injector`` as a dependency injection framework.
|
||||
|
||||
The main part of this application is the container. It keeps all the application components and
|
||||
their dependencies in one place:
|
||||
|
|
|
@ -11,6 +11,6 @@ frameworks.
|
|||
|
||||
flask
|
||||
aiohttp
|
||||
asyncio
|
||||
asyncio-daemon
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -25,11 +25,31 @@ The output should be something like:
|
|||
|
||||
Starting monitoring-daemon-asyncio_monitor_1 ... done
|
||||
Attaching to monitoring-daemon-asyncio_monitor_1
|
||||
monitor_1 | [2020-08-06 01:57:08,249] [INFO] [monitoringdaemon.dispatcher]: Dispatcher is starting up
|
||||
monitor_1 | [2020-08-06 01:57:08,249] [INFO] [monitoringdaemon.dispatcher]: Monitoring task has been started monitoringdaemon.monitors.HttpMonitor(url="http://example.com")
|
||||
monitor_1 | [2020-08-06 01:57:08,249] [INFO] [monitoringdaemon.dispatcher]: Monitoring task has been started monitoringdaemon.monitors.HttpMonitor(url="http://httpbin.org/get")
|
||||
monitor_1 | [2020-08-06 01:57:08,318] [INFO] [monitoringdaemon.monitors.HttpMonitor(url="http://example.com")]: Response code: 200, content length: 648, request took: 0.067 seconds
|
||||
monitor_1 | [2020-08-06 01:57:08,363] [INFO] [monitoringdaemon.monitors.HttpMonitor(url="http://httpbin.org/get")]: Response code: 200, content length: 309, request took: 0.112 seconds
|
||||
monitor_1 | [2020-08-08 17:04:36,655] [INFO] [Dispatcher]: Starting up
|
||||
monitor_1 | [2020-08-08 17:04:36,732] [INFO] [HttpMonitor]: Check
|
||||
monitor_1 | GET http://example.com
|
||||
monitor_1 | response code: 200
|
||||
monitor_1 | content length: 648
|
||||
monitor_1 | request took: 0.074 seconds
|
||||
monitor_1 |
|
||||
monitor_1 | [2020-08-08 17:04:36,811] [INFO] [HttpMonitor]: Check
|
||||
monitor_1 | GET https://httpbin.org/get
|
||||
monitor_1 | response code: 200
|
||||
monitor_1 | content length: 310
|
||||
monitor_1 | request took: 0.153 seconds
|
||||
monitor_1 |
|
||||
monitor_1 | [2020-08-08 17:04:41,731] [INFO] [HttpMonitor]: Check
|
||||
monitor_1 | GET http://example.com
|
||||
monitor_1 | response code: 200
|
||||
monitor_1 | content length: 648
|
||||
monitor_1 | request took: 0.067 seconds
|
||||
monitor_1 |
|
||||
monitor_1 | [2020-08-08 17:04:41,787] [INFO] [HttpMonitor]: Check
|
||||
monitor_1 | GET https://httpbin.org/get
|
||||
monitor_1 | response code: 200
|
||||
monitor_1 | content length: 310
|
||||
monitor_1 | request took: 0.122 seconds
|
||||
monitor_1 |
|
||||
|
||||
Test
|
||||
----
|
||||
|
@ -59,9 +79,9 @@ The output should be something like:
|
|||
monitoringdaemon/__init__.py 0 0 100%
|
||||
monitoringdaemon/__main__.py 9 9 0%
|
||||
monitoringdaemon/containers.py 11 0 100%
|
||||
monitoringdaemon/dispatcher.py 45 5 89%
|
||||
monitoringdaemon/dispatcher.py 43 5 88%
|
||||
monitoringdaemon/http.py 6 3 50%
|
||||
monitoringdaemon/monitors.py 29 2 93%
|
||||
monitoringdaemon/monitors.py 23 1 96%
|
||||
monitoringdaemon/tests.py 37 0 100%
|
||||
----------------------------------------------------
|
||||
TOTAL 137 19 86%
|
||||
TOTAL 129 18 86%
|
||||
|
|
|
@ -12,6 +12,6 @@ monitors:
|
|||
|
||||
httpbin:
|
||||
method: "GET"
|
||||
url: "http://httpbin.org/get"
|
||||
url: "https://httpbin.org/get"
|
||||
timeout: 5
|
||||
check_every: 5
|
||||
|
|
|
@ -9,30 +9,24 @@ from typing import List
|
|||
from .monitors import Monitor
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Dispatcher:
|
||||
|
||||
def __init__(self, monitors: List[Monitor]) -> None:
|
||||
self._monitors = monitors
|
||||
self._monitor_tasks: List[asyncio.Task] = []
|
||||
self._logger = logging.getLogger(self.__class__.__name__)
|
||||
self._stopping = False
|
||||
|
||||
def run(self) -> None:
|
||||
asyncio.run(self.start())
|
||||
|
||||
async def start(self) -> None:
|
||||
logger.info('Dispatcher is starting up')
|
||||
self._logger.info('Starting up')
|
||||
|
||||
for monitor in self._monitors:
|
||||
self._monitor_tasks.append(
|
||||
asyncio.create_task(self._run_monitor(monitor)),
|
||||
)
|
||||
logger.info(
|
||||
'Monitoring task has been started %s',
|
||||
monitor.full_name,
|
||||
)
|
||||
|
||||
asyncio.get_event_loop().add_signal_handler(signal.SIGTERM, self.stop)
|
||||
asyncio.get_event_loop().add_signal_handler(signal.SIGINT, self.stop)
|
||||
|
@ -47,11 +41,10 @@ class Dispatcher:
|
|||
|
||||
self._stopping = True
|
||||
|
||||
logger.info('Dispatcher is shutting down')
|
||||
self._logger.info('Shutting down')
|
||||
for task, monitor in zip(self._monitor_tasks, self._monitors):
|
||||
task.cancel()
|
||||
logger.info('Monitoring task has been stopped %s', monitor.full_name)
|
||||
logger.info('Dispatcher shutting down finished successfully')
|
||||
self._logger.info('Shutdown finished successfully')
|
||||
|
||||
@staticmethod
|
||||
async def _run_monitor(monitor: Monitor) -> None:
|
||||
|
@ -67,6 +60,6 @@ class Dispatcher:
|
|||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception:
|
||||
monitor.logger.exception('Error running monitoring check')
|
||||
monitor.logger.exception('Error executing monitor check')
|
||||
|
||||
await asyncio.sleep(_until_next(last=time_start))
|
||||
|
|
|
@ -11,11 +11,7 @@ class Monitor:
|
|||
|
||||
def __init__(self, check_every: int) -> None:
|
||||
self.check_every = check_every
|
||||
self.logger = logging.getLogger(self.full_name)
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
raise NotImplementedError()
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
async def check(self) -> None:
|
||||
raise NotImplementedError()
|
||||
|
@ -34,10 +30,6 @@ class HttpMonitor(Monitor):
|
|||
self._timeout = options.pop('timeout')
|
||||
super().__init__(check_every=options.pop('check_every'))
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
return '{0}.{1}(url="{2}")'.format(__name__, self.__class__.__name__, self._url)
|
||||
|
||||
async def check(self) -> None:
|
||||
time_start = time.time()
|
||||
|
||||
|
@ -51,7 +43,13 @@ class HttpMonitor(Monitor):
|
|||
time_took = time_end - time_start
|
||||
|
||||
self.logger.info(
|
||||
'Response code: %s, content length: %s, request took: %s seconds',
|
||||
'Check\n'
|
||||
' %s %s\n'
|
||||
' response code: %s\n'
|
||||
' content length: %s\n'
|
||||
' request took: %s seconds\n',
|
||||
self._method,
|
||||
self._url,
|
||||
response.status,
|
||||
response.content_length,
|
||||
round(time_took, 3)
|
||||
|
|
|
@ -32,7 +32,7 @@ def container():
|
|||
},
|
||||
'httpbin': {
|
||||
'method': 'GET',
|
||||
'url': 'http://fake-httpbin.org/get',
|
||||
'url': 'https://fake-httpbin.org/get',
|
||||
'timeout': 1,
|
||||
'check_every': 1,
|
||||
},
|
||||
|
@ -56,7 +56,7 @@ async def test_example_monitor(container, caplog):
|
|||
await example_monitor.check()
|
||||
|
||||
assert 'http://fake-example.com' in caplog.text
|
||||
assert 'Response code: 200' in caplog.text
|
||||
assert 'response code: 200' in caplog.text
|
||||
assert 'content length: 635' in caplog.text
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1239,6 +1239,7 @@ cdef class Configuration(Object):
|
|||
"""Configuration provider provides configuration options to the other providers.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
config = Configuration('config')
|
||||
print(config.section1.option1()) # None
|
||||
print(config.section1.option2()) # None
|
||||
|
|
Loading…
Reference in New Issue
Block a user