Skip to content

Commit 9653dfc

Browse files
committed
Add sanic example
1 parent e37b518 commit 9653dfc

File tree

12 files changed

+326
-0
lines changed

12 files changed

+326
-0
lines changed

docs/main/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ follows `Semantic versioning`_
1010
4.0.0
1111
-----
1212
- Add ``wiring`` feature.
13+
- Add ``sanic`` example.
1314
- Update ``aiohttp`` example.
1415
- Update ``flask`` example.
1516

examples/miniapps/sanic/README.rst

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
Dependency Injector + Sanic Example
2+
=====================================
3+
4+
Application ``giphynavigator`` is a `Sanic <https://sanic.readthedocs.io/en/latest/index.html>`_ +
5+
`Dependency Injector <http://python-dependency-injector.ets-labs.org/>`_ example application.
6+
7+
Run
8+
---
9+
10+
Create virtual environment:
11+
12+
.. code-block:: bash
13+
14+
virtualenv venv
15+
. venv/bin/activate
16+
17+
Install requirements:
18+
19+
.. code-block:: bash
20+
21+
pip install -r requirements.txt
22+
23+
To run the application do:
24+
25+
.. code-block:: bash
26+
27+
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
28+
python -m giphynavigator
29+
30+
The output should be something like:
31+
32+
.. code-block::
33+
34+
[2020-09-23 18:16:31 -0400] [48258] [INFO] Goin' Fast @ http://0.0.0.0:8000
35+
[2020-09-23 18:16:31 -0400] [48258] [INFO] Starting worker [48258]
36+
37+
After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``,
38+
etc). You should see something like:
39+
40+
.. code-block:: json
41+
42+
{
43+
"query": "Dependency Injector",
44+
"limit": 10,
45+
"gifs": [
46+
{
47+
"url": "https://giphy.com/gifs/boxes-dependent-swbf2-6Eo7KzABxgJMY"
48+
},
49+
{
50+
"url": "https://giphy.com/gifs/depends-J56qCcOhk6hKE"
51+
},
52+
{
53+
"url": "https://giphy.com/gifs/web-series-ccstudios-bro-dependent-1lhU8KAVwmVVu"
54+
},
55+
{
56+
"url": "https://giphy.com/gifs/TheBoysTV-friends-friend-weneedeachother-XxR9qcIwcf5Jq404Sx"
57+
},
58+
{
59+
"url": "https://giphy.com/gifs/netflix-a-series-of-unfortunate-events-asoue-9rgeQXbwoK53pcxn7f"
60+
},
61+
{
62+
"url": "https://giphy.com/gifs/black-and-white-sad-skins-Hs4YzLs2zJuLu"
63+
},
64+
{
65+
"url": "https://giphy.com/gifs/always-there-for-you-i-am-here-PlayjhCco9jHBYrd9w"
66+
},
67+
{
68+
"url": "https://giphy.com/gifs/stream-famous-dollar-YT2dvOByEwXCdoYiA1"
69+
},
70+
{
71+
"url": "https://giphy.com/gifs/i-love-you-there-for-am-1BhGzgpZXYWwWMAGB1"
72+
},
73+
{
74+
"url": "https://giphy.com/gifs/life-like-twerk-9hlnWxjHqmH28"
75+
}
76+
]
77+
}
78+
79+
.. note::
80+
81+
To create your own Giphy API key follow this
82+
`guide <https://support.giphy.com/hc/en-us/articles/360020283431-Request-A-GIPHY-API-Key>`_.
83+
84+
Test
85+
----
86+
87+
This application comes with the unit tests.
88+
89+
To run the tests do:
90+
91+
.. code-block:: bash
92+
93+
py.test giphynavigator/tests.py --cov=giphynavigator
94+
95+
The output should be something like:
96+
97+
.. code-block::
98+
99+
platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
100+
plugins: cov-2.10.0, sanic-1.6.1
101+
collected 3 items
102+
103+
giphynavigator/tests.py ... [100%]
104+
105+
---------- coverage: platform darwin, python 3.8.3-final-0 -----------
106+
Name Stmts Miss Cover
107+
---------------------------------------------------
108+
giphynavigator/__init__.py 0 0 100%
109+
giphynavigator/__main__.py 4 4 0%
110+
giphynavigator/application.py 12 0 100%
111+
giphynavigator/containers.py 6 0 100%
112+
giphynavigator/giphy.py 14 9 36%
113+
giphynavigator/handlers.py 10 0 100%
114+
giphynavigator/services.py 9 1 89%
115+
giphynavigator/tests.py 34 0 100%
116+
---------------------------------------------------
117+
TOTAL 89 14 84%

