{"id":307879,"date":"2020-08-02T21:00:39","date_gmt":"2020-08-02T21:00:39","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=307879"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=307879","title":{"rendered":"Aiohttp + Dependency Injector \u2014 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044e dependency injection"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/513510\/\">\u041f\u0440\u0438\u0432\u0435\u0442,<\/p>\n<p>  \u042f \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044c <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\" rel=\"nofollow\">Dependency Injector<\/a>. \u042d\u0442\u043e dependency injection \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f Python.<\/p>\n<p>  \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e \u0441\u0435\u0440\u0438\u044e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432 \u043f\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044e Dependency Injector \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0435 \u0445\u043e\u0447\u0443 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c Dependency Injector \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 <code>aiohttp<\/code> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>  \u0420\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0442\u0430\u043a\u0438\u0445 \u0447\u0430\u0441\u0442\u0435\u0439:<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<ol>\n<li><a href=\"#what-we-are-going-to-build\">\u0427\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0442\u0440\u043e\u0438\u0442\u044c?<\/a><\/li>\n<li><a href=\"#prepare-the-env\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/a><\/li>\n<li><a href=\"#structure\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/li>\n<li><a href=\"#install-requirements\">\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439<\/a><\/li>\n<li><a href=\"#minimal-app\">\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a><\/li>\n<li><a href=\"#giphy-api-client\">Giphy API \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/li>\n<li><a href=\"#search-service\">\u0421\u0435\u0440\u0432\u0438\u0441 \u043f\u043e\u0438\u0441\u043a\u0430<\/a><\/li>\n<li><a href=\"#enable-search\">\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043f\u043e\u0438\u0441\u043a<\/a><\/li>\n<li><a href=\"#refactoring\">\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0430<\/a><\/li>\n<li><a href=\"#tests\">\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0435\u0441\u0442\u044b<\/a><\/li>\n<li><a href=\"#conclusion\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/li>\n<\/ol>\n<p>  \u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\/tree\/master\/examples\/miniapps\/giphynav-aiohttp\" rel=\"nofollow\">Github<\/a>.<\/p>\n<p>  \u0414\u043b\u044f \u0441\u0442\u0430\u0440\u0442\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u043c\u0435\u0442\u044c:<\/p>\n<ul>\n<li>Python 3.5+<\/li>\n<li>Virtual environment<\/li>\n<\/ul>\n<p>  \u0418 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u043c\u0435\u0442\u044c:<\/p>\n<ul>\n<li>\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0432\u044b\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e aiohttp<\/li>\n<li>\u041e\u0431\u0449\u0435\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 dependency injection<\/li>\n<\/ul>\n<p>  <a name=\"what-we-are-going-to-build\"><\/a><\/p>\n<h3>\u0427\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0442\u0440\u043e\u0438\u0442\u044c?<\/h3>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/451\/61b\/381\/45161b3814e7b0f3b56004fbbeff0150.gif\"><\/p>\n<p>  \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0442\u0440\u043e\u0438\u0442\u044c REST API \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0438\u0449\u0435\u0442 \u0437\u0430\u0431\u0430\u0432\u043d\u044b\u0435 \u0433\u0438\u0444\u043a\u0438 \u043d\u0430 <a href=\"https:\/\/giphy.com\/\" rel=\"nofollow\">Giphy<\/a>. \u041d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e Giphy Navigator.<\/p>\n<p>  \u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 Giphy Navigator?<\/p>\n<ul>\n<li>\u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044f \u0447\u0442\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432\u0435\u0440\u043d\u0443\u0442\u044c.<\/li>\n<li>Giphy Navigator \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 json.<\/li>\n<li>\u041e\u0442\u0432\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442:<br \/> \n<ul>\n<li>\u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441<\/li>\n<li>\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432<\/li>\n<li>\u0441\u043f\u0438\u0441\u043e\u043a url \u0433\u0438\u0444\u043e\u043a<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>  \u041f\u0440\u0438\u043c\u0435\u0440 \u043e\u0442\u0432\u0435\u0442\u0430:<\/p>\n<pre><code class=\"json\">{     &quot;query&quot;: &quot;Dependency Injector&quot;,     &quot;limit&quot;: 10,     &quot;gifs&quot;: [         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/boxes-dependent-swbf2-6Eo7KzABxgJMY&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/depends-J56qCcOhk6hKE&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/web-series-ccstudios-bro-dependent-1lhU8KAVwmVVu&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/TheBoysTV-friends-friend-weneedeachother-XxR9qcIwcf5Jq404Sx&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/netflix-a-series-of-unfortunate-events-asoue-9rgeQXbwoK53pcxn7f&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/black-and-white-sad-skins-Hs4YzLs2zJuLu&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/always-there-for-you-i-am-here-PlayjhCco9jHBYrd9w&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/stream-famous-dollar-YT2dvOByEwXCdoYiA1&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/i-love-you-there-for-am-1BhGzgpZXYWwWMAGB1&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/life-like-twerk-9hlnWxjHqmH28&quot;         }     ] } <\/code><\/pre>\n<p>  <a name=\"prepare-the-env\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435<\/h3>\n<p>  \u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 virtual environment:<\/p>\n<pre><code class=\"bash\">mkdir giphynav-aiohttp-tutorial cd giphynav-aiohttp-tutorial python3 -m venv venv <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c virtual environment:<\/p>\n<pre><code class=\"bash\">. venv\/bin\/activate <\/code><\/pre>\n<p>  \u041e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>  <a name=\"structure\"><\/a><\/p>\n<h3>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043f\u0430\u043f\u043a\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. \u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u043c\u0438.<\/p>\n<p>  \u041d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 giphynavigator\/ \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u2514\u2500\u2500 views.py \u251c\u2500\u2500 venv\/ \u2514\u2500\u2500 requirements.txt <\/code><\/pre>\n<p>  <a name=\"install-requirements\"><\/a><\/p>\n<h3>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439<\/h3>\n<p>  \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u043f\u0430\u043a\u0435\u0442\u044b:<\/p>\n<ul>\n<li><code>dependency-injector<\/code> \u2014 dependency injection \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/li>\n<li><code>aiohttp<\/code> \u2014 \u0432\u0435\u0431 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/li>\n<li><code>aiohttp-devtools<\/code> \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430-\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441 live-\u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439<\/li>\n<li><code>pyyaml<\/code> \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 YAML \u0444\u0430\u0439\u043b\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0430<\/li>\n<li><code>pytest-aiohttp<\/code> \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430-\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>aiohttp<\/code> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/li>\n<li><code>pytest-cov<\/code> \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430-\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/li>\n<\/ul>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u0444\u0430\u0439\u043b <code>requirements.txt<\/code>:<\/p>\n<pre><code class=\"bash\">dependency-injector aiohttp aiohttp-devtools pyyaml pytest-aiohttp pytest-cov <\/code><\/pre>\n<p>  \u0418 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/p>\n<pre><code class=\"bash\">pip install -r requirements.txt <\/code><\/pre>\n<p>  \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c <code>httpie<\/code>. \u042d\u0442\u043e HTTP \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438. \u041c\u044b \u0431\u0443\u0434\u0435\u043c<br \/>  \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f API.<\/p>\n<p>  \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/p>\n<pre><code class=\"bash\">pip install httpie <\/code><\/pre>\n<p>  \u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>  <a name=\"minimal-app\"><\/a><\/p>\n<h3>\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/h3>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0423 \u043d\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043f\u0443\u0441\u0442\u043e\u0439 \u043e\u0442\u0432\u0435\u0442.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c <code>views.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Views module.&quot;&quot;&quot;  from aiohttp import web   async def index(request: web.Request) -&gt; web.Response:     query = request.query.get('query', 'Dependency Injector')     limit = int(request.query.get('limit', 10))      gifs = []      return web.json_response(         {             'query': query,             'limit': limit,             'gifs': gifs,         },     ) <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 (\u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440). \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432\u0441\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430. \u042d\u0442\u043e <code>aiohttp<\/code> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>index<\/code>.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c <code>containers.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application containers module.&quot;&quot;&quot;  from dependency_injector import containers from dependency_injector.ext import aiohttp from aiohttp import web  from . import views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = aiohttp.Application(web.Application)      index_view = aiohttp.View(views.index) <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0431\u0440\u0438\u043a\u0443 <code>aiohttp<\/code> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0415\u0435 \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442<br \/>  <code>create_app()<\/code>. \u041e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f <code>aiohttp<\/code> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u2014 \u043c\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>index_view<\/code> \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u043a\u043e\u0440\u043d\u044e <code>&quot;\/&quot;<\/code> \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c <code>application.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application module.&quot;&quot;&quot;  from aiohttp import web  from .containers import ApplicationContainer   def create_app():     &quot;&quot;&quot;Create and return aiohttp application.&quot;&quot;&quot;     container = ApplicationContainer()      app: web.Application = container.app()     app.container = container      app.add_routes([         web.get('\/', container.index_view.as_view()),     ])      return app <\/code><\/pre>\n<p>  <\/p>\n<blockquote><p><i>\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u2014 \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438. \u041e\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432.<\/i><\/p><\/blockquote>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0433\u043e\u0442\u043e\u0432\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435:<\/p>\n<p>  \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/p>\n<pre><code class=\"bash\">adev runserver giphynavigator\/application.py --livereload <\/code><\/pre>\n<p>  \u0412\u044b\u0432\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"bash\">[18:52:59] Starting aux server at http:\/\/localhost:8001 \u25c6 [18:52:59] Starting dev server at http:\/\/localhost:8000 \u25cf <\/code><\/pre>\n<p>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>httpie<\/code> \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430:<\/p>\n<pre><code class=\"bash\">http http:\/\/127.0.0.1:8000\/ <\/code><\/pre>\n<p>  \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435:<\/p>\n<pre><code class=\"bash\">HTTP\/1.1 200 OK Content-Length: 844 Content-Type: application\/json; charset=utf-8 Date: Wed, 29 Jul 2020 21:01:50 GMT Server: Python\/3.8 aiohttp\/3.6.2  {     &quot;gifs&quot;: [],     &quot;limit&quot;: 10,     &quot;query&quot;: &quot;Dependency Injector&quot; } <\/code><\/pre>\n<p>  \u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0433\u043e\u0442\u043e\u0432\u043e. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c Giphy API.<\/p>\n<p>  <a name=\"giphy-api-client\"><\/a><\/p>\n<h3>Giphy API \u043a\u043b\u0438\u0435\u043d\u0442<\/h3>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043c\u044b \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 Giphy API. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 API \u043a\u043b\u0438\u0435\u043d\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0447\u0430\u0441\u0442\u044c <code>aiohttp<\/code>.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0443\u0441\u0442\u043e\u0439 \u0444\u0430\u0439\u043b <code>giphy.py<\/code> \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>giphynavigator<\/code>:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 giphynavigator\/ \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u251c\u2500\u2500 giphy.py \u2502   \u2514\u2500\u2500 views.py \u251c\u2500\u2500 venv\/ \u2514\u2500\u2500 requirements.txt <\/code><\/pre>\n<p>  \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043d\u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Giphy client module.&quot;&quot;&quot;  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):         &quot;&quot;&quot;Make search API call and return result.&quot;&quot;&quot;         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() <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c GiphyClient \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u0423 GiphyClient \u0435\u0441\u0442\u044c \u0434\u0432\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u0440\u0438 \u0435\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438: API \u043a\u043b\u044e\u0447 \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0432\u0443\u043c\u044f \u043d\u043e\u0432\u044b\u043c\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043c\u0438 \u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u044f <code>dependency_injector.providers<\/code>:<\/p>\n<ul>\n<li>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 <code>Factory<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c GiphyClient.<\/li>\n<li>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 <code>Configuration<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c API \u043a\u043b\u044e\u0447 \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 GiphyClient.<\/li>\n<\/ul>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c <code>containers.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application containers module.&quot;&quot;&quot;  from dependency_injector import containers, providers from dependency_injector.ext import aiohttp from aiohttp import web  from . import giphy, views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      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,     )      index_view = aiohttp.View(views.index) <\/code><\/pre>\n<p>  <\/p>\n<blockquote><p><i>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0437\u0430\u0434\u0430\u043b\u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 <code>Configuration<\/code>.<\/i><\/p>\n<p>  <i>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c, \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u0434\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.<\/i>  <\/p><\/blockquote>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.<br \/>  \u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c YAML.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0443\u0441\u0442\u043e\u0439 \u0444\u0430\u0439\u043b <code>config.yml<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 giphynavigator\/ \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u251c\u2500\u2500 giphy.py \u2502   \u2514\u2500\u2500 views.py \u251c\u2500\u2500 venv\/ \u251c\u2500\u2500 config.yml \u2514\u2500\u2500 requirements.txt <\/code><\/pre>\n<p>  \u0418 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438:<\/p>\n<pre><code class=\"plaintext\">giphy:   request_timeout: 10 <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 API \u043a\u043b\u044e\u0447\u0430 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f <code>GIPHY_API_KEY <\/code>.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>create_app()<\/code> \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c 2 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<ul>\n<li>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0438\u0437 <code>config.yml<\/code><\/li>\n<li>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c API \u043a\u043b\u044e\u0447 \u0438\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f <code>GIPHY_API_KEY<\/code><\/li>\n<\/ul>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>application.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application module.&quot;&quot;&quot;  from aiohttp import web  from .containers import ApplicationContainer   def create_app():     &quot;&quot;&quot;Create and return aiohttp application.&quot;&quot;&quot;     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 <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c API \u043a\u043b\u044e\u0447 \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u043d\u0430 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0441\u0435\u0439\u0447\u0430\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0432\u043e\u0442 \u044d\u0442\u043e\u0442 \u043a\u043b\u044e\u0447:<\/p>\n<pre><code class=\"bash\">export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0 <\/code><\/pre>\n<p>  <\/p>\n<blockquote><p><i>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 Giphy API \u0441\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u044d\u0442\u043e\u043c\u0443 <a href=\"https:\/\/support.giphy.com\/hc\/en-us\/articles\/360020283431-Request-A-GIPHY-API-Key\" rel=\"nofollow\">\u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443<\/a>.<\/i><\/p><\/blockquote>\n<p>  \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Giphy API \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>  <a name=\"search-service\"><\/a><\/p>\n<h3>\u0421\u0435\u0440\u0432\u0438\u0441 \u043f\u043e\u0438\u0441\u043a\u0430<\/h3>\n<p>  \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u043e\u0438\u0441\u043a\u0430 <code>SearchService<\/code>. \u041e\u043d \u0431\u0443\u0434\u0435\u0442:<\/p>\n<ul>\n<li>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043f\u043e\u0438\u0441\u043a<\/li>\n<li>\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442<\/li>\n<\/ul>\n<p>  <code>SearchService<\/code> \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>GiphyClient<\/code>.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0443\u0441\u0442\u043e\u0439 \u0444\u0430\u0439\u043b <code>services.py<\/code> \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>giphynavigator<\/code>:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 giphynavigator\/ \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u251c\u2500\u2500 giphy.py \u2502   \u251c\u2500\u2500 services.py \u2502   \u2514\u2500\u2500 views.py \u251c\u2500\u2500 venv\/ \u2514\u2500\u2500 requirements.txt <\/code><\/pre>\n<p>  \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043d\u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Services module.&quot;&quot;&quot;  from .giphy import GiphyClient   class SearchService:      def __init__(self, giphy_client: GiphyClient):         self._giphy_client = giphy_client      async def search(self, query, limit):         &quot;&quot;&quot;Search for gifs and return formatted data.&quot;&quot;&quot;         if not query:             return []          result = await self._giphy_client.search(query, limit)          return [{'url': gif['url']} for gif in result['data']] <\/code><\/pre>\n<p>  \u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 <code>SearchService<\/code> \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c <code>GiphyClient<\/code>. \u041c\u044b \u0443\u043a\u0430\u0436\u0435\u043c \u044d\u0442\u043e \u043f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 <code>SearchService<\/code> \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c <code>containers.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application containers module.&quot;&quot;&quot;  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):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      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) <\/code><\/pre>\n<p>  \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e\u0438\u0441\u043a\u0430 <code>SearchService<\/code> \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e. \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0435\u0433\u043e \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044e.<\/p>\n<p>  <a name=\"enable-search\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043f\u043e\u0438\u0441\u043a<\/h3>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0433\u043e\u0442\u043e\u0432\u044b \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0438\u0441\u043a \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>SearchService<\/code> \u0432 <code>index<\/code> \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>views.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Views module.&quot;&quot;&quot;  from aiohttp import web  from .services import SearchService   async def index(         request: web.Request,         search_service: SearchService, ) -&gt; web.Response:     query = request.query.get('query', 'Dependency Injector')     limit = int(request.query.get('limit', 10))      gifs = await search_service.search(query, limit)      return web.json_response(         {             'query': query,             'limit': limit,             'gifs': gifs,         },     ) <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c <code>SearchService<\/code> \u0432 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>index<\/code> \u043f\u0440\u0438 \u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0435.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>containers.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application containers module.&quot;&quot;&quot;  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):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      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,     ) <\/code><\/pre>\n<p>  \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c \u0447\u0442\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435:<\/p>\n<pre><code class=\"bash\">adev runserver giphynavigator\/application.py --livereload <\/code><\/pre>\n<p>  \u0438 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a API \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/p>\n<pre><code class=\"python\">http http:\/\/localhost:8000\/ query==&quot;wow,it works&quot; limit==5 <\/code><\/pre>\n<p>  \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435:<\/p>\n<pre><code class=\"bash\">HTTP\/1.1 200 OK Content-Length: 850 Content-Type: application\/json; charset=utf-8 Date: Wed, 29 Jul 2020 22:22:55 GMT Server: Python\/3.8 aiohttp\/3.6.2  {     &quot;gifs&quot;: [         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/discoverychannel-nugget-gold-rush-rick-ness-KGGPIlnC4hr4u2s3pY&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/primevideoin-ll1hyBS2IrUPLE0E71&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/jackman-works-jackmanworks-l4pTgQoCrmXq8Txlu&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/cat-massage-at-work-l46CzMaOlJXAFuO3u&quot;         },         {             &quot;url&quot;: &quot;https:\/\/giphy.com\/gifs\/everwhatproductions-fun-christmas-3oxHQCI8tKXoeW4IBq&quot;         },     ],     &quot;limit&quot;: 10,     &quot;query&quot;: &quot;wow,it works&quot; } <\/code><\/pre>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/7ab\/730\/9af\/7ab7309afd53d180e8ca29d4a900037b.gif\"><\/p>\n<p>  \u041f\u043e\u0438\u0441\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<p>  <a name=\"refactoring\"><\/a><\/p>\n<h3>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0430<\/h3>\n<p>  \u041d\u0430\u0448\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>index<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0432\u0430 hardcoded \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f:<\/p>\n<ul>\n<li>\u041f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e<\/li>\n<li>\u041b\u0438\u043c\u0438\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432<\/li>\n<\/ul>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433. \u041c\u044b \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043c \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>views.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Views module.&quot;&quot;&quot;  from aiohttp import web  from .services import SearchService   async def index(         request: web.Request,         search_service: SearchService,         default_query: str,         default_limit: int, ) -&gt; 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,         },     ) <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u043b\u0438\u0441\u044c \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>containers.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application containers module.&quot;&quot;&quot;  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):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      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,     ) <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>config.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\">giphy:   request_timeout: 10 search:   default_query: &quot;Dependency Injector&quot;   default_limit: 10 <\/code><\/pre>\n<p>  \u0420\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d. \u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0447\u0438\u0449\u0435 \u2014 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 hardcoded \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.<\/p>\n<p>  \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>  <a name=\"tests\"><\/a><\/p>\n<h3>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0435\u0441\u0442\u044b<\/h3>\n<p>  \u0411\u044b\u043b\u043e \u0431\u044b \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0441\u0442\u043e\u0432. \u0414\u0430\u0432\u0430\u0439 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"nofollow\">pytest<\/a> \u0438 <a href=\"https:\/\/coverage.readthedocs.io\/\" rel=\"nofollow\">coverage<\/a>.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0443\u0441\u0442\u043e\u0439 \u0444\u0430\u0439\u043b <code>tests.py<\/code> \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>giphynavigator<\/code>:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 giphynavigator\/ \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u251c\u2500\u2500 giphy.py \u2502   \u251c\u2500\u2500 services.py \u2502   \u251c\u2500\u2500 tests.py \u2502   \u2514\u2500\u2500 views.py \u251c\u2500\u2500 venv\/ \u2514\u2500\u2500 requirements.txt <\/code><\/pre>\n<p>  \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u043d\u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Tests module.&quot;&quot;&quot;  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.com\/gif1.gif'},             {'url': 'https:\/\/giphy.com\/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.com\/gif1.gif'},             {'url': 'https:\/\/giphy.com\/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() <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435:<\/p>\n<pre><code class=\"bash\">py.test giphynavigator\/tests.py --cov=giphynavigator <\/code><\/pre>\n<p>  \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435:<\/p>\n<pre><code class=\"bash\">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% <\/code><\/pre>\n<p>  <\/p>\n<blockquote><p><i>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043a\u0430\u043a \u043c\u044b \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c <code> giphy_client <\/code> \u043c\u043e\u043a\u043e\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>.override()<\/code>. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430.<\/i>  <\/p><\/blockquote>\n<p>  \u0420\u0430\u0431\u043e\u0442\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0432\u0435\u0434\u0435\u043c \u0438\u0442\u043e\u0433\u0438.<\/p>\n<p>  <a name=\"conclusion\"><\/a><\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>  \u041c\u044b \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 <code>aiohttp<\/code> REST API \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044f \u043f\u0440\u0438\u043d\u0446\u0438\u043f dependency injection. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 Dependency Injector \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 dependency injection \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430.<\/p>\n<p>  \u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0435 \u0441 Dependency Injector \u2014 \u044d\u0442\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<p>  \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043e\u043a\u0443\u043f\u0430\u0442\u044c\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0421 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u043c \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u0441\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Application containers module.&quot;&quot;&quot;  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):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      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,     ) <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h4>\n<p>  <\/p>\n<ul>\n<li>\u0423\u0437\u043d\u0430\u0439\u0442\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\" rel=\"nofollow\">Dependency Injector \u043d\u0430 GitHub<\/a><\/li>\n<li>\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u0442\u0435\u0441\u044c \u0441 <a href=\"http:\/\/python-dependency-injector.ets-labs.org\/\" rel=\"nofollow\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043d\u0430 Read the Docs<\/a><\/li>\n<li>\u0415\u0441\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441 \u0438\u043b\u0438 \u043d\u0430\u0448\u043b\u0438 \u0431\u0430\u0433? <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\/issues\" rel=\"nofollow\">\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 issue \u043d\u0430 Github<\/a><\/li>\n<\/ul>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/513510\/\"> https:\/\/habr.com\/ru\/post\/513510\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/513510\/\">\u041f\u0440\u0438\u0432\u0435\u0442,<\/p>\n<p>  \u042f \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044c <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\" rel=\"nofollow\">Dependency Injector<\/a>. \u042d\u0442\u043e dependency injection \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f Python.<\/p>\n<p>  \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e \u0441\u0435\u0440\u0438\u044e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432 \u043f\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044e Dependency Injector \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0435 \u0445\u043e\u0447\u0443 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c Dependency Injector \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 <code>aiohttp<\/code> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>  \u0420\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0442\u0430\u043a\u0438\u0445 \u0447\u0430\u0441\u0442\u0435\u0439:  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-307879","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307879","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=307879"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307879\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=307879"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=307879"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=307879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}