mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-04 13:40:50 +03:00
Wiring with string module names (#515)
* Update main example * Updating wiring module * Update wiring test case name * Implement string imports for wiring * Update example * Refactor implementation * Update front example * Fix a typo in README * Update wiring docs * Update single container example * Update multiple containers example * Update quotes in multiple containers example * Update quotes in single container example * Update decoupled-packages example * Update single and multiple containers example * Update quotes * Update fastapi+redis example * Update resource docs * Update quotes in CLI tutorial * Update CLI application (movie lister) tutorial * Update monitoring daemon example * Update python version in asyncio daemon example * Update asyncio daemon tutorial * Update quotes in wiring docs * Update wiring docs
This commit is contained in:
parent
258c55dd22
commit
7d160cb4a5
14
README.rst
14
README.rst
|
@ -80,7 +80,7 @@ Key features of the ``Dependency Injector``:
|
|||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -104,11 +104,11 @@ Key features of the ``Dependency Injector``:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.api_key.from_env('API_KEY')
|
||||
container.config.timeout.from_env('TIMEOUT')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.api_key.from_env("API_KEY")
|
||||
container.config.timeout.from_env("TIMEOUT")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main() # <-- dependency is injected automatically
|
||||
|
||||
|
@ -195,7 +195,7 @@ What is the dependency injection?
|
|||
- dependency injection is a principle that decreases coupling and increases cohesion
|
||||
|
||||
Why should I do the dependency injection?
|
||||
- your code becomes more flexible, testable and clear 😎
|
||||
- your code becomes more flexible, testable, and clear 😎
|
||||
|
||||
How do I start doing the dependency injection?
|
||||
- you start writing the code following the dependency injection principle
|
||||
|
@ -204,7 +204,7 @@ How do I start doing the dependency injection?
|
|||
|
||||
What price do I pay and what do I get?
|
||||
- you need to explicitly specify the dependencies
|
||||
- it will be extra work in the beginning
|
||||
- it will be an extra work in the beginning
|
||||
- it will payoff as the project grows
|
||||
|
||||
Have a question?
|
||||
|
|
|
@ -86,7 +86,7 @@ Key features of the ``Dependency Injector``:
|
|||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -110,11 +110,11 @@ Key features of the ``Dependency Injector``:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.api_key.from_env('API_KEY')
|
||||
container.config.timeout.from_env('TIMEOUT')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.api_key.from_env("API_KEY")
|
||||
container.config.timeout.from_env("TIMEOUT")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main() # <-- dependency is injected automatically
|
||||
|
||||
|
|
|
@ -67,8 +67,8 @@ Before:
|
|||
class ApiClient:
|
||||
|
||||
def __init__(self):
|
||||
self.api_key = os.getenv('API_KEY') # <-- dependency
|
||||
self.timeout = os.getenv('TIMEOUT') # <-- dependency
|
||||
self.api_key = os.getenv("API_KEY") # <-- dependency
|
||||
self.timeout = os.getenv("TIMEOUT") # <-- dependency
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -82,7 +82,7 @@ Before:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
After:
|
||||
|
@ -109,12 +109,12 @@ After:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main(
|
||||
service=Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
api_key=os.getenv("API_KEY"),
|
||||
timeout=os.getenv("TIMEOUT"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -136,8 +136,8 @@ Now you need to assemble and inject the objects like this:
|
|||
main(
|
||||
service=Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
api_key=os.getenv("API_KEY"),
|
||||
timeout=os.getenv("TIMEOUT"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -162,7 +162,7 @@ the dependency.
|
|||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -186,11 +186,11 @@ the dependency.
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.api_key.from_env('API_KEY')
|
||||
container.config.timeout.from_env('TIMEOUT')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.api_key.from_env("API_KEY")
|
||||
container.config.timeout.from_env("TIMEOUT")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main() # <-- dependency is injected automatically
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ you configure global resource:
|
|||
|
||||
configure_logging = providers.Resource(
|
||||
logging.config.fileConfig,
|
||||
fname='logging.ini',
|
||||
fname="logging.ini",
|
||||
)
|
||||
|
||||
Function initializer does not provide a way to specify custom resource shutdown.
|
||||
|
@ -210,8 +210,8 @@ first argument.
|
|||
|
||||
.. _resource-provider-wiring-closing:
|
||||
|
||||
Resources, wiring and per-function execution scope
|
||||
--------------------------------------------------
|
||||
Resources, wiring, and per-function execution scope
|
||||
---------------------------------------------------
|
||||
|
||||
You can compound ``Resource`` provider with :ref:`wiring` to implement per-function
|
||||
execution scope. For doing this you need to use additional ``Closing`` marker from
|
||||
|
@ -220,7 +220,7 @@ execution scope. For doing this you need to use additional ``Closing`` marker fr
|
|||
.. literalinclude:: ../../examples/wiring/flask_resource_closing.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 24
|
||||
:emphasize-lines: 22
|
||||
|
||||
Framework initializes and injects the resource into the function. With the ``Closing`` marker
|
||||
framework calls resource ``shutdown()`` method when function execution is over.
|
||||
|
@ -325,7 +325,7 @@ When you use resource provider with asynchronous initializer you need to call it
|
|||
connection = await container.connection.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
Container ``init_resources()`` and ``shutdown_resources()`` methods should be used asynchronously if there is
|
||||
|
@ -349,7 +349,7 @@ at least one asynchronous resource provider:
|
|||
await container.shutdown_resources()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
See also:
|
||||
|
|
|
@ -59,8 +59,8 @@ The output should look something like:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
Docker version 19.03.12, build 48a66213fe
|
||||
docker-compose version 1.26.2, build eefe0d31
|
||||
Docker version 20.10.5, build 55c4c88
|
||||
docker-compose version 1.29.0, build 07737305
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -135,7 +135,7 @@ Put next lines into the ``Dockerfile`` file:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
FROM python:3.8-buster
|
||||
FROM python:3.9-buster
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
|
@ -267,9 +267,9 @@ Put next lines into the ``__main__.py`` file:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.from_yaml("config.yml")
|
||||
container.init_resources()
|
||||
|
||||
main()
|
||||
|
@ -356,7 +356,7 @@ and next into the ``dispatcher.py``:
|
|||
asyncio.run(self.start())
|
||||
|
||||
async def start(self) -> None:
|
||||
self._logger.info('Starting up')
|
||||
self._logger.info("Starting up")
|
||||
|
||||
for monitor in self._monitors:
|
||||
self._monitor_tasks.append(
|
||||
|
@ -376,11 +376,11 @@ and next into the ``dispatcher.py``:
|
|||
|
||||
self._stopping = True
|
||||
|
||||
self._logger.info('Shutting down')
|
||||
self._logger.info("Shutting down")
|
||||
for task, monitor in zip(self._monitor_tasks, self._monitors):
|
||||
task.cancel()
|
||||
self._monitor_tasks.clear()
|
||||
self._logger.info('Shutdown finished successfully')
|
||||
self._logger.info("Shutdown finished successfully")
|
||||
|
||||
@staticmethod
|
||||
async def _run_monitor(monitor: Monitor) -> None:
|
||||
|
@ -396,7 +396,7 @@ and next into the ``dispatcher.py``:
|
|||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception:
|
||||
monitor.logger.exception('Error executing monitor check')
|
||||
monitor.logger.exception("Error executing monitor check")
|
||||
|
||||
await asyncio.sleep(_until_next(last=time_start))
|
||||
|
||||
|
@ -442,13 +442,11 @@ and call the ``run()`` method. We will use :ref:`wiring` feature.
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3-7,11-13,20
|
||||
:emphasize-lines: 3-5,9-11,18
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .dispatcher import Dispatcher
|
||||
from .containers import Container
|
||||
|
@ -459,11 +457,11 @@ Edit ``__main__.py``:
|
|||
dispatcher.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.from_yaml("config.yml")
|
||||
container.init_resources()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
||||
|
@ -613,10 +611,10 @@ Edit ``monitors.py``:
|
|||
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'))
|
||||
self._method = options.pop("method")
|
||||
self._url = options.pop("url")
|
||||
self._timeout = options.pop("timeout")
|
||||
super().__init__(check_every=options.pop("check_every"))
|
||||
|
||||
async def check(self) -> None:
|
||||
time_start = time.time()
|
||||
|
@ -631,11 +629,11 @@ Edit ``monitors.py``:
|
|||
time_took = time_end - time_start
|
||||
|
||||
self.logger.info(
|
||||
'Check\n'
|
||||
' %s %s\n'
|
||||
' response code: %s\n'
|
||||
' content length: %s\n'
|
||||
' request took: %s seconds',
|
||||
"Check\n"
|
||||
" %s %s\n"
|
||||
" response code: %s\n"
|
||||
" content length: %s\n"
|
||||
" request took: %s seconds",
|
||||
self._method,
|
||||
self._url,
|
||||
response.status,
|
||||
|
@ -913,22 +911,22 @@ and put next into it:
|
|||
def container():
|
||||
container = Container()
|
||||
container.config.from_dict({
|
||||
'log': {
|
||||
'level': 'INFO',
|
||||
'formant': '[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s',
|
||||
"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,
|
||||
"monitors": {
|
||||
"example": {
|
||||
"method": "GET",
|
||||
"url": "http://fake-example.com",
|
||||
"timeout": 1,
|
||||
"check_every": 1,
|
||||
},
|
||||
'httpbin': {
|
||||
'method': 'GET',
|
||||
'url': 'https://fake-httpbin.org/get',
|
||||
'timeout': 1,
|
||||
'check_every': 1,
|
||||
"httpbin": {
|
||||
"method": "GET",
|
||||
"url": "https://fake-httpbin.org/get",
|
||||
"timeout": 1,
|
||||
"check_every": 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -937,7 +935,7 @@ and put next into it:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_example_monitor(container, caplog):
|
||||
caplog.set_level('INFO')
|
||||
caplog.set_level("INFO")
|
||||
|
||||
http_client_mock = mock.AsyncMock()
|
||||
http_client_mock.request.return_value = RequestStub(
|
||||
|
@ -949,14 +947,14 @@ and put next into it:
|
|||
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
|
||||
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')
|
||||
caplog.set_level("INFO")
|
||||
|
||||
example_monitor_mock = mock.AsyncMock()
|
||||
httpbin_monitor_mock = mock.AsyncMock()
|
||||
|
|
|
@ -160,19 +160,19 @@ Second put next in the ``fixtures.py``:
|
|||
|
||||
|
||||
SAMPLE_DATA = [
|
||||
('The Hunger Games: Mockingjay - Part 2', 2015, 'Francis Lawrence'),
|
||||
('Rogue One: A Star Wars Story', 2016, 'Gareth Edwards'),
|
||||
('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
("The Hunger Games: Mockingjay - Part 2", 2015, "Francis Lawrence"),
|
||||
("Rogue One: A Star Wars Story", 2016, "Gareth Edwards"),
|
||||
("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
FILE = pathlib.Path(__file__)
|
||||
DIR = FILE.parent
|
||||
CSV_FILE = DIR / 'movies.csv'
|
||||
SQLITE_FILE = DIR / 'movies.db'
|
||||
CSV_FILE = DIR / "movies.csv"
|
||||
SQLITE_FILE = DIR / "movies.db"
|
||||
|
||||
|
||||
def create_csv(movies_data, path):
|
||||
with open(path, 'w') as opened_file:
|
||||
with open(path, "w") as opened_file:
|
||||
writer = csv.writer(opened_file)
|
||||
for row in movies_data:
|
||||
writer.writerow(row)
|
||||
|
@ -181,20 +181,20 @@ Second put next in the ``fixtures.py``:
|
|||
def create_sqlite(movies_data, path):
|
||||
with sqlite3.connect(path) as db:
|
||||
db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS movies '
|
||||
'(title text, year int, director text)'
|
||||
"CREATE TABLE IF NOT EXISTS movies "
|
||||
"(title text, year int, director text)"
|
||||
)
|
||||
db.execute('DELETE FROM movies')
|
||||
db.executemany('INSERT INTO movies VALUES (?,?,?)', movies_data)
|
||||
db.execute("DELETE FROM movies")
|
||||
db.executemany("INSERT INTO movies VALUES (?,?,?)", movies_data)
|
||||
|
||||
|
||||
def main():
|
||||
create_csv(SAMPLE_DATA, CSV_FILE)
|
||||
create_sqlite(SAMPLE_DATA, SQLITE_FILE)
|
||||
print('OK')
|
||||
print("OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Now run in the terminal:
|
||||
|
@ -266,7 +266,7 @@ Edit ``__main__.py``:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
|
||||
main()
|
||||
|
@ -321,7 +321,7 @@ and put next into it:
|
|||
self.director = str(director)
|
||||
|
||||
def __repr__(self):
|
||||
return '{0}(title={1}, year={2}, director={3})'.format(
|
||||
return "{0}(title={1}, year={2}, director={3})".format(
|
||||
self.__class__.__name__,
|
||||
repr(self.title),
|
||||
repr(self.year),
|
||||
|
@ -483,9 +483,9 @@ Edit ``__main__.py``:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.from_yaml("config.yml")
|
||||
|
||||
main()
|
||||
|
||||
|
@ -575,13 +575,11 @@ Let's inject the ``lister`` into the ``main()`` function.
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3-7,11-12,19
|
||||
:emphasize-lines: 3-5,9-10,17
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
@ -592,10 +590,10 @@ Edit ``__main__.py``:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.from_yaml("config.yml")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
||||
|
@ -607,13 +605,11 @@ Francis Lawrence and movies released in 2016.
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 13-19
|
||||
:emphasize-lines: 11-17
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
@ -621,19 +617,19 @@ Edit ``__main__.py``:
|
|||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
print('Francis Lawrence movies:')
|
||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||
print('\t-', movie)
|
||||
print("Francis Lawrence movies:")
|
||||
for movie in lister.movies_directed_by("Francis Lawrence"):
|
||||
print("\t-", movie)
|
||||
|
||||
print('2016 movies:')
|
||||
print("2016 movies:")
|
||||
for movie in lister.movies_released_in(2016):
|
||||
print('\t-', movie)
|
||||
print("\t-", movie)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.from_yaml("config.yml")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
||||
|
@ -718,7 +714,7 @@ Edit ``finders.py``:
|
|||
|
||||
def find_all(self) -> List[Movie]:
|
||||
with self._database as db:
|
||||
rows = db.execute('SELECT title, year, director FROM movies')
|
||||
rows = db.execute("SELECT title, year, director FROM movies")
|
||||
return [self._movie_factory(*row) for row in rows]
|
||||
|
||||
Now we need to add the sqlite finder to the container and update lister's dependency to use it.
|
||||
|
@ -863,13 +859,11 @@ Now we need to read the value of the ``config.finder.type`` option from the envi
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 25
|
||||
:emphasize-lines: 23
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
@ -877,19 +871,19 @@ Edit ``__main__.py``:
|
|||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
print('Francis Lawrence movies:')
|
||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||
print('\t-', movie)
|
||||
print("Francis Lawrence movies:")
|
||||
for movie in lister.movies_directed_by("Francis Lawrence"):
|
||||
print("\t-", movie)
|
||||
|
||||
print('2016 movies:')
|
||||
print("2016 movies:")
|
||||
for movie in lister.movies_released_in(2016):
|
||||
print('\t-', movie)
|
||||
print("\t-", movie)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.finder.type.from_env('MOVIE_FINDER_TYPE')
|
||||
container.config.from_yaml("config.yml")
|
||||
container.config.finder.type.from_env("MOVIE_FINDER_TYPE")
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
|
||||
main()
|
||||
|
@ -963,14 +957,14 @@ and put next into it:
|
|||
def container():
|
||||
container = Container()
|
||||
container.config.from_dict({
|
||||
'finder': {
|
||||
'type': 'csv',
|
||||
'csv': {
|
||||
'path': '/fake-movies.csv',
|
||||
'delimiter': ',',
|
||||
"finder": {
|
||||
"type": "csv",
|
||||
"csv": {
|
||||
"path": "/fake-movies.csv",
|
||||
"delimiter": ",",
|
||||
},
|
||||
'sqlite': {
|
||||
'path': '/fake-movies.db',
|
||||
"sqlite": {
|
||||
"path": "/fake-movies.db",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -980,23 +974,23 @@ and put next into it:
|
|||
def test_movies_directed_by(container):
|
||||
finder_mock = mock.Mock()
|
||||
finder_mock.find_all.return_value = [
|
||||
container.movie('The 33', 2015, 'Patricia Riggen'),
|
||||
container.movie('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
with container.finder.override(finder_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_directed_by('Jon Favreau')
|
||||
movies = lister.movies_directed_by("Jon Favreau")
|
||||
|
||||
assert len(movies) == 1
|
||||
assert movies[0].title == 'The Jungle Book'
|
||||
assert movies[0].title == "The Jungle Book"
|
||||
|
||||
|
||||
def test_movies_released_in(container):
|
||||
finder_mock = mock.Mock()
|
||||
finder_mock.find_all.return_value = [
|
||||
container.movie('The 33', 2015, 'Patricia Riggen'),
|
||||
container.movie('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
with container.finder.override(finder_mock):
|
||||
|
@ -1004,7 +998,7 @@ and put next into it:
|
|||
movies = lister.movies_released_in(2015)
|
||||
|
||||
assert len(movies) == 1
|
||||
assert movies[0].title == 'The 33'
|
||||
assert movies[0].title == "The 33"
|
||||
|
||||
Run in the terminal:
|
||||
|
||||
|
|
|
@ -95,17 +95,17 @@ Also you can use ``Provide`` marker to inject a container.
|
|||
|
||||
.. literalinclude:: ../examples/wiring/example_container.py
|
||||
:language: python
|
||||
:emphasize-lines: 16-19
|
||||
:emphasize-lines: 14-17
|
||||
:lines: 3-
|
||||
|
||||
Strings identifiers
|
||||
-------------------
|
||||
String identifiers
|
||||
------------------
|
||||
|
||||
You can use wiring with string identifiers. String identifier should match provider name in the container:
|
||||
|
||||
.. literalinclude:: ../examples/wiring/example_string_id.py
|
||||
:language: python
|
||||
:emphasize-lines: 17
|
||||
:emphasize-lines: 15
|
||||
:lines: 3-
|
||||
|
||||
With string identifiers you don't need to use a container to specify an injection.
|
||||
|
@ -115,7 +115,7 @@ To specify an injection from a nested container use point ``.`` as a separator:
|
|||
.. code-block:: python
|
||||
|
||||
@inject
|
||||
def foo(service: UserService = Provide['services.user']) -> None:
|
||||
def foo(service: UserService = Provide["services.user"]) -> None:
|
||||
...
|
||||
|
||||
You can also use injection modifiers:
|
||||
|
@ -135,34 +135,34 @@ You can also use injection modifiers:
|
|||
|
||||
|
||||
@inject
|
||||
def foo(value: int = Provide['config.option', as_int()]) -> None:
|
||||
def foo(value: int = Provide["config.option", as_int()]) -> None:
|
||||
...
|
||||
|
||||
|
||||
@inject
|
||||
def foo(value: float = Provide['config.option', as_float()]) -> None:
|
||||
def foo(value: float = Provide["config.option", as_float()]) -> None:
|
||||
...
|
||||
|
||||
|
||||
@inject
|
||||
def foo(value: Decimal = Provide['config.option', as_(Decimal)]) -> None:
|
||||
def foo(value: Decimal = Provide["config.option", as_(Decimal)]) -> None:
|
||||
...
|
||||
|
||||
@inject
|
||||
def foo(value: str = Provide['config.option', required()]) -> None:
|
||||
def foo(value: str = Provide["config.option", required()]) -> None:
|
||||
...
|
||||
|
||||
@inject
|
||||
def foo(value: int = Provide['config.option', required().as_int()]) -> None:
|
||||
def foo(value: int = Provide["config.option", required().as_int()]) -> None:
|
||||
...
|
||||
|
||||
|
||||
@inject
|
||||
def foo(value: int = Provide['config.option', invariant('config.switch')]) -> None:
|
||||
def foo(value: int = Provide["config.option", invariant("config.switch")]) -> None:
|
||||
...
|
||||
|
||||
@inject
|
||||
def foo(value: int = Provide['service', provided().foo['bar'].call()]) -> None:
|
||||
def foo(value: int = Provide["service", provided().foo["bar"].call()]) -> None:
|
||||
...
|
||||
|
||||
|
||||
|
@ -171,7 +171,7 @@ To inject a container use special identifier ``<container>``:
|
|||
.. code-block:: python
|
||||
|
||||
@inject
|
||||
def foo(container: Container = Provide['<container>']) -> None:
|
||||
def foo(container: Container = Provide["<container>"]) -> None:
|
||||
...
|
||||
|
||||
|
||||
|
@ -183,25 +183,63 @@ You can use wiring to make injections into modules and class attributes.
|
|||
.. literalinclude:: ../examples/wiring/example_attribute.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 16,21
|
||||
:emphasize-lines: 14,19
|
||||
|
||||
You could also use string identifiers to avoid a dependency on a container:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 1,6
|
||||
|
||||
service: Service = Provide['service']
|
||||
service: Service = Provide["service"]
|
||||
|
||||
|
||||
class Main:
|
||||
|
||||
service: Service = Provide['service']
|
||||
service: Service = Provide["service"]
|
||||
|
||||
Wiring with modules and packages
|
||||
--------------------------------
|
||||
|
||||
To wire a container with a module you need to call ``container.wire(modules=[...])`` method. Argument
|
||||
``modules`` is an iterable of the module objects.
|
||||
To wire a container with the modules you need to call ``container.wire()`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.wire(
|
||||
modules=[
|
||||
"yourapp.module1",
|
||||
"yourapp.module2",
|
||||
],
|
||||
)
|
||||
|
||||
Method ``container.wire()`` can resolve relative imports:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# In module "yourapp.foo":
|
||||
|
||||
container.wire(
|
||||
modules=[
|
||||
".module1", # Resolved to: "yourapp.module1"
|
||||
".module2", # Resolved to: "yourapp.module2"
|
||||
],
|
||||
)
|
||||
|
||||
You can also manually specify a base package for resolving relative imports with
|
||||
the ``from_package`` argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# In module "yourapp.main":
|
||||
|
||||
container.wire(
|
||||
modules=[
|
||||
".module1", # Resolved to: "anotherapp.module1"
|
||||
".module2", # Resolved to: "anotherapp.module2"
|
||||
],
|
||||
from_package="anotherapp",
|
||||
)
|
||||
|
||||
Argument ``modules`` can also take already imported modules:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -211,15 +249,16 @@ To wire a container with a module you need to call ``container.wire(modules=[...
|
|||
container = Container()
|
||||
container.wire(modules=[module1, module2])
|
||||
|
||||
You can wire container with a package. Container walks recursively over package modules.
|
||||
You can wire container with a package. Container walks recursively over the package modules:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from yourapp import package1, package2
|
||||
|
||||
|
||||
container = Container()
|
||||
container.wire(packages=[package1, package2])
|
||||
container.wire(
|
||||
packages=[
|
||||
"yourapp.package1",
|
||||
"yourapp.package2",
|
||||
],
|
||||
)
|
||||
|
||||
Arguments ``modules`` and ``packages`` can be used together.
|
||||
|
||||
|
@ -233,7 +272,7 @@ When wiring is done functions and methods with the markers are patched to provid
|
|||
|
||||
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
foo() # <--- Argument "bar" is injected
|
||||
|
||||
|
@ -267,7 +306,7 @@ You can use that in testing to re-create and re-wire a container before each tes
|
|||
|
||||
def setUp(self):
|
||||
self.container = Container()
|
||||
self.container.wire(modules=[module1, module2])
|
||||
self.container.wire(modules=["yourapp.module1", "yourapp.module2"])
|
||||
self.addCleanup(self.container.unwire)
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -278,7 +317,7 @@ You can use that in testing to re-create and re-wire a container before each tes
|
|||
@pytest.fixture
|
||||
def container():
|
||||
container = Container()
|
||||
container.wire(modules=[module1, module2])
|
||||
container.wire(modules=["yourapp.module1", "yourapp.module2"])
|
||||
yield container
|
||||
container.unwire()
|
||||
|
||||
|
@ -402,11 +441,11 @@ This is useful when you import modules dynamically.
|
|||
from .containers import Container
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
register_loader_containers(container) # <--- installs import hook
|
||||
|
||||
module = importlib.import_module('package.module')
|
||||
module = importlib.import_module("package.module")
|
||||
module.foo()
|
||||
|
||||
You can register multiple containers in the import hook. For doing this call register function
|
||||
|
|
|
@ -18,12 +18,12 @@ def main(service: Service): # <-- dependency is injected
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main(
|
||||
service=Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
api_key=os.getenv("API_KEY"),
|
||||
timeout=os.getenv("TIMEOUT"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -4,8 +4,8 @@ import os
|
|||
class ApiClient:
|
||||
|
||||
def __init__(self):
|
||||
self.api_key = os.getenv('API_KEY') # <-- dependency
|
||||
self.timeout = os.getenv('TIMEOUT') # <-- dependency
|
||||
self.api_key = os.getenv("API_KEY") # <-- dependency
|
||||
self.timeout = os.getenv("TIMEOUT") # <-- dependency
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -19,5 +19,5 @@ def main() -> None:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import sys
|
||||
from unittest import mock
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from after import ApiClient, Service
|
||||
|
||||
|
@ -28,11 +27,11 @@ def main(service: Service = Provide[Container.service]):
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.api_key.from_env('API_KEY')
|
||||
container.config.timeout.from_env('TIMEOUT')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.api_key.from_env("API_KEY")
|
||||
container.config.timeout.from_env("TIMEOUT")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main() # <-- dependency is injected automatically
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from .containers import Application
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
application = Application()
|
||||
config = application.service.config()
|
||||
config.build()
|
||||
|
|
|
@ -6,17 +6,17 @@ from .services import ConfigService
|
|||
|
||||
|
||||
class Core(containers.DeclarativeContainer):
|
||||
config = providers.Configuration('config')
|
||||
config = providers.Configuration("config")
|
||||
|
||||
|
||||
class Storage(containers.DeclarativeContainer):
|
||||
queue = providers.Singleton(lambda: 'Some storage')
|
||||
queue = providers.Singleton(lambda: "Some storage")
|
||||
|
||||
|
||||
class Adapter(containers.DeclarativeContainer):
|
||||
core = providers.DependenciesContainer(config=providers.Configuration())
|
||||
tinydb = providers.Singleton(
|
||||
lambda db_path: f'DB Path=[{db_path}]',
|
||||
lambda db_path: f"DB Path=[{db_path}]",
|
||||
db_path=core.config.default.db_path,
|
||||
)
|
||||
|
||||
|
@ -25,7 +25,7 @@ class Repository(containers.DeclarativeContainer):
|
|||
adapter = providers.DependenciesContainer()
|
||||
storage = providers.DependenciesContainer()
|
||||
site = providers.Singleton(
|
||||
lambda adapter, queue: f'Adapter=[{adapter}], queue=[{queue}]',
|
||||
lambda adapter, queue: f"Adapter=[{adapter}], queue=[{queue}]",
|
||||
adapter=adapter.tinydb,
|
||||
queue=storage.queue,
|
||||
)
|
||||
|
|
|
@ -6,4 +6,4 @@ class ConfigService:
|
|||
self._config = config
|
||||
|
||||
def build(self):
|
||||
self._config.from_dict({'default': {'db_path': '~/test'}})
|
||||
self._config.from_dict({"default": {"db_path": "~/test"}})
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .services import UserService, AuthService, PhotoService
|
||||
from .containers import Application
|
||||
|
@ -22,10 +22,10 @@ def main(
|
|||
photo_service.upload_photo(user, photo)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
application = Application()
|
||||
application.config.from_yaml('config.yml')
|
||||
application.config.from_yaml("config.yml")
|
||||
application.core.init_resources()
|
||||
application.wire(modules=[sys.modules[__name__]])
|
||||
application.wire(modules=[__name__])
|
||||
|
||||
main(*sys.argv[1:])
|
||||
|
|
|
@ -30,7 +30,7 @@ class Gateways(containers.DeclarativeContainer):
|
|||
|
||||
s3_client = providers.Singleton(
|
||||
boto3.client,
|
||||
service_name='s3',
|
||||
service_name="s3",
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key,
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ class BaseService:
|
|||
|
||||
def __init__(self) -> None:
|
||||
self.logger = logging.getLogger(
|
||||
f'{__name__}.{self.__class__.__name__}',
|
||||
f"{__name__}.{self.__class__.__name__}",
|
||||
)
|
||||
|
||||
|
||||
|
@ -22,8 +22,8 @@ class UserService(BaseService):
|
|||
super().__init__()
|
||||
|
||||
def get_user(self, email: str) -> Dict[str, str]:
|
||||
self.logger.debug('User %s has been found in database', email)
|
||||
return {'email': email, 'password_hash': '...'}
|
||||
self.logger.debug("User %s has been found in database", email)
|
||||
return {"email": email, "password_hash": "..."}
|
||||
|
||||
|
||||
class AuthService(BaseService):
|
||||
|
@ -36,8 +36,8 @@ class AuthService(BaseService):
|
|||
def authenticate(self, user: Dict[str, str], password: str) -> None:
|
||||
assert password is not None
|
||||
self.logger.debug(
|
||||
'User %s has been successfully authenticated',
|
||||
user['email'],
|
||||
"User %s has been successfully authenticated",
|
||||
user["email"],
|
||||
)
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ class PhotoService(BaseService):
|
|||
|
||||
def upload_photo(self, user: Dict[str, str], photo_path: str) -> None:
|
||||
self.logger.debug(
|
||||
'Photo %s has been successfully uploaded by user %s',
|
||||
"Photo %s has been successfully uploaded by user %s",
|
||||
photo_path,
|
||||
user['email'],
|
||||
user["email"],
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .services import UserService, AuthService, PhotoService
|
||||
from .containers import Container
|
||||
|
@ -22,10 +22,10 @@ def main(
|
|||
photo_service.upload_photo(user, photo)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.init_resources()
|
||||
container.config.from_ini('config.ini')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.from_ini("config.ini")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main(*sys.argv[1:])
|
||||
|
|
|
@ -15,7 +15,7 @@ class Container(containers.DeclarativeContainer):
|
|||
|
||||
logging = providers.Resource(
|
||||
logging.config.fileConfig,
|
||||
fname='logging.ini',
|
||||
fname="logging.ini",
|
||||
)
|
||||
|
||||
# Gateways
|
||||
|
@ -27,7 +27,7 @@ class Container(containers.DeclarativeContainer):
|
|||
|
||||
s3_client = providers.Singleton(
|
||||
boto3.client,
|
||||
service_name='s3',
|
||||
service_name="s3",
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key,
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ class BaseService:
|
|||
|
||||
def __init__(self) -> None:
|
||||
self.logger = logging.getLogger(
|
||||
f'{__name__}.{self.__class__.__name__}',
|
||||
f"{__name__}.{self.__class__.__name__}",
|
||||
)
|
||||
|
||||
|
||||
|
@ -22,8 +22,8 @@ class UserService(BaseService):
|
|||
super().__init__()
|
||||
|
||||
def get_user(self, email: str) -> Dict[str, str]:
|
||||
self.logger.debug('User %s has been found in database', email)
|
||||
return {'email': email, 'password_hash': '...'}
|
||||
self.logger.debug("User %s has been found in database", email)
|
||||
return {"email": email, "password_hash": "..."}
|
||||
|
||||
|
||||
class AuthService(BaseService):
|
||||
|
@ -36,8 +36,8 @@ class AuthService(BaseService):
|
|||
def authenticate(self, user: Dict[str, str], password: str) -> None:
|
||||
assert password is not None
|
||||
self.logger.debug(
|
||||
'User %s has been successfully authenticated',
|
||||
user['email'],
|
||||
"User %s has been successfully authenticated",
|
||||
user["email"],
|
||||
)
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ class PhotoService(BaseService):
|
|||
|
||||
def upload_photo(self, user: Dict[str, str], photo_path: str) -> None:
|
||||
self.logger.debug(
|
||||
'Photo %s has been successfully uploaded by user %s',
|
||||
"Photo %s has been successfully uploaded by user %s",
|
||||
photo_path,
|
||||
user['email'],
|
||||
user["email"],
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.8-buster
|
||||
FROM python:3.9-buster
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .dispatcher import Dispatcher
|
||||
|
@ -13,10 +11,10 @@ def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
|||
dispatcher.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.from_yaml("config.yml")
|
||||
container.init_resources()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
|
|
@ -21,7 +21,7 @@ class Dispatcher:
|
|||
asyncio.run(self.start())
|
||||
|
||||
async def start(self) -> None:
|
||||
self._logger.info('Starting up')
|
||||
self._logger.info("Starting up")
|
||||
|
||||
for monitor in self._monitors:
|
||||
self._monitor_tasks.append(
|
||||
|
@ -41,11 +41,11 @@ class Dispatcher:
|
|||
|
||||
self._stopping = True
|
||||
|
||||
self._logger.info('Shutting down')
|
||||
self._logger.info("Shutting down")
|
||||
for task, monitor in zip(self._monitor_tasks, self._monitors):
|
||||
task.cancel()
|
||||
self._monitor_tasks.clear()
|
||||
self._logger.info('Shutdown finished successfully')
|
||||
self._logger.info("Shutdown finished successfully")
|
||||
|
||||
@staticmethod
|
||||
async def _run_monitor(monitor: Monitor) -> None:
|
||||
|
@ -61,6 +61,6 @@ class Dispatcher:
|
|||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception:
|
||||
monitor.logger.exception('Error executing monitor check')
|
||||
monitor.logger.exception("Error executing monitor check")
|
||||
|
||||
await asyncio.sleep(_until_next(last=time_start))
|
||||
|
|
|
@ -25,10 +25,10 @@ class HttpMonitor(Monitor):
|
|||
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'))
|
||||
self._method = options.pop("method")
|
||||
self._url = options.pop("url")
|
||||
self._timeout = options.pop("timeout")
|
||||
super().__init__(check_every=options.pop("check_every"))
|
||||
|
||||
async def check(self) -> None:
|
||||
time_start = time.time()
|
||||
|
@ -43,11 +43,11 @@ class HttpMonitor(Monitor):
|
|||
time_took = time_end - time_start
|
||||
|
||||
self.logger.info(
|
||||
'Check\n'
|
||||
' %s %s\n'
|
||||
' response code: %s\n'
|
||||
' content length: %s\n'
|
||||
' request took: %s seconds',
|
||||
"Check\n"
|
||||
" %s %s\n"
|
||||
" response code: %s\n"
|
||||
" content length: %s\n"
|
||||
" request took: %s seconds",
|
||||
self._method,
|
||||
self._url,
|
||||
response.status,
|
||||
|
|
|
@ -19,22 +19,22 @@ class RequestStub:
|
|||
def container():
|
||||
container = Container()
|
||||
container.config.from_dict({
|
||||
'log': {
|
||||
'level': 'INFO',
|
||||
'formant': '[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s',
|
||||
"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,
|
||||
"monitors": {
|
||||
"example": {
|
||||
"method": "GET",
|
||||
"url": "http://fake-example.com",
|
||||
"timeout": 1,
|
||||
"check_every": 1,
|
||||
},
|
||||
'httpbin': {
|
||||
'method': 'GET',
|
||||
'url': 'https://fake-httpbin.org/get',
|
||||
'timeout': 1,
|
||||
'check_every': 1,
|
||||
"httpbin": {
|
||||
"method": "GET",
|
||||
"url": "https://fake-httpbin.org/get",
|
||||
"timeout": 1,
|
||||
"check_every": 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -43,7 +43,7 @@ def container():
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_example_monitor(container, caplog):
|
||||
caplog.set_level('INFO')
|
||||
caplog.set_level("INFO")
|
||||
|
||||
http_client_mock = mock.AsyncMock()
|
||||
http_client_mock.request.return_value = RequestStub(
|
||||
|
@ -55,14 +55,14 @@ async def test_example_monitor(container, caplog):
|
|||
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
|
||||
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')
|
||||
caplog.set_level("INFO")
|
||||
|
||||
example_monitor_mock = mock.AsyncMock()
|
||||
httpbin_monitor_mock = mock.AsyncMock()
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .user.repositories import UserRepository
|
||||
from .photo.repositories import PhotoRepository
|
||||
|
@ -24,20 +22,20 @@ def main(
|
|||
) -> None:
|
||||
user1 = user_repository.get(id=1)
|
||||
user1_photos = photo_repository.get_photos(user1.id)
|
||||
print(f'Retrieve user id={user1.id}, photos count={len(user1_photos)}')
|
||||
print(f"Retrieve user id={user1.id}, photos count={len(user1_photos)}")
|
||||
|
||||
user2 = user_repository.get(id=2)
|
||||
user2_photos = photo_repository.get_photos(user2.id)
|
||||
print(f'Retrieve user id={user2.id}, photos count={len(user2_photos)}')
|
||||
print(f"Retrieve user id={user2.id}, photos count={len(user2_photos)}")
|
||||
|
||||
assert aggregation_service.user_repository is user_repository
|
||||
assert aggregation_service.photo_repository is photo_repository
|
||||
print('Aggregate analytics from user and photo packages')
|
||||
print("Aggregate analytics from user and photo packages")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
application = ApplicationContainer()
|
||||
application.config.from_ini('config.ini')
|
||||
application.wire(modules=[sys.modules[__name__]])
|
||||
application.config.from_ini("config.ini")
|
||||
application.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
|
|
@ -18,7 +18,7 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|||
|
||||
s3 = providers.Singleton(
|
||||
boto3.client,
|
||||
service_name='s3',
|
||||
service_name="s3",
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key,
|
||||
)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""Application module."""
|
||||
|
||||
import sys
|
||||
|
||||
from fastapi import FastAPI, Depends
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from fastapi import FastAPI, Depends
|
||||
|
||||
from .containers import Container
|
||||
from .services import Service
|
||||
|
@ -12,14 +10,14 @@ from .services import Service
|
|||
app = FastAPI()
|
||||
|
||||
|
||||
@app.api_route('/')
|
||||
@app.api_route("/")
|
||||
@inject
|
||||
async def index(service: Service = Depends(Provide[Container.service])):
|
||||
value = await service.process()
|
||||
return {'result': value}
|
||||
return {"result": value}
|
||||
|
||||
|
||||
container = Container()
|
||||
container.config.redis_host.from_env('REDIS_HOST', 'localhost')
|
||||
container.config.redis_password.from_env('REDIS_PASSWORD', 'password')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.redis_host.from_env("REDIS_HOST", "localhost")
|
||||
container.config.redis_password.from_env("REDIS_PASSWORD", "password")
|
||||
container.wire(modules=[__name__])
|
||||
|
|
|
@ -6,7 +6,7 @@ from aioredis import create_redis_pool, Redis
|
|||
|
||||
|
||||
async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]:
|
||||
pool = await create_redis_pool(f'redis://{host}', password=password)
|
||||
pool = await create_redis_pool(f"redis://{host}", password=password)
|
||||
yield pool
|
||||
pool.close()
|
||||
await pool.wait_closed()
|
||||
|
|
|
@ -8,5 +8,5 @@ class Service:
|
|||
self._redis = redis
|
||||
|
||||
async def process(self) -> str:
|
||||
await self._redis.set('my-key', 'value')
|
||||
return await self._redis.get('my-key', encoding='utf-8')
|
||||
await self._redis.set("my-key", "value")
|
||||
return await self._redis.get("my-key", encoding="utf-8")
|
||||
|
|
|
@ -11,7 +11,7 @@ from .services import Service
|
|||
|
||||
@pytest.fixture
|
||||
def client(event_loop):
|
||||
client = AsyncClient(app=app, base_url='http://test')
|
||||
client = AsyncClient(app=app, base_url="http://test")
|
||||
yield client
|
||||
event_loop.run_until_complete(client.aclose())
|
||||
|
||||
|
@ -19,10 +19,10 @@ def client(event_loop):
|
|||
@pytest.mark.asyncio
|
||||
async def test_index(client):
|
||||
service_mock = mock.AsyncMock(spec=Service)
|
||||
service_mock.process.return_value = 'Foo'
|
||||
service_mock.process.return_value = "Foo"
|
||||
|
||||
with container.service.override(service_mock):
|
||||
response = await client.get('/')
|
||||
response = await client.get("/")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {'result': 'Foo'}
|
||||
assert response.json() == {"result": "Foo"}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
dependency-injector
|
||||
fastapi
|
||||
uvicorn
|
||||
aioredis
|
||||
aioredis<2 # TODO: Update example to work with aioredis >= 2.0
|
||||
|
||||
# For testing:
|
||||
pytest
|
||||
|
|
|
@ -6,19 +6,19 @@ import pathlib
|
|||
|
||||
|
||||
SAMPLE_DATA = [
|
||||
('The Hunger Games: Mockingjay - Part 2', 2015, 'Francis Lawrence'),
|
||||
('Rogue One: A Star Wars Story', 2016, 'Gareth Edwards'),
|
||||
('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
("The Hunger Games: Mockingjay - Part 2", 2015, "Francis Lawrence"),
|
||||
("Rogue One: A Star Wars Story", 2016, "Gareth Edwards"),
|
||||
("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
FILE = pathlib.Path(__file__)
|
||||
DIR = FILE.parent
|
||||
CSV_FILE = DIR / 'movies.csv'
|
||||
SQLITE_FILE = DIR / 'movies.db'
|
||||
CSV_FILE = DIR / "movies.csv"
|
||||
SQLITE_FILE = DIR / "movies.db"
|
||||
|
||||
|
||||
def create_csv(movies_data, path):
|
||||
with open(path, 'w') as opened_file:
|
||||
with open(path, "w") as opened_file:
|
||||
writer = csv.writer(opened_file)
|
||||
for row in movies_data:
|
||||
writer.writerow(row)
|
||||
|
@ -27,18 +27,18 @@ def create_csv(movies_data, path):
|
|||
def create_sqlite(movies_data, path):
|
||||
with sqlite3.connect(path) as db:
|
||||
db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS movies '
|
||||
'(title text, year int, director text)'
|
||||
"CREATE TABLE IF NOT EXISTS movies "
|
||||
"(title text, year int, director text)"
|
||||
)
|
||||
db.execute('DELETE FROM movies')
|
||||
db.executemany('INSERT INTO movies VALUES (?,?,?)', movies_data)
|
||||
db.execute("DELETE FROM movies")
|
||||
db.executemany("INSERT INTO movies VALUES (?,?,?)", movies_data)
|
||||
|
||||
|
||||
def main():
|
||||
create_csv(SAMPLE_DATA, CSV_FILE)
|
||||
create_sqlite(SAMPLE_DATA, SQLITE_FILE)
|
||||
print('OK')
|
||||
print("OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
@ -10,19 +8,19 @@ from .containers import Container
|
|||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
print('Francis Lawrence movies:')
|
||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||
print('\t-', movie)
|
||||
print("Francis Lawrence movies:")
|
||||
for movie in lister.movies_directed_by("Francis Lawrence"):
|
||||
print("\t-", movie)
|
||||
|
||||
print('2016 movies:')
|
||||
print("2016 movies:")
|
||||
for movie in lister.movies_released_in(2016):
|
||||
print('\t-', movie)
|
||||
print("\t-", movie)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.finder.type.from_env('MOVIE_FINDER_TYPE')
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.config.from_yaml("config.yml")
|
||||
container.config.finder.type.from_env("MOVIE_FINDER_TYPE")
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
|
|
@ -9,7 +9,7 @@ class Movie:
|
|||
self.director = str(director)
|
||||
|
||||
def __repr__(self):
|
||||
return '{0}(title={1}, year={2}, director={3})'.format(
|
||||
return "{0}(title={1}, year={2}, director={3})".format(
|
||||
self.__class__.__name__,
|
||||
repr(self.title),
|
||||
repr(self.year),
|
||||
|
|
|
@ -46,5 +46,5 @@ class SqliteMovieFinder(MovieFinder):
|
|||
|
||||
def find_all(self) -> List[Movie]:
|
||||
with self._database as db:
|
||||
rows = db.execute('SELECT title, year, director FROM movies')
|
||||
rows = db.execute("SELECT title, year, director FROM movies")
|
||||
return [self._movie_factory(*row) for row in rows]
|
||||
|
|
|
@ -11,14 +11,14 @@ from .containers import Container
|
|||
def container():
|
||||
container = Container()
|
||||
container.config.from_dict({
|
||||
'finder': {
|
||||
'type': 'csv',
|
||||
'csv': {
|
||||
'path': '/fake-movies.csv',
|
||||
'delimiter': ',',
|
||||
"finder": {
|
||||
"type": "csv",
|
||||
"csv": {
|
||||
"path": "/fake-movies.csv",
|
||||
"delimiter": ",",
|
||||
},
|
||||
'sqlite': {
|
||||
'path': '/fake-movies.db',
|
||||
"sqlite": {
|
||||
"path": "/fake-movies.db",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -28,23 +28,23 @@ def container():
|
|||
def test_movies_directed_by(container):
|
||||
finder_mock = mock.Mock()
|
||||
finder_mock.find_all.return_value = [
|
||||
container.movie('The 33', 2015, 'Patricia Riggen'),
|
||||
container.movie('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
with container.finder.override(finder_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_directed_by('Jon Favreau')
|
||||
movies = lister.movies_directed_by("Jon Favreau")
|
||||
|
||||
assert len(movies) == 1
|
||||
assert movies[0].title == 'The Jungle Book'
|
||||
assert movies[0].title == "The Jungle Book"
|
||||
|
||||
|
||||
def test_movies_released_in(container):
|
||||
finder_mock = mock.Mock()
|
||||
finder_mock.find_all.return_value = [
|
||||
container.movie('The 33', 2015, 'Patricia Riggen'),
|
||||
container.movie('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
with container.finder.override(finder_mock):
|
||||
|
@ -52,4 +52,4 @@ def test_movies_released_in(container):
|
|||
movies = lister.movies_released_in(2015)
|
||||
|
||||
assert len(movies) == 1
|
||||
assert movies[0].title == 'The 33'
|
||||
assert movies[0].title == "The 33"
|
||||
|
|
|
@ -29,12 +29,12 @@ class Container(containers.DeclarativeContainer):
|
|||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
container = Container(config={'max_workers': 4})
|
||||
if __name__ == "__main__":
|
||||
container = Container(config={"max_workers": 4})
|
||||
|
||||
container.init_resources()
|
||||
|
||||
logging.info('Resources are initialized')
|
||||
logging.info("Resources are initialized")
|
||||
thread_pool = container.thread_pool()
|
||||
thread_pool.map(print, range(10))
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""Wiring example."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -20,8 +18,8 @@ def main(service: Service = Provide[Container.service]) -> None:
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
"""Wiring attribute example."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
|
||||
|
@ -23,9 +21,9 @@ class Main:
|
|||
service: Service = Provide[Container.service]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
assert isinstance(service, Service)
|
||||
assert isinstance(Main.service, Service)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""Wiring container injection example."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -21,8 +19,8 @@ def main(container: Container = Provide[Container]):
|
|||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""Wiring string id example."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -16,12 +14,12 @@ class Container(containers.DeclarativeContainer):
|
|||
|
||||
|
||||
@inject
|
||||
def main(service: Service = Provide['service']) -> None:
|
||||
def main(service: Service = Provide["service"]) -> None:
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main()
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""Flask wiring example."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
from flask import Flask, json
|
||||
|
||||
|
||||
|
@ -18,13 +16,13 @@ class Container(containers.DeclarativeContainer):
|
|||
|
||||
@inject
|
||||
def index_view(service: Service = Provide[Container.service]) -> str:
|
||||
return json.dumps({'service_id': id(service)})
|
||||
return json.dumps({"service_id": id(service)})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
app = Flask(__name__)
|
||||
app.add_url_rule('/', 'index', index_view)
|
||||
app.add_url_rule("/", "index", index_view)
|
||||
app.run()
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""`Resource` - Flask request scope example."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide, Closing
|
||||
from dependency_injector.wiring import Closing, Provide, inject
|
||||
from flask import Flask, current_app
|
||||
|
||||
|
||||
|
@ -12,9 +10,9 @@ class Service:
|
|||
|
||||
|
||||
def init_service() -> Service:
|
||||
print('Init service')
|
||||
print("Init service")
|
||||
yield Service()
|
||||
print('Shutdown service')
|
||||
print("Shutdown service")
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -25,16 +23,16 @@ class Container(containers.DeclarativeContainer):
|
|||
@inject
|
||||
def index_view(service: Service = Closing[Provide[Container.service]]):
|
||||
assert service is current_app.container.service()
|
||||
return 'Hello World!'
|
||||
return "Hello World!"
|
||||
|
||||
|
||||
container = Container()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
app = Flask(__name__)
|
||||
app.container = container
|
||||
app.add_url_rule('/', 'index', view_func=index_view)
|
||||
app.add_url_rule("/", "index", view_func=index_view)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,7 +43,7 @@ class Container:
|
|||
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> None: ...
|
||||
def reset_last_overriding(self) -> None: ...
|
||||
def reset_override(self) -> None: ...
|
||||
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None) -> None: ...
|
||||
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None) -> None: ...
|
||||
def unwire(self) -> None: ...
|
||||
def init_resources(self) -> Optional[Awaitable]: ...
|
||||
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
"""Containers module."""
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import sys
|
||||
import importlib
|
||||
import inspect
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
|
@ -248,11 +251,22 @@ class DynamicContainer(Container):
|
|||
for provider in six.itervalues(self.providers):
|
||||
provider.reset_override()
|
||||
|
||||
def wire(self, modules=None, packages=None):
|
||||
def wire(self, modules=None, packages=None, from_package=None):
|
||||
"""Wire container providers with provided packages and modules.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
modules = [*modules] if modules else []
|
||||
packages = [*packages] if packages else []
|
||||
|
||||
if _any_relative_string_imports_in(modules) or _any_relative_string_imports_in(packages):
|
||||
if from_package is None:
|
||||
with contextlib.suppress(Exception):
|
||||
from_package = _resolve_calling_package_name()
|
||||
|
||||
modules = _resolve_string_imports(modules, from_package)
|
||||
packages = _resolve_string_imports(packages, from_package)
|
||||
|
||||
wire(
|
||||
container=self,
|
||||
modules=modules,
|
||||
|
@ -261,7 +275,6 @@ class DynamicContainer(Container):
|
|||
|
||||
if modules:
|
||||
self.wired_to_modules.extend(modules)
|
||||
|
||||
if packages:
|
||||
self.wired_to_packages.extend(packages)
|
||||
|
||||
|
@ -789,3 +802,27 @@ cpdef object _check_provider_type(object container, object provider):
|
|||
if not isinstance(provider, container.provider_type):
|
||||
raise errors.Error('{0} can contain only {1} '
|
||||
'instances'.format(container, container.provider_type))
|
||||
|
||||
|
||||
cpdef bint _any_relative_string_imports_in(object modules):
|
||||
for module in modules:
|
||||
if not isinstance(module, str):
|
||||
continue
|
||||
if module.startswith("."):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
cpdef list _resolve_string_imports(object modules, object from_package):
|
||||
return [
|
||||
importlib.import_module(module, from_package) if isinstance(module, str) else module
|
||||
for module in modules
|
||||
]
|
||||
|
||||
|
||||
cpdef object _resolve_calling_package_name():
|
||||
stack = inspect.stack()
|
||||
pre_last_frame = stack[0]
|
||||
module = inspect.getmodule(pre_last_frame[0])
|
||||
return module.__package__
|
||||
|
|
|
@ -325,8 +325,7 @@ def wire( # noqa: C901
|
|||
packages: Optional[Iterable[ModuleType]] = None,
|
||||
) -> None:
|
||||
"""Wire container providers with provided packages and modules."""
|
||||
if not modules:
|
||||
modules = []
|
||||
modules = [*modules] if modules else []
|
||||
|
||||
if packages:
|
||||
for package in packages:
|
||||
|
@ -367,8 +366,7 @@ def unwire( # noqa: C901
|
|||
packages: Optional[Iterable[ModuleType]] = None,
|
||||
) -> None:
|
||||
"""Wire provided packages and modules with previous wired providers."""
|
||||
if not modules:
|
||||
modules = []
|
||||
modules = [*modules] if modules else []
|
||||
|
||||
if packages:
|
||||
for package in packages:
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
"""Wiring sample package."""
|
||||
|
||||
|
||||
def wire_with_relative_string_names(container):
|
||||
container.wire(
|
||||
modules=[".module"],
|
||||
packages=[".package"],
|
||||
)
|
|
@ -36,6 +36,7 @@ from asyncutils import AsyncTestCase
|
|||
from wiringsamples import module, package
|
||||
from wiringsamples.service import Service
|
||||
from wiringsamples.container import Container, SubContainer
|
||||
from wiringsamples.wire_relative_string_names import wire_with_relative_string_names
|
||||
|
||||
|
||||
class WiringTest(unittest.TestCase):
|
||||
|
@ -314,7 +315,53 @@ class WiringTest(unittest.TestCase):
|
|||
self.assertIsInstance(service, Service)
|
||||
|
||||
|
||||
class ModuleAsPackagingTest(unittest.TestCase):
|
||||
class WiringWithStringModuleAndPackageNamesTest(unittest.TestCase):
|
||||
|
||||
container: Container
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.container = Container()
|
||||
self.addCleanup(self.container.unwire)
|
||||
|
||||
def test_absolute_names(self):
|
||||
self.container.wire(
|
||||
modules=["wiringsamples.module"],
|
||||
packages=["wiringsamples.package"],
|
||||
)
|
||||
|
||||
service = module.test_function()
|
||||
self.assertIsInstance(service, Service)
|
||||
|
||||
from wiringsamples.package.subpackage.submodule import test_function
|
||||
service = test_function()
|
||||
self.assertIsInstance(service, Service)
|
||||
|
||||
def test_relative_names_with_explicit_package(self):
|
||||
self.container.wire(
|
||||
modules=[".module"],
|
||||
packages=[".package"],
|
||||
from_package="wiringsamples",
|
||||
)
|
||||
|
||||
service = module.test_function()
|
||||
self.assertIsInstance(service, Service)
|
||||
|
||||
from wiringsamples.package.subpackage.submodule import test_function
|
||||
service = test_function()
|
||||
self.assertIsInstance(service, Service)
|
||||
|
||||
def test_relative_names_with_auto_package(self):
|
||||
wire_with_relative_string_names(self.container)
|
||||
|
||||
service = module.test_function()
|
||||
self.assertIsInstance(service, Service)
|
||||
|
||||
from wiringsamples.package.subpackage.submodule import test_function
|
||||
service = test_function()
|
||||
self.assertIsInstance(service, Service)
|
||||
|
||||
|
||||
class ModuleAsPackageTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.container = Container(config={'a': {'b': {'c': 10}}})
|
||||
|
|
Loading…
Reference in New Issue
Block a user