examples/miniapps/sanic/config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
giphy:
2+
request_timeout: 10
3+
default:
4+
query: "Dependency Injector"
5+
limit: 10
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Top-level package."""
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""Main module."""
2+
3+
from .application import create_app
4+
5+
6+
if __name__ == '__main__':
7+
app = create_app()
8+
app.run(host='0.0.0.0', port=8000, debug=True)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Application module."""
2+
3+
from sanic import Sanic
4+
5+
from .containers import Container
6+
from . import handlers
7+
8+
9+
def create_app():
10+
"""Create and return aiohttp application."""
11+
container = Container()
12+
container.config.from_yaml('config.yml')
13+
container.config.giphy.api_key.from_env('GIPHY_API_KEY')
14+
15+
container.wire(modules=[handlers])
16+
17+
app = Sanic('Giphy Navigator')
18+
app.container = container
19+
20+
app.add_route(handlers.index, '/')
21+
22+
return app
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""Containers module."""
2+
3+
from dependency_injector import containers, providers
4+
5+
from . import giphy, services
6+
7+
8+
class Container(containers.DeclarativeContainer):
9+
10+
config = providers.Configuration()
11+
12+
giphy_client = providers.Factory(
13+
giphy.GiphyClient,
14+
api_key=config.giphy.api_key,
15+
timeout=config.giphy.request_timeout,
16+
)
17+
18+
search_service = providers.Factory(
19+
services.SearchService,
20+
giphy_client=giphy_client,
21+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Giphy client module."""
2+
3+
from aiohttp import ClientSession, ClientTimeout
4+
5+
6+
class GiphyClient:
7+
8+
API_URL = 'http://api.giphy.com/v1'
9+
10+
def __init__(self, api_key, timeout):
11+
self._api_key = api_key
12+
self._timeout = ClientTimeout(timeout)
13+
14+
async def search(self, query, limit):
15+
"""Make search API call and return result."""
16+
url = f'{self.API_URL}/gifs/search'
17+
params = {
18+
'q': query,
19+
'api_key': self._api_key,
20+
'limit': limit,
21+
}
22+
async with ClientSession(timeout=self._timeout) as session:
23+
async with session.get(url, params=params) as response:
24+
if response.status != 200:
25+
response.raise_for_status()
26+
return await response.json()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Handlers module."""
2+
3+
from sanic.request import Request
4+
from sanic.response import HTTPResponse, json
5+
from dependency_injector.wiring import Provide
6+
7+
from .services import SearchService
8+
from .containers import Container
9+
10+
11+
async def index(
12+
request: Request,
13+
search_service: SearchService = Provide[Container.search_service],
14+
default_query: str = Provide[Container.config.default.query],
15+
default_limit: int = Provide[Container.config.default.limit.as_int()],
16+
) -> HTTPResponse:
17+
query = request.args.get('query', default_query)
18+
limit = int(request.args.get('limit', default_limit))
19+
20+
gifs = await search_service.search(query, limit)
21+
22+
return json(
23+
{
24+
'query': query,
25+
'limit': limit,
26+
'gifs': gifs,
27+
},
28+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""Services module."""
2+
3+
from .giphy import GiphyClient
4+
5+
6+
class SearchService:
7+
8+
def __init__(self, giphy_client: GiphyClient):
9+
self._giphy_client = giphy_client
10+
11+
async def search(self, query, limit):
12+
"""Search for gifs and return formatted data."""
13+
if not query:
14+
return []
15+
16+
result = await self._giphy_client.search(query, limit)
17+
18+
return [{'url': gif['url']} for gif in result['data']]

0 commit comments

Comments
 (0)