{"id":307465,"date":"2020-07-25T03:00:14","date_gmt":"2020-07-25T03:00:14","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=307465"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=307465","title":{"rendered":"Flask + 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\/512242\/\">\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>  \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 Flask \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<br \/>  <a name=\"habracut\"><\/a><br \/>  \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<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\u0438\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435<\/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=\"#hello-world\">Hello world!<\/a><\/li>\n<li><a href=\"#make-it-pretty\">\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0442\u0438\u043b\u0438<\/a><\/li>\n<li><a href=\"#connect-github\">\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c Github<\/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\/ghnav-flask\" 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 Flask<\/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>  \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 Github. \u041d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e Github Navigator.<\/p>\n<p>  \u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 Github Navigator?<\/p>\n<ul>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0433\u0434\u0435 \u0435\u043c\u0443 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e\u0442 \u0432\u0432\u0435\u0441\u0442\u0438 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441.<\/li>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0432\u043e\u0434\u0438\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 Enter.<\/li>\n<li>Github Navigator \u0438\u0449\u0435\u0442 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 Github.<\/li>\n<li>\u041f\u043e \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044e \u043f\u043e\u0438\u0441\u043a\u0430 Github Navigator \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438.<\/li>\n<li>\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0438 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441.<\/li>\n<li>\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442:<br \/> \n<ul>\n<li>\u0438\u043c\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f<\/li>\n<li>\u0432\u043b\u0430\u0434\u0435\u043b\u044c\u0446\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f<\/li>\n<li>\u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043e\u043c\u043c\u0438\u0442 \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/li>\n<\/ul>\n<\/li>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0435\u0433\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043d\u0430 Github.<\/li>\n<\/ul>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/yk\/ll\/v7\/ykllv7d56efrllm830mksgoiits.png\"><\/p>\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>  \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 ghnav-flask-tutorial cd ghnav-flask-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>  \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. \u042d\u0442\u043e \u043f\u043e\u043a\u0430 \u043d\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e.<\/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 githubnavigator\/ \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>  \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c Flask \u0438 Dependency Injector.<\/p>\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 flask <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0438\u0445 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c:<\/p>\n<pre><code class=\"bash\">pip install -r requirements.txt <\/code><\/pre>\n<p>  \u0418 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0447\u0442\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0448\u043b\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e:<\/p>\n<pre><code class=\"bash\">python -c &quot;import dependency_injector; print(dependency_injector.__version__)&quot; python -c &quot;import flask; print(flask.__version__)&quot; <\/code><\/pre>\n<p>  \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435:<\/p>\n<pre><code class=\"bash\">(venv) $ python -c &quot;import dependency_injector; print(dependency_injector.__version__)&quot; 3.22.0 (venv) $ python -c &quot;import flask; print(flask.__version__)&quot; 1.1.2 <\/code><\/pre>\n<p>  <a name=\"hello-world\"><\/a><\/p>\n<h3>Hello world!<\/h3>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 hello world \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\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>views.py<\/code>:<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Views module.&quot;&quot;&quot;   def index():     return 'Hello, World!' <\/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 Flask \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>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u0432 \u0444\u0430\u0439\u043b <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 flask from flask import Flask  from . import views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      index_view = flask.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 Flask \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 <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 Flask \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 &quot;\/&quot; \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 .containers import ApplicationContainer   def create_app():     &quot;&quot;&quot;Create and return Flask application.&quot;&quot;&quot;     container = ApplicationContainer()      app = container.app()     app.container = container      app.add_url_rule('\/', view_func=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 \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0433\u043e\u0442\u043e\u0432\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u00abHello, World!\u00bb.<\/p>\n<p>  \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/p>\n<pre><code class=\"bash\">export FLASK_APP=githubnavigator.application export FLASK_ENV=development flask run <\/code><\/pre>\n<p>  \u0412\u044b\u0432\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"bash\">* Serving Flask app &quot;githubnavigator.application&quot; (lazy loading) * Environment: development * Debug mode: on * Running on http:\/\/127.0.0.1:5000\/ (Press CTRL+C to quit) * Restarting with fsevents reloader * Debugger is active! * Debugger PIN: 473-587-859 <\/code><\/pre>\n<p>  \u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 \u0437\u0430\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 <a href=\"http:\/\/127.0.0.1:5000\/\" rel=\"nofollow\">http:\/\/127.0.0.1:5000\/<\/a>.<\/p>\n<p>  \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u00abHello, World!\u00bb.<\/p>\n<p>  \u041e\u0442\u043b\u0438\u0447\u043d\u043e. \u041d\u0430\u0448\u0435 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0435\u0433\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043a\u0440\u0430\u0441\u0438\u0432\u0435\u0435.<\/p>\n<p>  <a name=\"make-it-pretty\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0442\u0438\u043b\u0438<\/h3>\n<p>  \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/getbootstrap.com\/\" rel=\"nofollow\">Bootstrap 4<\/a>. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <a href=\"https:\/\/pypi.org\/project\/Bootstrap-Flask\/\" rel=\"nofollow\">Bootstrap-Flask<\/a>. \u041e\u043d\u043e \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0438\u043a\u043e\u0432.<\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c <code>bootstrap-flask<\/code> \u0432 <code>requirements.txt<\/code>:<\/p>\n<pre><code class=\"bash\">dependency-injector flask bootstrap-flask <\/code><\/pre>\n<p>  \u0438 \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 --upgrade -r requirements.txt <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>bootstrap-flask<\/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\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 from dependency_injector.ext import flask from flask import Flask from flask_bootstrap import Bootstrap  from . import views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      bootstrap = flask.Extension(Bootstrap)      index_view = flask.View(views.index) <\/code><\/pre>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>bootstrap-flask<\/code>. \u041d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c <code>create_app()<\/code>.<\/p>\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 .containers import ApplicationContainer   def create_app():     &quot;&quot;&quot;Create and return Flask application.&quot;&quot;&quot;     container = ApplicationContainer()      app = container.app()     app.container = container      bootstrap = container.bootstrap()     bootstrap.init_app(app)      app.add_url_rule('\/', view_func=container.index_view.as_view())      return app <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d\u044b. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0430\u043f\u043a\u0443 <code>templates\/<\/code> \u0432 \u043f\u0430\u043a\u0435\u0442 <code>githubnavigator<\/code>. \u0412\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u043f\u043a\u0438 \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430:<\/p>\n<ul>\n<li><code>base.html<\/code> \u2014 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d<\/li>\n<li><code>index.html<\/code> \u2014 \u0448\u0430\u0431\u043b\u043e\u043d \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b<\/li>\n<\/ul>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 <code>templates<\/code> \u0438 \u0434\u0432\u0430 \u043f\u0443\u0441\u0442\u044b\u0445 \u0444\u0430\u0439\u043b\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 <code>base.html<\/code> \u0438 <code>index.html<\/code>:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 githubnavigator\/ \u2502   \u251c\u2500\u2500 templates\/ \u2502   \u2502   \u251c\u2500\u2500 base.html \u2502   \u2502   \u2514\u2500\u2500 index.html \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>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d.<\/p>\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>base.html<\/code>:<\/p>\n<pre><code class=\"xml\">&lt;!doctype html&gt; &lt;html lang=&quot;en&quot;&gt;     &lt;head&gt;         {% block head %}         &lt;!-- Required meta tags --&gt;         &lt;meta charset=&quot;utf-8&quot;&gt;         &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;          {% block styles %}             &lt;!-- Bootstrap CSS --&gt;             {{ bootstrap.load_css() }}         {% endblock %}          &lt;title&gt;{% block title %}{% endblock %}&lt;\/title&gt;         {% endblock %}     &lt;\/head&gt;     &lt;body&gt;         &lt;!-- Your page content --&gt;         {% block content %}{% endblock %}          {% block scripts %}             &lt;!-- Optional JavaScript --&gt;             {{ bootstrap.load_js() }}         {% endblock %}     &lt;\/body&gt; &lt;\/html&gt; <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u0448\u0430\u0431\u043b\u043e\u043d \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\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>index.html<\/code>:<\/p>\n<pre><code class=\"xml\">{% extends &quot;base.html&quot; %}  {% block title %}Github Navigator{% endblock %}  {% block content %} &lt;div class=&quot;container&quot;&gt;     &lt;h1 class=&quot;mb-4&quot;&gt;Github Navigator&lt;\/h1&gt;      &lt;form&gt;         &lt;div class=&quot;form-group form-row&quot;&gt;             &lt;div class=&quot;col-10&quot;&gt;                 &lt;label for=&quot;search_query&quot; class=&quot;col-form-label&quot;&gt;                     Search for:                 &lt;\/label&gt;                 &lt;input class=&quot;form-control&quot; type=&quot;text&quot; id=&quot;search_query&quot;                        placeholder=&quot;Type something to search on the GitHub&quot;                        name=&quot;query&quot;                        value=&quot;{{ query if query }}&quot;&gt;             &lt;\/div&gt;             &lt;div class=&quot;col&quot;&gt;                 &lt;label for=&quot;search_limit&quot; class=&quot;col-form-label&quot;&gt;                     Limit:                 &lt;\/label&gt;                 &lt;select class=&quot;form-control&quot; id=&quot;search_limit&quot; name=&quot;limit&quot;&gt;                     {% for value in [5, 10, 20] %}                     &lt;option {% if value == limit %}selected{% endif %}&gt;                         {{ value }}                     &lt;\/option&gt;                     {% endfor %}                 &lt;\/select&gt;             &lt;\/div&gt;         &lt;\/div&gt;     &lt;\/form&gt;      &lt;p&gt;&lt;small&gt;Results found: {{ repositories|length }}&lt;\/small&gt;&lt;\/p&gt;      &lt;table class=&quot;table table-striped&quot;&gt;         &lt;thead&gt;             &lt;tr&gt;                 &lt;th&gt;#&lt;\/th&gt;                 &lt;th&gt;Repository&lt;\/th&gt;                 &lt;th class=&quot;text-nowrap&quot;&gt;Repository owner&lt;\/th&gt;                 &lt;th class=&quot;text-nowrap&quot;&gt;Last commit&lt;\/th&gt;             &lt;\/tr&gt;         &lt;\/thead&gt;         &lt;tbody&gt;         {% for repository in repositories %} {{n}}             &lt;tr&gt;               &lt;th&gt;{{ loop.index }}&lt;\/th&gt;               &lt;td&gt;&lt;a href=&quot;{{ repository.url }}&quot;&gt;                   {{ repository.name }}&lt;\/a&gt;               &lt;\/td&gt;               &lt;td&gt;&lt;a href=&quot;{{ repository.owner.url }}&quot;&gt;                   &lt;img src=&quot;{{ repository.owner.avatar_url }}&quot;                        alt=&quot;avatar&quot; height=&quot;24&quot; width=&quot;24&quot;\/&gt;&lt;\/a&gt;                   &lt;a href=&quot;{{ repository.owner.url }}&quot;&gt;                       {{ repository.owner.login }}&lt;\/a&gt;               &lt;\/td&gt;               &lt;td&gt;&lt;a href=&quot;{{ repository.latest_commit.url }}&quot;&gt;                   {{ repository.latest_commit.sha }}&lt;\/a&gt;                   {{ repository.latest_commit.message }}                   {{ repository.latest_commit.author_name }}               &lt;\/td&gt;             &lt;\/tr&gt;         {% endfor %}         &lt;\/tbody&gt;     &lt;\/table&gt; &lt;\/div&gt;  {% endblock %} <\/code><\/pre>\n<p>  \u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u043f\u043e\u0447\u0442\u0438 \u0433\u043e\u0442\u043e\u0432\u043e. \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>index<\/code> \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u043e \u0448\u0430\u0431\u043b\u043e\u043d <code>index.html<\/code>.<\/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 flask import request, render_template   def index():     query = request.args.get('query', 'Dependency Injector')     limit = request.args.get('limit', 10, int)      repositories = []      return render_template(         'index.html',         query=query,         limit=limit,         repositories=repositories,     ) <\/code><\/pre>\n<p>  \u0413\u043e\u0442\u043e\u0432\u043e.<\/p>\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 <code>flask run<\/code> \u0438 \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 <a href=\"http:\/\/127.0.0.1:5000\/\" rel=\"nofollow\">http:\/\/127.0.0.1:5000\/<\/a>.<\/p>\n<p>  \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/hh\/uu\/0j\/hhuu0jb9quuwjp-pmqfo1h62vby.png\"><br \/>  <a name=\"connect-github\"><\/a>  <\/p>\n<h3>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c Github<\/h3>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \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 Github API.<br \/>  \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/PyGithub\/PyGithub\" rel=\"nofollow\">PyGithub<\/a>.<\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0451 \u0432 <code>requirements.txt<\/code>:<\/p>\n<pre><code class=\"bash\">dependency-injector flask bootstrap-flask pygithub <\/code><\/pre>\n<p>  \u0438 \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 --upgrade -r requirements.txt <\/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 Github API \u043a\u043b\u0438\u0435\u043d\u0442 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \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 Github \u043a\u043b\u0438\u0435\u043d\u0442.<\/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 \u0442\u043e\u043a\u0435\u043d \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 Github \u043a\u043b\u0438\u0435\u043d\u0442\u0443.<\/li>\n<\/ul>\n<p>  \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e.<\/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 flask from flask import Flask from flask_bootstrap import Bootstrap from github import Github  from . import views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      bootstrap = flask.Extension(Bootstrap)      config = providers.Configuration()      github_client = providers.Factory(         Github,         login_or_token=config.github.auth_token,         timeout=config.github.request_timeout,     )      index_view = flask.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 githubnavigator\/ \u2502   \u251c\u2500\u2500 templates\/ \u2502   \u2502   \u251c\u2500\u2500 base.html \u2502   \u2502   \u2514\u2500\u2500 index.html \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\/ \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\">github:   request_timeout: 10 <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u043e\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/pypi.org\/project\/PyYAML\/\" rel=\"nofollow\">PyYAML<\/a>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0435 \u0432 \u0444\u0430\u0439\u043b \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>requirements.txt<\/code>:<\/p>\n<pre><code class=\"bash\">dependency-injector flask bootstrap-flask pygithub pyyaml <\/code><\/pre>\n<p>  \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c:<\/p>\n<pre><code class=\"bash\">pip install --upgrade -r requirements.txt <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 API \u0442\u043e\u043a\u0435\u043d\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>GITHUB_TOKEN<\/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 \u0442\u043e\u043a\u0435\u043d \u0438\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f <code>GITHUB_TOKEN<\/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 .containers import ApplicationContainer   def create_app():     &quot;&quot;&quot;Create and return Flask application.&quot;&quot;&quot;     container = ApplicationContainer()     container.config.from_yaml('config.yml')     container.config.github.auth_token.from_env('GITHUB_TOKEN')      app = container.app()     app.container = container      bootstrap = container.bootstrap()     bootstrap.init_app(app)      app.add_url_rule('\/', view_func=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 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<p>  \u0414\u043b\u044f \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u043c\u0443 <a href=\"https:\/\/docs.github.com\/en\/github\/authenticating-to-github\/creating-a-personal-access-token\" rel=\"nofollow\">\u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u043d\u0430 Github<\/a><\/li>\n<li>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f:\n<pre><code class=\"bash\">export GITHUB_TOKEN=&lt;your token&gt;<\/code><\/pre>\n<\/li>\n<\/ul>\n<p>  <\/p>\n<blockquote><p><i>\u042d\u0442\u043e\u0442 \u043f\u0443\u043d\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c.<\/p>\n<p>  \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0430, \u043d\u043e \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043d\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c\u044e. \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043d\u0435\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432: 60 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0447\u0430\u0441. \u0422\u043e\u043a\u0435\u043d \u043d\u0443\u0436\u0435\u043d \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u043a\u0432\u043e\u0442\u0443 \u0434\u043e 5000 \u0432 \u0447\u0430\u0441.<\/i><\/p><\/blockquote>\n<p>  \u0413\u043e\u0442\u043e\u0432\u043e.<\/p>\n<p>  \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Github API \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430.<br \/>  <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 \u043d\u0430 Github<\/li>\n<li>\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043a\u043e\u043c\u043c\u0438\u0442\u0430\u0445<\/li>\n<li>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\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 Github API \u043a\u043b\u0438\u0435\u043d\u0442.<\/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>githubnavigator<\/code>:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 githubnavigator\/ \u2502   \u251c\u2500\u2500 templates\/ \u2502   \u2502   \u251c\u2500\u2500 base.html \u2502   \u2502   \u2514\u2500\u2500 index.html \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u251c\u2500\u2500 services.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>  \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 github import Github from github.Repository import Repository from github.Commit import Commit   class SearchService:     &quot;&quot;&quot;Search service performs search on Github.&quot;&quot;&quot;      def __init__(self, github_client: Github):         self._github_client = github_client      def search_repositories(self, query, limit):         &quot;&quot;&quot;Search for repositories and return formatted data.&quot;&quot;&quot;         repositories = self._github_client.search_repositories(             query=query,             **{'in': 'name'},         )         return [             self._format_repo(repository)             for repository in repositories[:limit]         ]      def _format_repo(self, repository: Repository):         commits = repository.get_commits()         return {             'url': repository.html_url,             'name': repository.name,             'owner': {                 'login': repository.owner.login,                 'url': repository.owner.html_url,                 'avatar_url': repository.owner.avatar_url,             },             'latest_commit': self._format_commit(commits[0]) if commits else {},         }      def _format_commit(self, commit: Commit):         return {             'sha': commit.sha,             'url': commit.html_url,             'message': commit.commit.message,             'author_name': commit.commit.author.name,         } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c <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\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 flask from flask import Flask from flask_bootstrap import Bootstrap from github import Github  from . import services, views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      bootstrap = flask.Extension(Bootstrap)      config = providers.Configuration()      github_client = providers.Factory(         Github,         login_or_token=config.github.auth_token,         timeout=config.github.request_timeout,     )      search_service = providers.Factory(         services.SearchService,         github_client=github_client,     )      index_view = flask.View(views.index) <\/code><\/pre>\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 flask import request, render_template  from .services import SearchService   def index(search_service: SearchService):     query = request.args.get('query', 'Dependency Injector')     limit = request.args.get('limit', 10, int)      repositories = search_service.search_repositories(query, limit)      return render_template(         'index.html',         query=query,         limit=limit,         repositories=repositories,     ) <\/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 flask from flask import Flask from flask_bootstrap import Bootstrap from github import Github  from . import services, views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      bootstrap = flask.Extension(Bootstrap)      config = providers.Configuration()      github_client = providers.Factory(         Github,         login_or_token=config.github.auth_token,         timeout=config.github.request_timeout,     )      search_service = providers.Factory(         services.SearchService,         github_client=github_client,     )      index_view = flask.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 <code>flask run<\/code> \u0438 \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 <a href=\"http:\/\/127.0.0.1:5000\/\" rel=\"nofollow\">http:\/\/127.0.0.1:5000\/<\/a>.<\/p>\n<p>  \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/yk\/ll\/v7\/ykllv7d56efrllm830mksgoiits.png\"><br \/>  <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 flask import request, render_template  from .services import SearchService   def index(         search_service: SearchService,         default_query: str,         default_limit: int, ):     query = request.args.get('query', default_query)     limit = request.args.get('limit', default_limit, int)      repositories = search_service.search_repositories(query, limit)      return render_template(         'index.html',         query=query,         limit=limit,         repositories=repositories,     ) <\/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 flask from flask import Flask from flask_bootstrap import Bootstrap from github import Github  from . import services, views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      bootstrap = flask.Extension(Bootstrap)      config = providers.Configuration()      github_client = providers.Factory(         Github,         login_or_token=config.github.auth_token,         timeout=config.github.request_timeout,     )      search_service = providers.Factory(         services.SearchService,         github_client=github_client,     )      index_view = flask.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\">github:   request_timeout: 10 search:   default_query: &quot;Dependency Injector&quot;   default_limit: 10 <\/code><\/pre>\n<p>  \u0413\u043e\u0442\u043e\u0432\u043e.<\/p>\n<p>  \u0420\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d. \u041c\u0443 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043a\u043e\u0434 \u0447\u0438\u0449\u0435.<br \/>  <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 \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u043e\u0432. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0435\u043c.<\/p>\n<p>  \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\/en\/coverage-5.2.1\/\" rel=\"nofollow\">coverage<\/a>.<\/p>\n<p>  \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>requirements.txt<\/code>:<\/p>\n<pre><code class=\"bash\">dependency-injector flask bootstrap-flask pygithub pyyaml pytest-flask pytest-cov <\/code><\/pre>\n<p>  \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043d\u043e\u0432\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b:<\/p>\n<pre><code class=\"bash\">pip install -r requirements.txt <\/code><\/pre>\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>githubnavigator<\/code>:<\/p>\n<pre><code class=\"bash\">.\/ \u251c\u2500\u2500 githubnavigator\/ \u2502   \u251c\u2500\u2500 templates\/ \u2502   \u2502   \u251c\u2500\u2500 base.html \u2502   \u2502   \u2514\u2500\u2500 index.html \u2502   \u251c\u2500\u2500 __init__.py \u2502   \u251c\u2500\u2500 application.py \u2502   \u251c\u2500\u2500 containers.py \u2502   \u251c\u2500\u2500 services.py \u2502   \u251c\u2500\u2500 tests.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>  \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 github import Github from flask import url_for  from .application import create_app   @pytest.fixture def app():     return create_app()   def test_index(client, app):     github_client_mock = mock.Mock(spec=Github)     github_client_mock.search_repositories.return_value = [         mock.Mock(             html_url='repo1-url',             name='repo1-name',             owner=mock.Mock(                 login='owner1-login',                 html_url='owner1-url',                 avatar_url='owner1-avatar-url',             ),             get_commits=mock.Mock(return_value=[mock.Mock()]),         ),         mock.Mock(             html_url='repo2-url',             name='repo2-name',             owner=mock.Mock(                 login='owner2-login',                 html_url='owner2-url',                 avatar_url='owner2-avatar-url',             ),             get_commits=mock.Mock(return_value=[mock.Mock()]),         ),     ]      with app.container.github_client.override(github_client_mock):         response = client.get(url_for('index'))      assert response.status_code == 200     assert b'Results found: 2' in response.data      assert b'repo1-url' in response.data     assert b'repo1-name' in response.data     assert b'owner1-login' in response.data     assert b'owner1-url' in response.data     assert b'owner1-avatar-url' in response.data      assert b'repo2-url' in response.data     assert b'repo2-name' in response.data     assert b'owner2-login' in response.data     assert b'owner2-url' in response.data     assert b'owner2-avatar-url' in response.data   def test_index_no_results(client, app):     github_client_mock = mock.Mock(spec=Github)     github_client_mock.search_repositories.return_value = []      with app.container.github_client.override(github_client_mock):         response = client.get(url_for('index'))      assert response.status_code == 200     assert b'Results found: 0' in response.data <\/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 githubnavigator\/tests.py --cov=githubnavigator <\/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: flask-1.0.0, cov-2.10.0 collected 2 items  githubnavigator\/tests.py ..                                     [100%]  ---------- coverage: platform darwin, python 3.8.3-final-0 ----------- Name                             Stmts   Miss  Cover ---------------------------------------------------- githubnavigator\/__init__.py          0      0   100% githubnavigator\/application.py      11      0   100% githubnavigator\/containers.py       13      0   100% githubnavigator\/services.py         14      0   100% githubnavigator\/tests.py            32      0   100% githubnavigator\/views.py             7      0   100% ---------------------------------------------------- TOTAL                               77      0   100% <\/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>github_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>  <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 Flask \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \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>  \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u044d\u0442\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u041e\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \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. \u042d\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u043d\u0430\u0434 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0415\u0451 \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c:<\/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 flask from flask import Flask from flask_bootstrap import Bootstrap from github import Github  from . import services, views   class ApplicationContainer(containers.DeclarativeContainer):     &quot;&quot;&quot;Application container.&quot;&quot;&quot;      app = flask.Application(Flask, __name__)      bootstrap = flask.Extension(Bootstrap)      config = providers.Configuration()      github_client = providers.Factory(         Github,         login_or_token=config.github.auth_token,         timeout=config.github.request_timeout,     )      search_service = providers.Factory(         services.SearchService,         github_client=github_client,     )      index_view = flask.View(         views.index,         search_service=search_service,         default_query=config.search.default_query,         default_limit=config.search.default_limit,     ) <\/code><\/pre>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bg\/ah\/8j\/bgah8jswglao0nmeuw43wkoy920.png\"><br \/>  <i>\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043a\u0430\u043a \u043a\u0430\u0440\u0442\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0412\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0437\u043d\u0430\u0439\u0442\u0435 \u0447\u0442\u043e \u043e\u0442 \u0447\u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442.<\/i><\/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\/512242\/\"> https:\/\/habr.com\/ru\/post\/512242\/<\/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\/512242\/\">\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>  \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 Flask \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\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-307465","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307465","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=307465"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307465\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=307465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=307465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=307465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}