mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-30 23:47:40 +03:00 
			
		
		
		
	Merge branch 'release/3.27.0' into master
This commit is contained in:
		
						commit
						2ff36b44ab
					
				|  | @ -7,6 +7,10 @@ that were made in every particular version. | ||||||
| From version 0.7.6 *Dependency Injector* framework strictly  | From version 0.7.6 *Dependency Injector* framework strictly  | ||||||
| follows `Semantic versioning`_ | follows `Semantic versioning`_ | ||||||
| 
 | 
 | ||||||
|  | 3.27.0 | ||||||
|  | ------ | ||||||
|  | - Add deep init injections overriding for ``Factory`` provider. | ||||||
|  | 
 | ||||||
| 3.26.0 | 3.26.0 | ||||||
| ------ | ------ | ||||||
| - Add configuration itemselector feature (see | - Add configuration itemselector feature (see | ||||||
|  |  | ||||||
|  | @ -48,6 +48,21 @@ injectable values are also provided by another factories: | ||||||
| .. literalinclude:: ../../examples/providers/factory_init_injections.py | .. literalinclude:: ../../examples/providers/factory_init_injections.py | ||||||
|    :language: python |    :language: python | ||||||
| 
 | 
 | ||||||
|  | Factory providers and building complex object graphs | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | 
 | ||||||
|  | You can use :py:class:`Factory` provider to build complex object graphs. | ||||||
|  | 
 | ||||||
|  | Consider next example: | ||||||
|  | 
 | ||||||
|  | .. literalinclude:: ../../examples/providers/factory_deep_init_injections.py | ||||||
|  |    :language: python | ||||||
|  | 
 | ||||||
|  | .. note:: | ||||||
|  | 
 | ||||||
|  |    You can use ``__`` separator in the name of the keyword argument to pass the value to the child | ||||||
|  |    factory, e.g. ``algorithm_factory(task__loss__regularizer__alpha=0.5)``. | ||||||
|  | 
 | ||||||
| .. _factory_providers_delegation: | .. _factory_providers_delegation: | ||||||
| 
 | 
 | ||||||
| Factory providers delegation | Factory providers delegation | ||||||
|  |  | ||||||
|  | @ -1,2 +1,2 @@ | ||||||
| [pydocstyle] | [pydocstyle] | ||||||
| ignore = D101,D103,D107,D203,D213 | ignore = D100,D101,D102,D103,D107,D203,D213 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| Aiohttp Dependency Injection Example | Aiohttp Dependency Injection Example | ||||||
| ==================================== | ==================================== | ||||||
| 
 | 
 | ||||||
| Application ``giphynavigator`` is a `Aiohttp <https://docs.aiohttp.org/>`_ + | Application ``giphynavigator`` is an `Aiohttp <https://docs.aiohttp.org/>`_ + | ||||||
| `Dependency Injector <http://python-dependency-injector.ets-labs.org/>`_ application. | `Dependency Injector <http://python-dependency-injector.ets-labs.org/>`_ application. | ||||||
| 
 | 
 | ||||||
| Run | Run | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								examples/miniapps/monitoring-daemon-asyncio/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/miniapps/monitoring-daemon-asyncio/Dockerfile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | FROM python:3.8-buster | ||||||
|  | 
 | ||||||
|  | ENV PYTHONUNBUFFERED=1 | ||||||
|  | 
 | ||||||
|  | WORKDIR /code | ||||||
|  | COPY . /code/ | ||||||
|  | 
 | ||||||
|  | RUN apt-get install openssl \ | ||||||
|  |  && pip install --upgrade pip \ | ||||||
|  |  && pip install -r requirements.txt \ | ||||||
|  |  && rm -rf ~/.cache | ||||||
|  | 
 | ||||||
|  | CMD ["python", "-m", "monitoringdaemon"] | ||||||
							
								
								
									
										67
									
								
								examples/miniapps/monitoring-daemon-asyncio/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								examples/miniapps/monitoring-daemon-asyncio/README.rst
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | Asyncio Daemon Dependency Injection Example | ||||||
|  | =========================================== | ||||||
|  | 
 | ||||||
|  | Application ``monitoringdaemon`` is an `asyncio <https://docs.python.org/3/library/asyncio.html>`_ | ||||||
|  | + `Dependency Injector <http://python-dependency-injector.ets-labs.org/>`_ application. | ||||||
|  | 
 | ||||||
|  | Run | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Build the Docker image: | ||||||
|  | 
 | ||||||
|  | .. code-block:: bash | ||||||
|  | 
 | ||||||
|  |    docker-compose build | ||||||
|  | 
 | ||||||
|  | Run the docker-compose environment: | ||||||
|  | 
 | ||||||
|  | .. code-block:: bash | ||||||
|  | 
 | ||||||
|  |     docker-compose up | ||||||
|  | 
 | ||||||
|  | The output should be something like: | ||||||
|  | 
 | ||||||
|  | .. code-block:: | ||||||
|  | 
 | ||||||
|  |    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 | ||||||
|  | 
 | ||||||
|  | Test | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | This application comes with the unit tests. | ||||||
|  | 
 | ||||||
|  | To run the tests do: | ||||||
|  | 
 | ||||||
|  | .. code-block:: bash | ||||||
|  | 
 | ||||||
|  |    docker-compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon | ||||||
|  | 
 | ||||||
|  | The output should be something like: | ||||||
|  | 
 | ||||||
|  | .. code-block:: | ||||||
|  | 
 | ||||||
|  |    platform linux -- Python 3.8.3, pytest-6.0.1, py-1.9.0, pluggy-0.13.1 | ||||||
|  |    rootdir: /code | ||||||
|  |    plugins: asyncio-0.14.0, cov-2.10.0 | ||||||
|  |    collected 2 items | ||||||
|  | 
 | ||||||
|  |    monitoringdaemon/tests.py ..                                     [100%] | ||||||
|  | 
 | ||||||
|  |    ----------- coverage: platform linux, python 3.8.3-final-0 ----------- | ||||||
|  |    Name                             Stmts   Miss  Cover | ||||||
|  |    ---------------------------------------------------- | ||||||
|  |    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/http.py             6      3    50% | ||||||
|  |    monitoringdaemon/monitors.py        29      2    93% | ||||||
|  |    monitoringdaemon/tests.py           37      0   100% | ||||||
|  |    ---------------------------------------------------- | ||||||
|  |    TOTAL                              137     19    86% | ||||||
							
								
								
									
										17
									
								
								examples/miniapps/monitoring-daemon-asyncio/config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								examples/miniapps/monitoring-daemon-asyncio/config.yml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | log: | ||||||
|  |   level: "INFO" | ||||||
|  |   format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s" | ||||||
|  | 
 | ||||||
|  | monitors: | ||||||
|  | 
 | ||||||
|  |   example: | ||||||
|  |     method: "GET" | ||||||
|  |     url: "http://example.com" | ||||||
|  |     timeout: 5 | ||||||
|  |     check_every: 5 | ||||||
|  | 
 | ||||||
|  |   httpbin: | ||||||
|  |     method: "GET" | ||||||
|  |     url: "http://httpbin.org/get" | ||||||
|  |     timeout: 5 | ||||||
|  |     check_every: 5 | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | version: "3.7" | ||||||
|  | 
 | ||||||
|  | services: | ||||||
|  | 
 | ||||||
|  |   monitor: | ||||||
|  |     build: ./ | ||||||
|  |     image: monitoring-daemon | ||||||
|  |     volumes: | ||||||
|  |       - "./:/code" | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | """Top-level package.""" | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | """Main module.""" | ||||||
|  | 
 | ||||||
|  | from .containers import ApplicationContainer | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main() -> None: | ||||||
|  |     """Run the application.""" | ||||||
|  |     container = ApplicationContainer() | ||||||
|  | 
 | ||||||
|  |     container.config.from_yaml('config.yml') | ||||||
|  |     container.configure_logging() | ||||||
|  | 
 | ||||||
|  |     dispatcher = container.dispatcher() | ||||||
|  |     dispatcher.run() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main() | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | """Application containers module.""" | ||||||
|  | 
 | ||||||
|  | import logging | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | from dependency_injector import containers, providers | ||||||
|  | 
 | ||||||
|  | from . import http, monitors, dispatcher | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ApplicationContainer(containers.DeclarativeContainer): | ||||||
|  |     """Application container.""" | ||||||
|  | 
 | ||||||
|  |     config = providers.Configuration() | ||||||
|  | 
 | ||||||
|  |     configure_logging = providers.Callable( | ||||||
|  |         logging.basicConfig, | ||||||
|  |         stream=sys.stdout, | ||||||
|  |         level=config.log.level, | ||||||
|  |         format=config.log.format, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     http_client = providers.Factory(http.HttpClient) | ||||||
|  | 
 | ||||||
|  |     example_monitor = providers.Factory( | ||||||
|  |         monitors.HttpMonitor, | ||||||
|  |         http_client=http_client, | ||||||
|  |         options=config.monitors.example, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     httpbin_monitor = providers.Factory( | ||||||
|  |         monitors.HttpMonitor, | ||||||
|  |         http_client=http_client, | ||||||
|  |         options=config.monitors.httpbin, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     dispatcher = providers.Factory( | ||||||
|  |         dispatcher.Dispatcher, | ||||||
|  |         monitors=providers.List( | ||||||
|  |             example_monitor, | ||||||
|  |             httpbin_monitor, | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  | @ -0,0 +1,72 @@ | ||||||
|  | """Dispatcher module.""" | ||||||
|  | 
 | ||||||
|  | import asyncio | ||||||
|  | import logging | ||||||
|  | import signal | ||||||
|  | import time | ||||||
|  | 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._stopping = False | ||||||
|  | 
 | ||||||
|  |     def run(self) -> None: | ||||||
|  |         asyncio.run(self.start()) | ||||||
|  | 
 | ||||||
|  |     async def start(self) -> None: | ||||||
|  |         logger.info('Dispatcher is 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) | ||||||
|  | 
 | ||||||
|  |         await asyncio.gather(*self._monitor_tasks, return_exceptions=True) | ||||||
|  | 
 | ||||||
|  |         self.stop() | ||||||
|  | 
 | ||||||
|  |     def stop(self) -> None: | ||||||
|  |         if self._stopping: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         self._stopping = True | ||||||
|  | 
 | ||||||
|  |         logger.info('Dispatcher is 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') | ||||||
|  | 
 | ||||||
|  |     @staticmethod | ||||||
|  |     async def _run_monitor(monitor: Monitor) -> None: | ||||||
|  |         def _until_next(last: float) -> float: | ||||||
|  |             time_took = time.time() - last | ||||||
|  |             return monitor.check_every - time_took | ||||||
|  | 
 | ||||||
|  |         while True: | ||||||
|  |             time_start = time.time() | ||||||
|  | 
 | ||||||
|  |             try: | ||||||
|  |                 await monitor.check() | ||||||
|  |             except asyncio.CancelledError: | ||||||
|  |                 break | ||||||
|  |             except Exception: | ||||||
|  |                 monitor.logger.exception('Error running monitoring check') | ||||||
|  | 
 | ||||||
|  |             await asyncio.sleep(_until_next(last=time_start)) | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | """Http client module.""" | ||||||
|  | 
 | ||||||
|  | from aiohttp import ClientSession, ClientTimeout, ClientResponse | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class HttpClient: | ||||||
|  | 
 | ||||||
|  |     async def request(self, method: str, url: str, timeout: int) -> ClientResponse: | ||||||
|  |         async with ClientSession(timeout=ClientTimeout(timeout)) as session: | ||||||
|  |             async with session.request(method, url) as response: | ||||||
|  |                 return response | ||||||
|  | @ -0,0 +1,58 @@ | ||||||
|  | """Monitors module.""" | ||||||
|  | 
 | ||||||
|  | import logging | ||||||
|  | import time | ||||||
|  | from typing import Dict, Any | ||||||
|  | 
 | ||||||
|  | from .http import HttpClient | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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() | ||||||
|  | 
 | ||||||
|  |     async def check(self) -> None: | ||||||
|  |         raise NotImplementedError() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class HttpMonitor(Monitor): | ||||||
|  | 
 | ||||||
|  |     def __init__( | ||||||
|  |             self, | ||||||
|  |             http_client: HttpClient, | ||||||
|  |             options: Dict[str, Any], | ||||||
|  |     ) -> None: | ||||||
|  |         self._client = http_client | ||||||
|  |         self._method = options.pop('method') | ||||||
|  |         self._url = options.pop('url') | ||||||
|  |         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() | ||||||
|  | 
 | ||||||
|  |         response = await self._client.request( | ||||||
|  |             method=self._method, | ||||||
|  |             url=self._url, | ||||||
|  |             timeout=self._timeout, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         time_end = time.time() | ||||||
|  |         time_took = time_end - time_start | ||||||
|  | 
 | ||||||
|  |         self.logger.info( | ||||||
|  |             'Response code: %s, content length: %s, request took: %s seconds', | ||||||
|  |             response.status, | ||||||
|  |             response.content_length, | ||||||
|  |             round(time_took, 3) | ||||||
|  |         ) | ||||||
|  | @ -0,0 +1,79 @@ | ||||||
|  | """Tests module.""" | ||||||
|  | 
 | ||||||
|  | import asyncio | ||||||
|  | import dataclasses | ||||||
|  | from unittest import mock | ||||||
|  | 
 | ||||||
|  | import pytest | ||||||
|  | 
 | ||||||
|  | from .containers import ApplicationContainer | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @dataclasses.dataclass | ||||||
|  | class RequestStub: | ||||||
|  |     status: int | ||||||
|  |     content_length: int | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.fixture | ||||||
|  | def container(): | ||||||
|  |     container = ApplicationContainer() | ||||||
|  |     container.config.from_dict({ | ||||||
|  |         'log': { | ||||||
|  |             'level': 'INFO', | ||||||
|  |             'formant': '[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s', | ||||||
|  |         }, | ||||||
|  |         'monitors': { | ||||||
|  |             'example': { | ||||||
|  |                 'method': 'GET', | ||||||
|  |                 'url': 'http://fake-example.com', | ||||||
|  |                 'timeout': 1, | ||||||
|  |                 'check_every': 1, | ||||||
|  |             }, | ||||||
|  |             'httpbin': { | ||||||
|  |                 'method': 'GET', | ||||||
|  |                 'url': 'http://fake-httpbin.org/get', | ||||||
|  |                 'timeout': 1, | ||||||
|  |                 'check_every': 1, | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|  |     }) | ||||||
|  |     return container | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.asyncio | ||||||
|  | async def test_example_monitor(container, caplog): | ||||||
|  |     caplog.set_level('INFO') | ||||||
|  | 
 | ||||||
|  |     http_client_mock = mock.AsyncMock() | ||||||
|  |     http_client_mock.request.return_value = RequestStub( | ||||||
|  |         status=200, | ||||||
|  |         content_length=635, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     with container.http_client.override(http_client_mock): | ||||||
|  |         example_monitor = container.example_monitor() | ||||||
|  |         await example_monitor.check() | ||||||
|  | 
 | ||||||
|  |     assert 'http://fake-example.com' in caplog.text | ||||||
|  |     assert 'Response code: 200' in caplog.text | ||||||
|  |     assert 'content length: 635' in caplog.text | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.asyncio | ||||||
|  | async def test_dispatcher(container, caplog, event_loop): | ||||||
|  |     caplog.set_level('INFO') | ||||||
|  | 
 | ||||||
|  |     example_monitor_mock = mock.AsyncMock() | ||||||
|  |     httpbin_monitor_mock = mock.AsyncMock() | ||||||
|  | 
 | ||||||
|  |     with container.example_monitor.override(example_monitor_mock), \ | ||||||
|  |             container.httpbin_monitor.override(httpbin_monitor_mock): | ||||||
|  | 
 | ||||||
|  |         dispatcher = container.dispatcher() | ||||||
|  |         event_loop.create_task(dispatcher.start()) | ||||||
|  |         await asyncio.sleep(0.1) | ||||||
|  |         dispatcher.stop() | ||||||
|  | 
 | ||||||
|  |     assert example_monitor_mock.check.called | ||||||
|  |     assert httpbin_monitor_mock.check.called | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | dependency-injector | ||||||
|  | aiohttp | ||||||
|  | pyyaml | ||||||
|  | pytest | ||||||
|  | pytest-asyncio | ||||||
|  | pytest-cov | ||||||
							
								
								
									
										48
									
								
								examples/providers/factory_deep_init_injections.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								examples/providers/factory_deep_init_injections.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | """`Factory` providers - building a complex object graph with deep init injections example.""" | ||||||
|  | 
 | ||||||
|  | from dependency_injector import providers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Regularizer: | ||||||
|  |     def __init__(self, alpha): | ||||||
|  |         self.alpha = alpha | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Loss: | ||||||
|  |     def __init__(self, regularizer): | ||||||
|  |         self.regularizer = regularizer | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ClassificationTask: | ||||||
|  |     def __init__(self, loss): | ||||||
|  |         self.loss = loss | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Algorithm: | ||||||
|  |     def __init__(self, task): | ||||||
|  |         self.task = task | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | algorithm_factory = providers.Factory( | ||||||
|  |     Algorithm, | ||||||
|  |     task=providers.Factory( | ||||||
|  |         ClassificationTask, | ||||||
|  |         loss=providers.Factory( | ||||||
|  |             Loss, | ||||||
|  |             regularizer=providers.Factory( | ||||||
|  |                 Regularizer, | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     algorithm_1 = algorithm_factory(task__loss__regularizer__alpha=0.5) | ||||||
|  |     assert algorithm_1.task.loss.regularizer.alpha == 0.5 | ||||||
|  | 
 | ||||||
|  |     algorithm_2 = algorithm_factory(task__loss__regularizer__alpha=0.7) | ||||||
|  |     assert algorithm_2.task.loss.regularizer.alpha == 0.7 | ||||||
|  | 
 | ||||||
|  |     algorithm_3 = algorithm_factory(task__loss__regularizer=Regularizer(alpha=0.8)) | ||||||
|  |     assert algorithm_3.task.loss.regularizer.alpha == 0.8 | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| """Dependency injector top-level package.""" | """Dependency injector top-level package.""" | ||||||
| 
 | 
 | ||||||
| __version__ = '3.26.0' | __version__ = '3.27.0' | ||||||
| """Version number that follows semantic versioning. | """Version number that follows semantic versioning. | ||||||
| 
 | 
 | ||||||
| :type: str | :type: str | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -250,11 +250,38 @@ cdef inline object __get_value(Injection self): | ||||||
|     return self.__value() |     return self.__value() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | cdef inline object __get_value_kwargs(Injection self, dict kwargs): | ||||||
|  |     if self.__call == 0: | ||||||
|  |         return self.__value | ||||||
|  |     return self.__value(**kwargs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cdef inline tuple __separate_prefixed_kwargs(dict kwargs): | ||||||
|  |     cdef dict plain_kwargs = {} | ||||||
|  |     cdef dict prefixed_kwargs = {} | ||||||
|  | 
 | ||||||
|  |     for key, value in kwargs.items(): | ||||||
|  |         if '__' not in key: | ||||||
|  |             plain_kwargs[key] = value | ||||||
|  |             continue | ||||||
|  | 
 | ||||||
|  |         index = key.index('__') | ||||||
|  |         prefix, name = key[:index], key[index+2:] | ||||||
|  | 
 | ||||||
|  |         if prefix not in prefixed_kwargs: | ||||||
|  |             prefixed_kwargs[prefix] = {} | ||||||
|  |         prefixed_kwargs[prefix][name] = value | ||||||
|  | 
 | ||||||
|  |     return plain_kwargs, prefixed_kwargs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @cython.boundscheck(False) | @cython.boundscheck(False) | ||||||
| @cython.wraparound(False) | @cython.wraparound(False) | ||||||
| cdef inline tuple __provide_positional_args(tuple args, | cdef inline tuple __provide_positional_args( | ||||||
|  |         tuple args, | ||||||
|         tuple inj_args, |         tuple inj_args, | ||||||
|                                             int inj_args_len): |         int inj_args_len, | ||||||
|  | ): | ||||||
|     cdef int index |     cdef int index | ||||||
|     cdef list positional_args |     cdef list positional_args | ||||||
|     cdef PositionalInjection injection |     cdef PositionalInjection injection | ||||||
|  | @ -273,11 +300,15 @@ cdef inline tuple __provide_positional_args(tuple args, | ||||||
| 
 | 
 | ||||||
| @cython.boundscheck(False) | @cython.boundscheck(False) | ||||||
| @cython.wraparound(False) | @cython.wraparound(False) | ||||||
| cdef inline dict __provide_keyword_args(dict kwargs, | cdef inline dict __provide_keyword_args( | ||||||
|  |         dict kwargs, | ||||||
|         tuple inj_kwargs, |         tuple inj_kwargs, | ||||||
|                                         int inj_kwargs_len): |         int inj_kwargs_len, | ||||||
|  | ): | ||||||
|     cdef int index |     cdef int index | ||||||
|     cdef object name |     cdef object name | ||||||
|  |     cdef object value | ||||||
|  |     cdef dict prefixed | ||||||
|     cdef NamedInjection kw_injection |     cdef NamedInjection kw_injection | ||||||
| 
 | 
 | ||||||
|     if len(kwargs) == 0: |     if len(kwargs) == 0: | ||||||
|  | @ -286,20 +317,33 @@ cdef inline dict __provide_keyword_args(dict kwargs, | ||||||
|             name = __get_name(kw_injection) |             name = __get_name(kw_injection) | ||||||
|             kwargs[name] = __get_value(kw_injection) |             kwargs[name] = __get_value(kw_injection) | ||||||
|     else: |     else: | ||||||
|  |         kwargs, prefixed = __separate_prefixed_kwargs(kwargs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|         for index in range(inj_kwargs_len): |         for index in range(inj_kwargs_len): | ||||||
|             kw_injection = <NamedInjection>inj_kwargs[index] |             kw_injection = <NamedInjection>inj_kwargs[index] | ||||||
|             name = __get_name(kw_injection) |             name = __get_name(kw_injection) | ||||||
|             if name not in kwargs: | 
 | ||||||
|                 kwargs[name] = __get_value(kw_injection) |             if name in kwargs: | ||||||
|  |                 continue | ||||||
|  | 
 | ||||||
|  |             if name in prefixed: | ||||||
|  |                 value = __get_value_kwargs(kw_injection, prefixed[name]) | ||||||
|  |             else: | ||||||
|  |                 value = __get_value(kw_injection) | ||||||
|  | 
 | ||||||
|  |             kwargs[name] = value | ||||||
| 
 | 
 | ||||||
|     return kwargs |     return kwargs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @cython.boundscheck(False) | @cython.boundscheck(False) | ||||||
| @cython.wraparound(False) | @cython.wraparound(False) | ||||||
| cdef inline object __inject_attributes(object instance, | cdef inline object __inject_attributes( | ||||||
|  |         object instance, | ||||||
|         tuple attributes, |         tuple attributes, | ||||||
|                                        int attributes_len): |         int attributes_len, | ||||||
|  | ): | ||||||
|     cdef NamedInjection attr_injection |     cdef NamedInjection attr_injection | ||||||
|     for index in range(attributes_len): |     for index in range(attributes_len): | ||||||
|         attr_injection = <NamedInjection>attributes[index] |         attr_injection = <NamedInjection>attributes[index] | ||||||
|  |  | ||||||
|  | @ -166,6 +166,45 @@ class FactoryTests(unittest.TestCase): | ||||||
|         self.assertEqual(instance.init_arg3, 33) |         self.assertEqual(instance.init_arg3, 33) | ||||||
|         self.assertEqual(instance.init_arg4, 44) |         self.assertEqual(instance.init_arg4, 44) | ||||||
| 
 | 
 | ||||||
|  |     def test_call_with_deep_context_kwargs(self): | ||||||
|  |         """`Factory` providers deep init injections example.""" | ||||||
|  |         class Regularizer: | ||||||
|  |             def __init__(self, alpha): | ||||||
|  |                 self.alpha = alpha | ||||||
|  | 
 | ||||||
|  |         class Loss: | ||||||
|  |             def __init__(self, regularizer): | ||||||
|  |                 self.regularizer = regularizer | ||||||
|  | 
 | ||||||
|  |         class ClassificationTask: | ||||||
|  |             def __init__(self, loss): | ||||||
|  |                 self.loss = loss | ||||||
|  | 
 | ||||||
|  |         class Algorithm: | ||||||
|  |             def __init__(self, task): | ||||||
|  |                 self.task = task | ||||||
|  | 
 | ||||||
|  |         algorithm_factory = providers.Factory( | ||||||
|  |             Algorithm, | ||||||
|  |             task=providers.Factory( | ||||||
|  |                 ClassificationTask, | ||||||
|  |                 loss=providers.Factory( | ||||||
|  |                     Loss, | ||||||
|  |                     regularizer=providers.Factory( | ||||||
|  |                         Regularizer, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |             ), | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         algorithm_1 = algorithm_factory(task__loss__regularizer__alpha=0.5) | ||||||
|  |         algorithm_2 = algorithm_factory(task__loss__regularizer__alpha=0.7) | ||||||
|  |         algorithm_3 = algorithm_factory(task__loss__regularizer=Regularizer(alpha=0.8)) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(algorithm_1.task.loss.regularizer.alpha, 0.5) | ||||||
|  |         self.assertEqual(algorithm_2.task.loss.regularizer.alpha, 0.7) | ||||||
|  |         self.assertEqual(algorithm_3.task.loss.regularizer.alpha, 0.8) | ||||||
|  | 
 | ||||||
|     def test_fluent_interface(self): |     def test_fluent_interface(self): | ||||||
|         provider = providers.Factory(Example) \ |         provider = providers.Factory(Example) \ | ||||||
|             .add_args(1, 2) \ |             .add_args(1, 2) \ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user