mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +03:00
Aiohttp integration (#270)
* Add aiohttp extension module * Add giphynav-aiohttp app * Add missing docstrings * Remove print() call * Remove not needed import from flask extension tests * Improve coroutine provider tests * Add aiohttp extension tests * Update tox.ini * Add aiohttp extras * Try fix Python 3.4 tests * Try fix 3.6 tests * Stop running coroutine tests for Python 3.4 * Rename tests * Remove type hints * Fix pypy and change python version for coverage job to 3.8 * Fix coveralls job * Try fix Python 3.4, 3.5 tests * Make coverage job to run 3.5+ tests * Add tests * Add readme * Update the readmes * Add API docs * Add API docs page * Update changelog
This commit is contained in:
parent
bed547cc91
commit
e0d81c2d28
|
@ -3,7 +3,7 @@ dist: xenial
|
|||
language: python
|
||||
jobs:
|
||||
include:
|
||||
- python: 3.6
|
||||
- python: 3.8
|
||||
env: TOXENV=coveralls DEPENDENCY_INJECTOR_DEBUG_MODE=1
|
||||
install:
|
||||
- pip install tox
|
||||
|
|
9
docs/api/aiohttpext.rst
Normal file
9
docs/api/aiohttpext.rst
Normal file
|
@ -0,0 +1,9 @@
|
|||
dependency_injector.ext.aiohttp
|
||||
===============================
|
||||
|
||||
.. automodule:: dependency_injector.ext.aiohttp
|
||||
:members:
|
||||
:special-members:
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -8,4 +8,5 @@ API Documentation
|
|||
providers
|
||||
containers
|
||||
errors
|
||||
aiohttpext
|
||||
flaskext
|
||||
|
|
|
@ -7,6 +7,11 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
Development version
|
||||
-------------------
|
||||
- Add ``Aiohttp`` integration module ``dependency_injector.ext.aiohttp``.
|
||||
- Add ``Aiohttp`` + ``Dependency Injector`` example ``giphynav-aiohttp``.
|
||||
|
||||
3.23.2
|
||||
------
|
||||
- Fix ``Flask`` tutorial code issues, typos and change some wording.
|
||||
|
@ -48,7 +53,7 @@ follows `Semantic versioning`_
|
|||
|
||||
3.20.0
|
||||
------
|
||||
- Add ``Flask`` integration module ``dependency_injector.flask.ext``.
|
||||
- Add ``Flask`` integration module ``dependency_injector.ext.flask``.
|
||||
- Add ``Flask`` + ``Dependency Injector`` example ``ghnav-flask``.
|
||||
- Add ``Factory.provides`` attribute. It is an alias to the ``Factory.cls``.
|
||||
- New README.
|
||||
|
|
|
@ -68,7 +68,7 @@ After that visit http://127.0.0.1:5000/ in your browser.
|
|||
Test
|
||||
----
|
||||
|
||||
This application comes with unit tests.
|
||||
This application comes with the unit tests.
|
||||
|
||||
To run the tests do:
|
||||
|
||||
|
|
117
examples/miniapps/giphynav-aiohttp/README.rst
Normal file
117
examples/miniapps/giphynav-aiohttp/README.rst
Normal file
|
@ -0,0 +1,117 @@
|
|||
Aiohttp Dependency Injection Example
|
||||
====================================
|
||||
|
||||
Application ``giphynavigator`` is a `Aiohttp <https://docs.aiohttp.org/>`_ +
|
||||
`Dependency Injector <http://python-dependency-injector.ets-labs.org/>`_ application.
|
||||
|
||||
Run
|
||||
---
|
||||
|
||||
Create virtual environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
virtualenv venv
|
||||
. venv/bin/activate
|
||||
|
||||
Install requirements:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
To run the application do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
|
||||
adev runserver giphynavigator/application.py --livereload
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
[18:52:59] Starting aux server at http://localhost:8001 ◆
|
||||
[18:52:59] Starting dev server at http://localhost:8000 ●
|
||||
|
||||
After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``,
|
||||
etc). You should see something like:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"query": "Dependency Injector",
|
||||
"limit": 10,
|
||||
"gifs": [
|
||||
{
|
||||
"url": "https://giphy.com/gifs/boxes-dependent-swbf2-6Eo7KzABxgJMY"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/depends-J56qCcOhk6hKE"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/web-series-ccstudios-bro-dependent-1lhU8KAVwmVVu"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/TheBoysTV-friends-friend-weneedeachother-XxR9qcIwcf5Jq404Sx"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/netflix-a-series-of-unfortunate-events-asoue-9rgeQXbwoK53pcxn7f"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/black-and-white-sad-skins-Hs4YzLs2zJuLu"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/always-there-for-you-i-am-here-PlayjhCco9jHBYrd9w"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/stream-famous-dollar-YT2dvOByEwXCdoYiA1"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/i-love-you-there-for-am-1BhGzgpZXYWwWMAGB1"
|
||||
},
|
||||
{
|
||||
"url": "https://giphy.com/gifs/life-like-twerk-9hlnWxjHqmH28"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
To create your own Giphy API key follow this
|
||||
`guide <https://support.giphy.com/hc/en-us/articles/360020283431-Request-A-GIPHY-API-Key>`_.
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
This application comes with the unit tests.
|
||||
|
||||
To run the tests do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
py.test giphynavigator/tests.py --cov=giphynavigator
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
|
||||
plugins: cov-2.10.0, aiohttp-0.3.0, asyncio-0.14.0
|
||||
collected 3 items
|
||||
|
||||
giphynavigator/tests.py ... [100%]
|
||||
|
||||
---------- coverage: platform darwin, python 3.8.3-final-0 -----------
|
||||
Name Stmts Miss Cover
|
||||
---------------------------------------------------
|
||||
giphynavigator/__init__.py 0 0 100%
|
||||
giphynavigator/__main__.py 5 5 0%
|
||||
giphynavigator/application.py 10 0 100%
|
||||
giphynavigator/containers.py 10 0 100%
|
||||
giphynavigator/giphy.py 16 11 31%
|
||||
giphynavigator/services.py 9 1 89%
|
||||
giphynavigator/tests.py 35 0 100%
|
||||
giphynavigator/views.py 7 0 100%
|
||||
---------------------------------------------------
|
||||
TOTAL 92 17 82%
|
5
examples/miniapps/giphynav-aiohttp/config.yml
Normal file
5
examples/miniapps/giphynav-aiohttp/config.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
giphy:
|
||||
request_timeout: 10
|
||||
search:
|
||||
default_query: "Dependency Injector"
|
||||
default_limit: 10
|
|
@ -0,0 +1 @@
|
|||
"""Top-level package."""
|
|
@ -0,0 +1,10 @@
|
|||
"""Main module."""
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from .application import create_app
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = create_app()
|
||||
web.run_app(app)
|
|
@ -0,0 +1,21 @@
|
|||
"""Application module."""
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from .containers import ApplicationContainer
|
||||
|
||||
|
||||
def create_app():
|
||||
"""Create and return Flask application."""
|
||||
container = ApplicationContainer()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.giphy.api_key.from_env('GIPHY_API_KEY')
|
||||
|
||||
app: web.Application = container.app()
|
||||
app.container = container
|
||||
|
||||
app.add_routes([
|
||||
web.get('/', container.index_view.as_view()),
|
||||
])
|
||||
|
||||
return app
|
|
@ -0,0 +1,33 @@
|
|||
"""Application containers module."""
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.ext import aiohttp
|
||||
|
||||
from aiohttp import web
|
||||
from . import giphy, services, views
|
||||
|
||||
|
||||
class ApplicationContainer(containers.DeclarativeContainer):
|
||||
"""Application container."""
|
||||
|
||||
app = aiohttp.Application(web.Application)
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
giphy_client = providers.Factory(
|
||||
giphy.GiphyClient,
|
||||
api_key=config.giphy.api_key,
|
||||
timeout=config.giphy.request_timeout,
|
||||
)
|
||||
|
||||
search_service = providers.Factory(
|
||||
services.SearchService,
|
||||
giphy_client=giphy_client,
|
||||
)
|
||||
|
||||
index_view = aiohttp.View(
|
||||
views.index,
|
||||
search_service=search_service,
|
||||
default_query=config.search.default_query,
|
||||
default_limit=config.search.default_limit,
|
||||
)
|
29
examples/miniapps/giphynav-aiohttp/giphynavigator/giphy.py
Normal file
29
examples/miniapps/giphynav-aiohttp/giphynavigator/giphy.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
"""Giphy client module."""
|
||||
|
||||
from aiohttp import ClientSession, ClientTimeout
|
||||
|
||||
|
||||
class GiphyClient:
|
||||
|
||||
API_URL = 'http://api.giphy.com/v1'
|
||||
|
||||
def __init__(self, api_key, timeout):
|
||||
self._api_key = api_key
|
||||
self._timeout = ClientTimeout(timeout)
|
||||
|
||||
async def search(self, query, limit):
|
||||
"""Make search API call and return result."""
|
||||
if not query:
|
||||
return []
|
||||
|
||||
url = f'{self.API_URL}/gifs/search'
|
||||
params = {
|
||||
'q': query,
|
||||
'api_key': self._api_key,
|
||||
'limit': limit,
|
||||
}
|
||||
async with ClientSession(timeout=self._timeout) as session:
|
||||
async with session.get(url, params=params) as response:
|
||||
if response.status != 200:
|
||||
response.raise_for_status()
|
||||
return await response.json()
|
|
@ -0,0 +1,18 @@
|
|||
"""Services module."""
|
||||
|
||||
from .giphy import GiphyClient
|
||||
|
||||
|
||||
class SearchService:
|
||||
|
||||
def __init__(self, giphy_client: GiphyClient):
|
||||
self._giphy_client = giphy_client
|
||||
|
||||
async def search(self, query, limit):
|
||||
"""Search for gifs and return formatted data."""
|
||||
if not query:
|
||||
return []
|
||||
|
||||
result = await self._giphy_client.search(query, limit)
|
||||
|
||||
return [{'url': gif['url']} for gif in result['data']]
|
77
examples/miniapps/giphynav-aiohttp/giphynavigator/tests.py
Normal file
77
examples/miniapps/giphynav-aiohttp/giphynavigator/tests.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
"""Tests module."""
|
||||
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from giphynavigator.application import create_app
|
||||
from giphynavigator.giphy import GiphyClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
return create_app()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(app, aiohttp_client, loop):
|
||||
return loop.run_until_complete(aiohttp_client(app))
|
||||
|
||||
|
||||
async def test_index(client, app):
|
||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||
giphy_client_mock.search.return_value = {
|
||||
'data': [
|
||||
{'url': 'https://giphy/gif1.gif'},
|
||||
{'url': 'https://giphy/gif2.gif'},
|
||||
],
|
||||
}
|
||||
|
||||
with app.container.giphy_client.override(giphy_client_mock):
|
||||
response = await client.get(
|
||||
'/',
|
||||
params={
|
||||
'query': 'test',
|
||||
'limit': 10,
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
data = await response.json()
|
||||
assert data == {
|
||||
'query': 'test',
|
||||
'limit': 10,
|
||||
'gifs': [
|
||||
{'url': 'https://giphy/gif1.gif'},
|
||||
{'url': 'https://giphy/gif2.gif'},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
async def test_index_no_data(client, app):
|
||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||
giphy_client_mock.search.return_value = {
|
||||
'data': [],
|
||||
}
|
||||
|
||||
with app.container.giphy_client.override(giphy_client_mock):
|
||||
response = await client.get('/')
|
||||
|
||||
assert response.status == 200
|
||||
data = await response.json()
|
||||
assert data['gifs'] == []
|
||||
|
||||
|
||||
async def test_index_default_params(client, app):
|
||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||
giphy_client_mock.search.return_value = {
|
||||
'data': [],
|
||||
}
|
||||
|
||||
with app.container.giphy_client.override(giphy_client_mock):
|
||||
response = await client.get('/')
|
||||
|
||||
assert response.status == 200
|
||||
data = await response.json()
|
||||
assert data['query'] == app.container.config.search.default_query()
|
||||
assert data['limit'] == app.container.config.search.default_limit()
|
25
examples/miniapps/giphynav-aiohttp/giphynavigator/views.py
Normal file
25
examples/miniapps/giphynav-aiohttp/giphynavigator/views.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
"""Views module."""
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from .services import SearchService
|
||||
|
||||
|
||||
async def index(
|
||||
request: web.Request,
|
||||
search_service: SearchService,
|
||||
default_query: str,
|
||||
default_limit: int,
|
||||
) -> web.Response:
|
||||
query = request.query.get('query', default_query)
|
||||
limit = int(request.query.get('limit', default_limit))
|
||||
|
||||
gifs = await search_service.search(query, limit)
|
||||
|
||||
return web.json_response(
|
||||
{
|
||||
'query': query,
|
||||
'limit': limit,
|
||||
'gifs': gifs,
|
||||
},
|
||||
)
|
6
examples/miniapps/giphynav-aiohttp/requirements.txt
Normal file
6
examples/miniapps/giphynav-aiohttp/requirements.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
dependency-injector
|
||||
aiohttp
|
||||
aiohttp-devtools
|
||||
pyyaml
|
||||
pytest-aiohttp
|
||||
pytest-cov
|
|
@ -1 +1,2 @@
|
|||
flask
|
||||
aiohttp
|
||||
|
|
3
setup.py
3
setup.py
|
@ -67,6 +67,9 @@ setup(name='dependency-injector',
|
|||
'flask': [
|
||||
'flask',
|
||||
],
|
||||
'aiohttp': [
|
||||
'aiohttp',
|
||||
],
|
||||
},
|
||||
zip_safe=True,
|
||||
license='BSD New',
|
||||
|
|
46
src/dependency_injector/ext/aiohttp.py
Normal file
46
src/dependency_injector/ext/aiohttp.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
"""Aiohttp extension module."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
class Application(providers.Singleton):
|
||||
"""Aiohttp application provider."""
|
||||
|
||||
|
||||
class Extension(providers.Singleton):
|
||||
"""Aiohttp extension provider."""
|
||||
|
||||
|
||||
class Middleware(providers.DelegatedCallable):
|
||||
"""Aiohttp middleware provider."""
|
||||
|
||||
__middleware_version__ = 1
|
||||
|
||||
|
||||
class MiddlewareFactory(providers.Factory):
|
||||
"""Aiohttp middleware factory provider."""
|
||||
|
||||
|
||||
class View(providers.Callable):
|
||||
"""Aiohttp view provider."""
|
||||
|
||||
def as_view(self):
|
||||
"""Return aiohttp view function."""
|
||||
@functools.wraps(self.provides)
|
||||
async def _view(request, *args, **kwargs):
|
||||
return await self.__call__(request, *args, **kwargs)
|
||||
return _view
|
||||
|
||||
|
||||
class ClassBasedView(providers.Factory):
|
||||
"""Aiohttp class-based view provider."""
|
||||
|
||||
def as_view(self):
|
||||
"""Return aiohttp view function."""
|
||||
async def _view(request, *args, **kwargs):
|
||||
return await self.__call__(request, *args, **kwargs)
|
||||
return _view
|
93
tests/unit/ext/test_aiohttp_py35.py
Normal file
93
tests/unit/ext/test_aiohttp_py35.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
"""Dependency injector Aiohttp extension unit tests."""
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.ext import aiohttp
|
||||
|
||||
|
||||
async def index(_):
|
||||
return web.Response(text='Hello World!')
|
||||
|
||||
|
||||
async def test(_):
|
||||
return web.Response(text='Test!')
|
||||
|
||||
|
||||
class Test(web.View):
|
||||
async def get(self):
|
||||
return web.Response(text='Test class-based!')
|
||||
|
||||
|
||||
@web.middleware
|
||||
async def middleware(request, handler):
|
||||
resp = await handler(request)
|
||||
resp.text = resp.text + ' wink1'
|
||||
return resp
|
||||
|
||||
|
||||
def middleware_factory(text):
|
||||
@web.middleware
|
||||
async def sample_middleware(request, handler):
|
||||
resp = await handler(request)
|
||||
resp.text = resp.text + text
|
||||
return resp
|
||||
return sample_middleware
|
||||
|
||||
|
||||
class ApplicationContainer(containers.DeclarativeContainer):
|
||||
|
||||
app = aiohttp.Application(
|
||||
web.Application,
|
||||
middlewares=providers.List(
|
||||
aiohttp.Middleware(middleware),
|
||||
aiohttp.MiddlewareFactory(middleware_factory, text=' wink2'),
|
||||
),
|
||||
)
|
||||
|
||||
index_view = aiohttp.View(index)
|
||||
test_view = aiohttp.View(test)
|
||||
test_class_view = aiohttp.ClassBasedView(Test)
|
||||
|
||||
|
||||
class ApplicationTests(AioHTTPTestCase):
|
||||
|
||||
async def get_application(self):
|
||||
"""
|
||||
Override the get_app method to return your application.
|
||||
"""
|
||||
container = ApplicationContainer()
|
||||
app = container.app()
|
||||
app.container = container
|
||||
app.add_routes([
|
||||
web.get('/', container.index_view.as_view()),
|
||||
web.get('/test', container.test_view.as_view(), name='test'),
|
||||
web.get('/test-class', container.test_class_view.as_view()),
|
||||
])
|
||||
return app
|
||||
|
||||
@unittest_run_loop
|
||||
async def test_index(self):
|
||||
response = await self.client.get('/')
|
||||
|
||||
self.assertEqual(response.status, 200)
|
||||
self.assertEqual(await response.text(), 'Hello World! wink2 wink1')
|
||||
|
||||
@unittest_run_loop
|
||||
async def test_test(self):
|
||||
response = await self.client.get('/test')
|
||||
|
||||
self.assertEqual(response.status, 200)
|
||||
self.assertEqual(await response.text(), 'Test! wink2 wink1')
|
||||
|
||||
@unittest_run_loop
|
||||
async def test_test_class_based(self):
|
||||
response = await self.client.get('/test-class')
|
||||
|
||||
self.assertEqual(response.status, 200)
|
||||
self.assertEqual(await response.text(), 'Test class-based! wink2 wink1')
|
||||
|
||||
@unittest_run_loop
|
||||
async def test_endpoints(self):
|
||||
self.assertEqual(str(self.app.router['test'].url_for()), '/test')
|
|
@ -4,7 +4,7 @@ import unittest2 as unittest
|
|||
from flask import Flask, url_for
|
||||
from flask.views import MethodView
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector import containers
|
||||
from dependency_injector.ext import flask
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
"""Dependency injector coroutine providers unit tests."""
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import sys
|
||||
import gc
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
|
@ -10,20 +13,65 @@ from dependency_injector import (
|
|||
)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _example(arg1, arg2, arg3, arg4):
|
||||
async def _example(arg1, arg2, arg3, arg4):
|
||||
future = asyncio.Future()
|
||||
future.set_result(None)
|
||||
yield from future
|
||||
await future
|
||||
return arg1, arg2, arg3, arg4
|
||||
|
||||
|
||||
def _run(coro):
|
||||
def run(main):
|
||||
loop = asyncio.get_event_loop()
|
||||
return loop.run_until_complete(coro)
|
||||
return loop.run_until_complete(main)
|
||||
|
||||
|
||||
class CoroutineTests(unittest.TestCase):
|
||||
def setup_test_loop(
|
||||
loop_factory=asyncio.new_event_loop
|
||||
) -> asyncio.AbstractEventLoop:
|
||||
loop = loop_factory()
|
||||
try:
|
||||
module = loop.__class__.__module__
|
||||
skip_watcher = 'uvloop' in module
|
||||
except AttributeError: # pragma: no cover
|
||||
# Just in case
|
||||
skip_watcher = True
|
||||
asyncio.set_event_loop(loop)
|
||||
if sys.platform != "win32" and not skip_watcher:
|
||||
policy = asyncio.get_event_loop_policy()
|
||||
watcher = asyncio.SafeChildWatcher() # type: ignore
|
||||
watcher.attach_loop(loop)
|
||||
with contextlib.suppress(NotImplementedError):
|
||||
policy.set_child_watcher(watcher)
|
||||
return loop
|
||||
|
||||
|
||||
def teardown_test_loop(loop: asyncio.AbstractEventLoop,
|
||||
fast: bool=False) -> None:
|
||||
closed = loop.is_closed()
|
||||
if not closed:
|
||||
loop.call_soon(loop.stop)
|
||||
loop.run_forever()
|
||||
loop.close()
|
||||
|
||||
if not fast:
|
||||
gc.collect()
|
||||
|
||||
asyncio.set_event_loop(None)
|
||||
|
||||
|
||||
class AsyncTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.loop = setup_test_loop()
|
||||
|
||||
def tearDown(self):
|
||||
teardown_test_loop(self.loop)
|
||||
|
||||
def _run(self, f):
|
||||
return self.loop.run_until_complete(f)
|
||||
|
||||
|
||||
class CoroutineTests(AsyncTestCase):
|
||||
|
||||
def test_init_with_coroutine(self):
|
||||
self.assertTrue(providers.Coroutine(_example))
|
||||
|
@ -33,34 +81,34 @@ class CoroutineTests(unittest.TestCase):
|
|||
|
||||
def test_call_with_positional_args(self):
|
||||
provider = providers.Coroutine(_example, 1, 2, 3, 4)
|
||||
self.assertTupleEqual(_run(provider()), (1, 2, 3, 4))
|
||||
self.assertTupleEqual(self._run(provider()), (1, 2, 3, 4))
|
||||
|
||||
def test_call_with_keyword_args(self):
|
||||
provider = providers.Coroutine(_example,
|
||||
arg1=1, arg2=2, arg3=3, arg4=4)
|
||||
self.assertTupleEqual(_run(provider()), (1, 2, 3, 4))
|
||||
self.assertTupleEqual(self._run(provider()), (1, 2, 3, 4))
|
||||
|
||||
def test_call_with_positional_and_keyword_args(self):
|
||||
provider = providers.Coroutine(_example,
|
||||
1, 2,
|
||||
arg3=3, arg4=4)
|
||||
self.assertTupleEqual(_run(provider()), (1, 2, 3, 4))
|
||||
self.assertTupleEqual(run(provider()), (1, 2, 3, 4))
|
||||
|
||||
def test_call_with_context_args(self):
|
||||
provider = providers.Coroutine(_example, 1, 2)
|
||||
self.assertTupleEqual(_run(provider(3, 4)), (1, 2, 3, 4))
|
||||
self.assertTupleEqual(self._run(provider(3, 4)), (1, 2, 3, 4))
|
||||
|
||||
def test_call_with_context_kwargs(self):
|
||||
provider = providers.Coroutine(_example, arg1=1)
|
||||
self.assertTupleEqual(
|
||||
_run(provider(arg2=2, arg3=3, arg4=4)),
|
||||
self._run(provider(arg2=2, arg3=3, arg4=4)),
|
||||
(1, 2, 3, 4),
|
||||
)
|
||||
|
||||
def test_call_with_context_args_and_kwargs(self):
|
||||
provider = providers.Coroutine(_example, 1)
|
||||
self.assertTupleEqual(
|
||||
_run(provider(2, arg3=3, arg4=4)),
|
||||
self._run(provider(2, arg3=3, arg4=4)),
|
||||
(1, 2, 3, 4),
|
||||
)
|
||||
|
||||
|
@ -69,7 +117,7 @@ class CoroutineTests(unittest.TestCase):
|
|||
.add_args(1, 2) \
|
||||
.add_kwargs(arg3=3, arg4=4)
|
||||
|
||||
self.assertTupleEqual(_run(provider()), (1, 2, 3, 4))
|
||||
self.assertTupleEqual(self._run(provider()), (1, 2, 3, 4))
|
||||
|
||||
def test_set_args(self):
|
||||
provider = providers.Coroutine(_example) \
|
||||
|
@ -213,7 +261,7 @@ class DelegatedCoroutineTests(unittest.TestCase):
|
|||
hex(id(provider))))
|
||||
|
||||
|
||||
class AbstractCoroutineTests(unittest.TestCase):
|
||||
class AbstractCoroutineTests(AsyncTestCase):
|
||||
|
||||
def test_inheritance(self):
|
||||
self.assertIsInstance(providers.AbstractCoroutine(_example),
|
||||
|
@ -227,7 +275,7 @@ class AbstractCoroutineTests(unittest.TestCase):
|
|||
provider = providers.AbstractCoroutine(_abstract_example)
|
||||
provider.override(providers.Coroutine(_example))
|
||||
|
||||
self.assertTrue(_run(provider(1, 2, 3, 4)), (1, 2, 3, 4))
|
||||
self.assertTrue(self._run(provider(1, 2, 3, 4)), (1, 2, 3, 4))
|
||||
|
||||
def test_call_overridden_by_delegated_coroutine(self):
|
||||
@asyncio.coroutine
|
||||
|
@ -237,7 +285,7 @@ class AbstractCoroutineTests(unittest.TestCase):
|
|||
provider = providers.AbstractCoroutine(_abstract_example)
|
||||
provider.override(providers.DelegatedCoroutine(_example))
|
||||
|
||||
self.assertTrue(_run(provider(1, 2, 3, 4)), (1, 2, 3, 4))
|
||||
self.assertTrue(self._run(provider(1, 2, 3, 4)), (1, 2, 3, 4))
|
||||
|
||||
def test_call_not_overridden(self):
|
||||
provider = providers.AbstractCoroutine(_example)
|
15
tox.ini
15
tox.ini
|
@ -8,12 +8,13 @@ deps=
|
|||
extras=
|
||||
yaml
|
||||
flask
|
||||
aiohttp
|
||||
commands=
|
||||
unit2 discover -s tests/unit -p test_*_py3.py
|
||||
unit2 discover -s tests/unit -p test_*_py3*.py
|
||||
|
||||
[testenv:coveralls]
|
||||
passenv=TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH DEPENDENCY_INJECTOR_DEBUG_MODE
|
||||
basepython=python3.6
|
||||
basepython=python3.8
|
||||
usedevelop=True
|
||||
deps=
|
||||
{[testenv]deps}
|
||||
|
@ -22,19 +23,27 @@ deps=
|
|||
coveralls
|
||||
commands=
|
||||
coverage erase
|
||||
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*_py3.py
|
||||
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*_py35.py
|
||||
coverage report --rcfile=./.coveragerc
|
||||
coveralls
|
||||
|
||||
[testenv:py27]
|
||||
extras=
|
||||
yaml
|
||||
flask
|
||||
commands=
|
||||
unit2 discover -s tests/unit -p test_*_py2_py3.py
|
||||
|
||||
[testenv:py34]
|
||||
commands=
|
||||
unit2 discover -s tests/unit -p test_*_py3.py
|
||||
extras=
|
||||
flask
|
||||
|
||||
[testenv:pypy]
|
||||
extras=
|
||||
yaml
|
||||
flask
|
||||
commands=
|
||||
unit2 discover -s tests/unit -p test_*_py2_py3.py
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user