{"id":294541,"date":"2019-09-11T09:00:23","date_gmt":"2019-09-11T09:00:23","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=294541"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=294541","title":{"rendered":"\u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 CLI-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f Python: click, cement, fire \u0438 \u0434\u0440\u0443\u0433\u0438\u0435"},"content":{"rendered":"\n<div class=\"post__text post__text-html js-mediator-article\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/rp\/ek\/be\/rpekben_u6jtlwru-flsslxk70e.png\"\/><\/p>\n<p>  Python \u2014 \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 \u044f\u0437\u044b\u043a \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438 \u044d\u0442\u043e \u043f\u043e\u0434\u0447\u0451\u0440\u043a\u0438\u0432\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0437\u0430\u0434\u0430\u0447. <i>\u041d\u043e \u043a\u0430\u043a\u0438\u0435 \u0432\u043e\u043e\u0431\u0449\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442? \u0410 \u043a\u0430\u043a\u0443\u044e \u043b\u0443\u0447\u0448\u0435 \u0432\u0437\u044f\u0442\u044c?<\/i> \u0412 \u044d\u0442\u043e\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0435 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0438 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0438\u0440\u0430 \u0438 \u0434\u0430\u043d\u0430 \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u043f\u0440\u043e\u0441.<\/p>\n<p>  \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0447\u0442\u0435\u043d\u0438\u044f \u043e\u0431\u0437\u043e\u0440 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d \u043d\u0430 \u0434\u0432\u0430 \u043f\u043e\u0441\u0442\u0430: \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0448\u0435\u0441\u0442\u044c \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u2014 \u043c\u0435\u043d\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0435, \u043d\u043e \u0432\u0441\u0451 \u0436\u0435 \u0437\u0430\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u044e\u0449\u0438\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044f. <br \/>  <a name=\"habracut\"><\/a><br \/>  \u0412 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043d\u0430 Python 3.7 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u0430\u044f \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u043a \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 <a href=\"https:\/\/github.com\/kam1sh\/py-console-examples\/blob\/1.0.2\/todolib\/__init__.py\">todolib<\/a>, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c, \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c, \u043f\u043e\u043c\u0435\u0447\u0430\u0442\u044c \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u043f\u0438\u0441\u0430\u043d\u043e \u043f\u0440\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435. \u0421\u0430\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 json-\u0444\u0430\u0439\u043b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c \u2014 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c.<br \/>  \u0412\u0434\u043e\u0431\u0430\u0432\u043e\u043a \u043a \u044d\u0442\u043e\u043c\u0443, \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u0441\u0442. \u0417\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u0437\u044f\u0442 pytest \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430\u043c\u0438:<\/p>\n<pre><code class=\"python\">@pytest.fixture(autouse=True) def db(monkeypatch):     &quot;&quot;&quot;     monkeypatch \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430,     \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u044b\u043b\u0430 \u043f\u0443\u0441\u0442\u0430\u044f \u0431\u0430\u0437\u0430     &quot;&quot;&quot;     value = {&quot;tasks&quot;: []}     monkeypatch.setattr(todolib.TodoApp, &quot;get_db&quot;, lambda _: value)     return value  @pytest.yield_fixture(autouse=True) def check(db):     &quot;&quot;&quot; \u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0411\u0414 &quot;&quot;&quot;     yield     assert db[&quot;tasks&quot;] and db[&quot;tasks&quot;][0][&quot;title&quot;] == &quot;test&quot;  # \u0432\u044b\u0432\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434 EXPECTED = &quot;Task 'test' created with number 1.\\n&quot; <\/code><\/pre>\n<p>  \u0412 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0432\u0441\u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0445\u0432\u0430\u0442\u0438\u0442. \u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 <a href=\"https:\/\/github.com\/kam1sh\/py-console-examples\/tree\/1.0.2\">\u044d\u0442\u043e\u043c<\/a> \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438.<\/p>\n<h2>argparse<\/h2>\n<p>  \u0423 <a href=\"https:\/\/docs.python.org\/3.7\/library\/argparse.html\">argparse<\/a> \u0435\u0441\u0442\u044c \u043d\u0435\u043e\u0441\u043f\u043e\u0440\u0438\u043c\u043e\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u2014 \u043e\u043d \u0435\u0441\u0442\u044c \u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u0438 \u0435\u0433\u043e API \u043d\u0435\u0442\u0440\u0443\u0434\u043d\u043e \u0432\u044b\u0443\u0447\u0438\u0442\u044c: \u0435\u0441\u0442\u044c \u043f\u0430\u0440\u0441\u0435\u0440, \u0435\u0441\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u0443 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0435\u0441\u0442\u044c <i>type<\/i>, <i>action<\/i>, <i>dest<\/i>, <i>default<\/i> \u0438 <i>help<\/i>. \u0418 \u0435\u0441\u0442\u044c <i>subparser<\/i> \u2014 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0434\u0435\u043b\u044f\u0442\u044c \u0447\u0430\u0441\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u043b\u043e\u0433\u0438\u043a\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b.<\/p>\n<h4>\u041f\u0430\u0440\u0441\u0435\u0440<\/h4>\n<p>  \u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u2014 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e, \u043f\u0430\u0440\u0441\u0435\u0440 \u043a\u0430\u043a \u043f\u0430\u0440\u0441\u0435\u0440. \u041d\u043e \u2014 \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u2014 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u043d\u0435 \u0441\u0430\u043c\u0430\u044f \u043b\u0443\u0447\u0448\u0430\u044f, \u0435\u0441\u043b\u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438, \u0442.\u043a. \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043a \u0440\u0430\u0437\u043d\u044b\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">def get_parser():     parser = argparse.ArgumentParser(&quot;Todo notes - argparse version&quot;)     parser.add_argument(         &quot;--verbose&quot;, &quot;-v&quot;, action=&quot;store_true&quot;, help=&quot;Enable verbose mode&quot;     )     parser.add_argument(&quot;--version&quot;, &quot;-V&quot;, action=&quot;store_true&quot;, help=&quot;Show version&quot;)     subparsers = parser.add_subparsers(title=&quot;Commands&quot;, dest=&quot;cmd&quot;)      add = subparsers.add_parser(&quot;add&quot;, help=&quot;Add new task&quot;)     add.add_argument(&quot;title&quot;, help=&quot;Todo title&quot;)      show = subparsers.add_parser(&quot;show&quot;, help=&quot;Show tasks&quot;)     show.add_argument(         &quot;--show-done&quot;, action=&quot;store_true&quot;, help=&quot;Include done tasks in the output&quot;     )      done = subparsers.add_parser(&quot;done&quot;, help=&quot;Mark task as done&quot;)     done.add_argument(&quot;number&quot;, type=int, help=&quot;Task number&quot;)      remove = subparsers.add_parser(&quot;remove&quot;, help=&quot;Remove task&quot;)     remove.add_argument(&quot;number&quot;, type=int, help=&quot;Task number&quot;)      return parser <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>main<\/h4>\n<p>  \u0418 \u0437\u0434\u0435\u0441\u044c \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u2014 \u043f\u0430\u0440\u0441\u0435\u0440 \u043a\u0440\u043e\u043c\u0435 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0443\u043c\u0435\u0435\u0442, \u0442\u0430\u043a \u0447\u0442\u043e \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435. \u0421 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u2014 \u0436\u0438\u0442\u044c \u043c\u043e\u0436\u043d\u043e, \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u2014 \u043c\u043e\u0436\u043d\u043e \u0436\u0435 \u043b\u0443\u0447\u0448\u0435, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043a\u0430 \u043d\u0435\u044f\u0441\u043d\u043e, \u043a\u0430\u043a \u0438\u043c\u0435\u043d\u043d\u043e.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">def main(raw_args=None):     &quot;&quot;&quot; Argparse example entrypoint &quot;&quot;&quot;     parser = get_parser()     args = parser.parse_args(raw_args)     logging.basicConfig()      if args.verbose:         logging.getLogger(&quot;todolib&quot;).setLevel(logging.INFO)     if args.version:         print(lib_version)         exit(0)     cmd = args.cmd     if not cmd:         parser.print_help()         exit(1)     with TodoApp.fromenv() as app:         if cmd == &quot;add&quot;:             task = app.add_task(args.title)             print(task, &quot;created with number&quot;, task.number, end=&quot;.\\n&quot;)         elif cmd == &quot;show&quot;:             app.print_tasks(args.show_done)         elif cmd == &quot;done&quot;:             task = app.task_done(args.number)             print(task, &quot;marked as done.&quot;)         elif cmd == &quot;remove&quot;:             task = app.remove_task(args.number)             print(task, &quot;removed from list.&quot;) <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  \u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u044b\u0432\u043e\u0434\u0430 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 <i>capsys<\/i>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u0430\u0451\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0442\u0435\u043a\u0441\u0442\u0443 \u0438\u0437 stdout \u0438 stderr.<\/p>\n<pre><code class=\"python\">def test_argparse(capsys):     todo_argparse.main([&quot;add&quot;, &quot;test&quot;])     out, _ = capsys.readouterr()     assert out == EXPECTED <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0418\u0442\u043e\u0433<\/h4>\n<p>  \u0418\u0437 \u043f\u043b\u044e\u0441\u043e\u0432 \u2014 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043d\u0430\u0431\u043e\u0440 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430, \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u044f \u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435. <\/p>\n<p>  \u041c\u0438\u043d\u0443\u0441\u044b \u2014 argparse \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043b\u0438\u0448\u044c \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u043e\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0438 \u0432 main \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u043c\u0443. \u0418 \u043d\u0435\u044f\u0441\u043d\u043e, \u043a\u0430\u043a \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c exit code.<\/p>\n<h2>docopt<\/h2>\n<p>  <a href=\"https:\/\/github.com\/docopt\/docopt\">docopt<\/a> \u2014 \u044d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 (&lt;600 \u0441\u0442\u0440\u043e\u043a, \u0432 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0438 \u0441 2500 \u0443 argparse) \u043f\u0430\u0440\u0441\u0435\u0440, that will make you smile, \u0446\u0438\u0442\u0438\u0440\u0443\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043d\u0430 GitHub. \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0438\u0434\u0435\u044f docopt \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u043c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 docstring.<\/p>\n<p>  \u041d\u0430 \u0442\u043e\u043c \u0436\u0435 \u0433\u0438\u0442\u0445\u0430\u0431\u0435 \u0443 docopt &gt;6700 \u0437\u0432\u0451\u0437\u0434, \u043e\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043c\u0438\u043d\u0438\u043c\u0443\u043c 22 \u0442\u044b\u0441\u044f\u0447\u0430\u0445 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445. \u0418 \u044d\u0442\u043e \u043b\u0438\u0448\u044c \u0443 python-\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438! \u041d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 docopt \u0435\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043f\u043e\u0434 \u0440\u0430\u0437\u043d\u044b\u0435 \u044f\u0437\u044b\u043a\u0438, \u043e\u0442 C \u0438 PHP \u0434\u043e CoffeeScript \u0438 \u0434\u0430\u0436\u0435 R. \u0422\u0430\u043a\u0443\u044e \u043a\u0440\u043e\u0441\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043c\u043e\u0433\u0443 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u043b\u0438\u0448\u044c \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u043e\u0439 \u043a\u043e\u0434\u0430.<\/p>\n<h4>\u041f\u0430\u0440\u0441\u0435\u0440<\/h4>\n<p>  \u0412 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0438 \u0441 argparse, \u044d\u0442\u043e\u0442 \u043f\u0430\u0440\u0441\u0435\u0440 \u2014 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0448\u0430\u0433 \u0432\u043f\u0435\u0440\u0451\u0434.<\/p>\n<pre><code class=\"python\">&quot;&quot;&quot;Todo notes on docopt.  Usage:   todo_docopt [-v | -vv ] add &lt;task&gt;   todo_docopt [-v | -vv ] show --show-done   todo_docopt [-v | -vv ] done &lt;number&gt;   todo_docopt [-v | -vv ] remove &lt;number&gt;   todo_docopt -h | --help   todo_docopt --version  Options:   -h --help     Show help.   -v --verbose  Enable verbose mode. &quot;&quot;&quot; <\/code><\/pre>\n<p>  <\/p>\n<h4>main<\/h4>\n<p>  \u0412 \u0446\u0435\u043b\u043e\u043c \u0432\u0441\u0451 \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0441 argparse, \u043e\u0434\u043d\u0430\u043a\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0443 <i>verbose<\/i> \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 (0-2), \u0438 \u0435\u0449\u0451 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f: docopt \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u0435 namespace \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438, \u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043b\u043e\u0432\u0430\u0440\u044c, \u0433\u0434\u0435 \u0432\u044b\u0431\u043e\u0440 \u0442\u043e\u0439 \u0438\u043b\u0438 \u0438\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0435\u0451 boolean, \u0447\u0442\u043e \u0432\u0438\u0434\u043d\u043e \u0432 <i>if<\/i>:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">def main(argv=None):     args = docopt(__doc__, argv=argv, version=lib_version)     log.setLevel(levels[args[&quot;--verbose&quot;]])     logging.basicConfig()     log.debug(&quot;Arguments: %s&quot;, args)      with TodoApp.fromenv() as app:         if args[&quot;add&quot;]:             task = app.add_task(args[&quot;&lt;task&gt;&quot;])             print(task, &quot;created with number&quot;, task.number, end=&quot;.\\n&quot;)         elif args[&quot;show&quot;]:             app.print_tasks(args[&quot;--show-done&quot;])         elif args[&quot;done&quot;]:             task = app.task_done(args[&quot;&lt;number&gt;&quot;])             print(task, &quot;marked as done.&quot;)         elif args[&quot;remove&quot;]:             task = app.remove_task(args[&quot;&lt;number&gt;&quot;])             print(task, &quot;removed from list.&quot;) <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e argparse:  <\/p>\n<pre><code class=\"python\">def test_docopt(capsys):     todo_docopt.main([&quot;add&quot;, &quot;test&quot;])     out, _ = capsys.readouterr()     assert out == EXPECTED <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0418\u0442\u043e\u0433<\/h4>\n<p>  \u0418\u0437 \u043f\u043b\u044e\u0441\u043e\u0432 \u2014 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043c\u0435\u043d\u044c\u0448\u0435 \u043a\u043e\u0434\u0430 \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0435\u0440\u0430, \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u0447\u0442\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434 \u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 version.<\/p>\n<p>  \u041c\u0438\u043d\u0443\u0441\u044b, \u0432\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u0442\u0435 \u0436\u0435, \u0447\u0442\u043e \u0438 \u0443 argparse \u2014 \u043c\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u043a\u0438 \u0432 <i>main<\/i>, \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c exit code. \u041a \u0442\u043e\u043c\u0443 \u0436\u0435 \u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f (0.6.2) docopt \u0435\u0449\u0451 \u043d\u0435 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0430 \u0438 \u0432\u0440\u044f\u0434 \u043b\u0438 \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0431\u0443\u0434\u0435\u0442 \u2014 \u043f\u0440\u043e\u0435\u043a\u0442 <a href=\"https:\/\/github.com\/docopt\/docopt\/graphs\/contributors\">\u0430\u043a\u0442\u0438\u0432\u043d\u043e<\/a> \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u043b\u0441\u044f \u0441 2012 \u043f\u043e \u043a\u043e\u043d\u0435\u0446 2013 \u0433\u043e\u0434\u0430, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043e\u043c\u043c\u0438\u0442 \u0431\u044b\u043b \u0432 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 17-\u0433\u043e. \u0410 \u0441\u0430\u043c\u043e\u0435 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u043e\u0435 \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u2014 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043a\u0438 docopt&#8217;\u0430 \u043f\u0440\u043e\u0432\u043e\u0446\u0438\u0440\u0443\u044e\u0442 DeprecationWarning&#8217;\u0438 \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<h2>Click<\/h2>\n<p>  <a href=\"https:\/\/github.com\/pallets\/click\">Click<\/a> \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 argparse \u0438 docopt \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u043a \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434 \u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b, \u0430 \u0441\u0430\u043c\u0443 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f \u0432\u044b\u0434\u0435\u043b\u044f\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e <i>main<\/i>. \u0410\u0432\u0442\u043e\u0440\u044b \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044e\u0442, \u0447\u0442\u043e \u0443 Click \u043c\u043d\u043e\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u043d\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043e\u043b\u0436\u043d\u043e \u0445\u0432\u0430\u0442\u0438\u0442\u044c. \u0421\u0440\u0435\u0434\u0438 \u0444\u0438\u0447 \u043f\u043e\u0434\u0447\u0451\u0440\u043a\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0438 \u0438\u0445 \u043b\u0435\u043d\u0438\u0432\u0430\u044f \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u043a\u0430. <\/p>\n<p>  \u041f\u0440\u043e\u0435\u043a\u0442 \u043a\u0440\u0430\u0439\u043d\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d: \u043a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0443 \u043d\u0435\u0433\u043e &gt;8100 \u0437\u0432\u0451\u0437\u0434 \u0438 \u043e\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043c\u0438\u043d\u0438\u043c\u0443\u043c 174 \u0442\u044b\u0441\u044f\u0447\u0430\u0445 (!) \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u043e\u043d \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0435\u0442\u0441\u044f: \u0432\u0435\u0440\u0441\u0438\u044f 7.0 \u0432\u044b\u0448\u043b\u0430 \u043e\u0441\u0435\u043d\u044c\u044e 2018 \u0433\u043e\u0434\u0430, \u0430 \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043c\u043c\u0438\u0442\u044b \u0438 merge request&#8217;\u044b \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438 \u043f\u043e \u0441\u0435\u0439 \u0434\u0435\u043d\u044c.<\/p>\n<h4>\u041f\u0430\u0440\u0441\u0435\u0440<\/h4>\n<p>  \u041d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u044f \u043d\u0430\u0448\u0451\u043b \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <i>confirmation_option<\/i>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b. \u0414\u043b\u044f \u0435\u0433\u043e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0431\u044b\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 wipe, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u0432\u0435\u0441\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">levels = [logging.WARN, logging.INFO, logging.DEBUG]  pass_app = click.make_pass_decorator(TodoApp)   @click.group() @click.version_option(lib_version, prog_name=&quot;todo_click&quot;) @click.option(&quot;-v&quot;, &quot;--verbose&quot;, count=True) # click \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u043e\u043f\u0446\u0438\u0438, \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 @click.option(&quot;--db&quot;, help=&quot;Path to the database file&quot;) @click.pass_context def cli(ctx, verbose, db):     &quot;&quot;&quot;Todo notes - click version.&quot;&quot;&quot;     level = levels[min(verbose, 2)]     logging.basicConfig(level=level)     logging.getLogger(&quot;todolib&quot;).setLevel(level)     ctx.obj = TodoApp.fromenv(db)     atexit.register(ctx.obj.save)   @cli.command() @click.argument(&quot;task&quot;) @pass_app def add(app, task):     &quot;&quot;&quot; Add new task. &quot;&quot;&quot;     task = app.add_task(task)     click.echo(f&quot;{task} created with number {task.number}.&quot;)   @cli.command() @click.option(&quot;--show-done&quot;, is_flag=True, help=&quot;Include done tasks&quot;) @pass_app def show(app, show_done):     &quot;&quot;&quot; Show current tasks. &quot;&quot;&quot;     app.print_tasks(show_done)   @cli.command() @click.argument(&quot;number&quot;, type=int) @pass_app def done(app, number):     &quot;&quot;&quot; Mark task as done. &quot;&quot;&quot;     task = app.task_done(number)     click.echo(f&quot;{task} marked as done.&quot;)   @cli.command() @click.argument(&quot;number&quot;, type=int) @pass_app def remove(app, number):     &quot;&quot;&quot; Remove task from the list. &quot;&quot;&quot;     task = app.remove_task(number)     click.echo(f&quot;{task} removed from the list.&quot;)   @cli.command() @click.confirmation_option(prompt=&quot;Are you sure you want to remove database&quot;) @pass_app def wipe(app):     for task in app.list_tasks():         task.remove() <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>main<\/h4>\n<p>  \u0418 \u0442\u0443\u0442 \u043c\u044b \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u043c\u0441\u044f \u0441 \u0433\u043b\u0430\u0432\u043d\u044b\u043c \u043f\u0440\u0435\u0438\u043c\u0435\u0449\u0435\u0441\u0442\u0432\u043e\u043c Click \u2014 \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043b\u043e\u0433\u0438\u043a\u0430 \u043a\u043e\u043c\u0430\u043d\u0434 \u0440\u0430\u0437\u043d\u0435\u0441\u0435\u043d\u0430 \u043f\u043e \u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c, \u0432 main \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f. \u0422\u0430\u043a\u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"python\">if __name__ == &quot;__main__&quot;:     cli(auto_envvar_prefix=&quot;TODO&quot;) <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 Click \u0432 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0435 sys.stdout \u043d\u0435\u0442 \u043d\u0443\u0436\u0434\u044b, \u0442\u0430\u043a \u043a\u0430\u043a \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c <i>click.testing<\/i> \u0441 \u0440\u0430\u043d\u043d\u0435\u0440\u043e\u043c \u0434\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0432\u0435\u0449\u0435\u0439. \u0418 \u043c\u0430\u043b\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e <i>CliRunner<\/i> \u0441\u0430\u043c \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u044b\u0432\u043e\u0434, \u043e\u043d \u0435\u0449\u0451 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c exit code, \u0447\u0442\u043e \u0442\u043e\u0436\u0435 \u043a\u0440\u0443\u0442\u043e. \u0412\u0441\u0451 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c click-\u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f pytest \u0438 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u043c\u043e\u0434\u0443\u043b\u0435\u043c <i>unittest<\/i>.<\/p>\n<pre><code class=\"python\">import click.testing def test_click():     runner = click.testing.CliRunner()     result = runner.invoke(todo_click.cli, [&quot;add&quot;, &quot;test&quot;])     assert result.exit_code == 0     assert result.output == EXPECTED <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0418\u0442\u043e\u0433<\/h4>\n<p>  \u042d\u0442\u043e \u043b\u0438\u0448\u044c \u043c\u0430\u043b\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0443\u043c\u0435\u0435\u0442 Click. \u0418\u0437 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e API \u2014 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u043e\u043c (\u0446\u0432\u0435\u0442\u0430, \u043f\u0435\u0439\u0434\u0436\u0435\u0440 \u0430-\u043b\u044f less, \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441-\u0431\u0430\u0440 \u0438 \u0442.\u0434.), result callback, \u0430\u0432\u0442\u043e\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435. \u041c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u044b <a href=\"https:\/\/github.com\/pallets\/click\/tree\/7.x\/examples\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>  \u041f\u043b\u044e\u0441\u044b: \u043c\u043d\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u0441\u043b\u0443\u0447\u0430\u0439, \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439, \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434, \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u0436\u0438\u0437\u043d\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>  \u041c\u0438\u043d\u0443\u0441\u044b: \u041a\u0430\u043a\u0438\u0435 \u0443 \u00ab\u043a\u043b\u0438\u043a\u0430\u00bb \u043c\u0438\u043d\u0443\u0441\u044b \u2014 \u044d\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441. \u041c\u043e\u0436\u0435\u0442, \u043e\u043d \u0447\u0435\u0433\u043e-\u0442\u043e \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u0438\u0437 \u0442\u043e\u0433\u043e, \u043d\u0430 \u0447\u0442\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438?<\/p>\n<h2>Fire<\/h2>\n<p>  <a href=\"https:\/\/github.com\/google\/python-fire\">Fire<\/a> \u2014 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u043e\u043b\u043e\u0434\u0430\u044f (\u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 2017-\u043c) \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u043e\u0442 Google, \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0438\u0437, \u0434\u043e\u0441\u043b\u043e\u0432\u043d\u043e \u0446\u0438\u0442\u0438\u0440\u0443\u044f, <i>\u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043b\u044e\u0431\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430<\/i> Python. <br \/>  \u0421\u0440\u0435\u0434\u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e \u0437\u0430\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f, \u0447\u0442\u043e fire \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0435 \u043a\u043e\u0434\u0430, \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0432 CLI, \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u0438\u0437 \u0431\u0430\u0448\u0430 \u0432 Python \u0438 \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u0441\u0432\u043e\u0438\u043c REPL \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c?<\/p>\n<h4>\u041f\u0430\u0440\u0441\u0435\u0440 \u0438 main<\/h4>\n<p>  fire.Fire \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0435\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043b\u044e\u0431\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442: \u043c\u043e\u0434\u0443\u043b\u044c, \u0438\u043d\u0441\u0442\u0430\u043d\u0441 \u043a\u043b\u0430\u0441\u0441\u0430, \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0441 \u0438\u043c\u0435\u043d\u0430\u043c\u0438 \u043a\u043e\u043c\u0430\u043d\u0434 \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. <\/p>\n<p>  \u0427\u0442\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e, \u0442\u0430\u043a \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e Fire \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043a\u043b\u0430\u0441\u0441\u0430. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u043e\u0431\u0449\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u0430\u043d\u0434, \u0430 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u044b \u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438. \u042d\u0442\u0438\u043c \u043c\u044b \u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">class Commands:     def __init__(self, db=None, verbose=False):         level = logging.INFO if verbose else logging.WARNING         logging.basicConfig(level=level)         logging.getLogger(&quot;todolib&quot;).setLevel(level)         self._app = todolib.TodoApp.fromenv(db)         atexit.register(self._app.save)      def version(self):         return todolib.__version__      def add(self, task):         &quot;&quot;&quot;Add new task.&quot;&quot;&quot;         task = self._app.add_task(task)         print(task, &quot;created with number&quot;, task.number, end=&quot;.\\n&quot;)      def show(self, show_done=False):         &quot;&quot;&quot; Show current tasks. &quot;&quot;&quot;         self._app.print_tasks(show_done)      def done(self, number):         &quot;&quot;&quot; Mark task as done. &quot;&quot;&quot;         task = self._app.task_done(number)         print(task, &quot;marked as done.&quot;)      def remove(self, number):         &quot;&quot;&quot; Removes task from the list. &quot;&quot;&quot;         task = self._app.remove_task(number)         print(task, &quot;removed from the list.&quot;)  def main(args=None):     fire.Fire(Commands, command=args) <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0444\u043b\u0430\u0433\u0438<\/h4>\n<p>  \u0423 Fire \u0435\u0441\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0444\u043b\u0430\u0433\u0438 \u0441 \u043e\u0441\u043e\u0431\u044b\u043c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u043e\u043c (\u0438\u0445 \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 &quot;&#8212;&quot;), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0437\u0430\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442 \u043f\u0430\u0440\u0441\u0435\u0440\u0430 \u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0446\u0435\u043b\u043e\u043c:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0432\u044b\u0437\u043e\u0432\u043e\u0432<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"bash\">$ .\/todo_fire.py show -- --trace Fire trace: 1. Initial component 2. Instantiated class &quot;Commands&quot; (todo_fire.py:9) 3. Accessed property &quot;show&quot; (todo_fire.py:25) $ .\/todo_fire.py -- --verbose | head -n 12 # \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432\u044b\u0432\u043e\u0434 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432, \u0442\u0430\u043a\u0438\u0445, \u043a\u0430\u043a Commands._app NAME     todo_fire.py -  SYNOPSIS     todo_fire.py - GROUP | COMMAND  GROUPS     GROUP is one of the following:       _app        Todo Application definition. $ .\/todo_fire.py show -- --interactive Fire is starting a Python REPL with the following objects: Modules: atexit, fire, logging, todolib Objects: Commands, args, component, main, result, self, todo_fire.py, trace  Python 3.7.4 (default, Aug 15 2019, 13:09:37)  [GCC 7.4.0] on linux Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information. (InteractiveConsole) &gt;&gt;&gt; self &lt;__main__.Commands object at 0x7fd0a6125bd0&gt; &gt;&gt;&gt; self._app.db {'tasks': [{'title': 'test', 'done': False}]} <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 main-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e argparse \u0438 docopt, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0435\u0433\u043e \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u0432\u0438\u0436\u0443 \u0441\u043c\u044b\u0441\u043b\u0430. <\/p>\n<p>  \u0412 \u0442\u043e \u0436\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0438\u0437-\u0437\u0430 \u0438\u043d\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043d\u0430\u0442\u0443\u0440\u044b Fire \u043c\u043e\u0436\u043d\u043e \u0441 \u0442\u0435\u043c \u0436\u0435 \u0443\u0441\u043f\u0435\u0445\u043e\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0440\u0430\u0437\u0443 \u043a\u043b\u0430\u0441\u0441 Commands.<\/p>\n<h4>\u0418\u0442\u043e\u0433<\/h4>\n<p>  Fire \u2014 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043d\u0435 \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c click. \u041e\u043d \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u043e\u043f\u0446\u0438\u0439 \u0432 \u043f\u0430\u0440\u0441\u0435\u0440\u0435, \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430, \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0438 \u043e\u043f\u0446\u0438\u0438 \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438, \u0430 \u0441\u0430\u043c\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/github.com\/google\/python-fire\/graphs\/code-frequency\">\u0436\u0438\u0432\u0451\u0442 \u0438 \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0435\u0442\u0441\u044f<\/a> \u0434\u0430\u0436\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u0435\u0435, \u0447\u0435\u043c click (60 \u043a\u043e\u043c\u043c\u0438\u0442\u043e\u0432 \u0437\u0430 \u044d\u0442\u043e \u043b\u0435\u0442\u043e).<\/p>\n<p>  \u041c\u0438\u043d\u0443\u0441\u044b: \u0443\u043c\u0435\u0435\u0442 \u043e\u0449\u0443\u0442\u0438\u043c\u043e \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c click \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u0430\u0440\u0441\u0435\u0440\u044b; \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0439 API (\u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u2014 0.2.1).<\/p>\n<h2>Cement<\/h2>\n<p>  \u0412\u043e\u043e\u0431\u0449\u0435-\u0442\u043e, Cement \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c CLI-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430, \u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u043d\u043e \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043e\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0438 \u0434\u043b\u044f \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432, \u0438 \u0434\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u043c\u0438.<\/p>\n<h4>\u041f\u0430\u0440\u0441\u0435\u0440<\/h4>\n<p>  \u041f\u0430\u0440\u0441\u0435\u0440 \u0432 Cement \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u043e, \u043d\u043e \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0433\u043b\u044f\u0434\u0435\u0442\u044c\u0441\u044f \u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c, \u0442\u043e \u043d\u0435\u0442\u0440\u0443\u0434\u043d\u043e \u0434\u043e\u0433\u0430\u0434\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0441\u0442\u043e\u0438\u0442 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0439 argparse. \u041d\u043e, \u043c\u043e\u0436\u0435\u0442, \u044d\u0442\u043e \u0438 \u043a \u043b\u0443\u0447\u0448\u0435\u043c\u0443 \u2014 \u043d\u0435 \u043d\u0430\u0434\u043e \u0443\u0447\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">from cement import Controller, ex class Base(Controller):     class Meta:         label = &quot;base&quot;          arguments = [             (                 [&quot;-v&quot;, &quot;--version&quot;],                 {&quot;action&quot;: &quot;version&quot;, &quot;version&quot;: f&quot;todo_cement v{todolib.__version__}&quot;},             )         ]      def _default(self):         &quot;&quot;&quot;Default action if no sub-command is passed.&quot;&quot;&quot;         self.app.args.print_help()      @ex(help=&quot;Add new task&quot;, arguments=[([&quot;task&quot;], {&quot;help&quot;: &quot;Task title&quot;})])     def add(self):         title = self.app.pargs.task         self.app.log.debug(f&quot;Task title: {title!r}&quot;)         task = self.app.todoobj.add_task(title)         print(task, &quot;created with number&quot;, task.number, end=&quot;.\\n&quot;)      @ex(         help=&quot;Show current tasks&quot;,         arguments=[             ([&quot;--show-done&quot;], dict(action=&quot;store_true&quot;, help=&quot;Include done tasks&quot;))         ],     )     def show(self):         self.app.todoobj.print_tasks(self.app.pargs.show_done)      @ex(help=&quot;Mark task as done&quot;, arguments=[([&quot;number&quot;], {&quot;type&quot;: int})])     def done(self):         task = self.app.todoobj.task_done(self.app.pargs.number)         print(task, &quot;marked as done.&quot;)      @ex(help=&quot;Remove task from the list&quot;, arguments=[([&quot;number&quot;], {&quot;type&quot;: int})])     def remove(self):         task = self.app.todoobj.remove_task(self.app.pargs.number)         print(task, &quot;removed from the list.&quot;) <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>App \u0438 main<\/h4>\n<p>  Cement, \u043a\u0440\u043e\u043c\u0435 \u0432\u0441\u0435\u0433\u043e \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e, \u0435\u0449\u0451 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0438\u0433\u043d\u0430\u043b\u044b \u0432 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f. \u0417\u0434\u0435\u0441\u044c \u044d\u0442\u043e \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u0441 \u043d\u0443\u043b\u0435\u0432\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u043f\u0440\u0438 SIGINT\/SIGTERM.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">class TodoApp(App):     def __init__(self, argv=None):         super().__init__(argv=argv)         self.todoobj = None      def load_db(self):         self.todoobj = todolib.TodoApp.fromenv()      def save(self):         self.todoobj.save()      class Meta:         # application label         label = &quot;todo_cement&quot;         # register handlers         handlers = [Base]         hooks = [(&quot;post_setup&quot;, lambda app: app.load_db()), (&quot;pre_close&quot;, lambda app: app.save())]         # call sys.exit() on close         close_on_exit = True   def main():     with TodoApp() as app:         try:             app.run()         except CaughtSignal as e:             if e.signum not in (signal.SIGINT, signal.SIGTERM):                 raise             app.log.debug(f&quot;\\n{e}&quot;)             app.exit_code = 0 <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0415\u0441\u043b\u0438 \u0432\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u0432 main, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 todolib.TodoApp \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u0438 \u0432 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0445 __enter__\/__exit__, \u043d\u043e \u044d\u0442\u0438 \u0444\u0430\u0437\u044b \u0432 \u0438\u0442\u043e\u0433\u0435 \u0431\u044b\u043b\u0438 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u044b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0445\u0443\u043a\u0438 Cement.<\/p>\n<h4>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u0442 \u0436\u0435 \u043a\u043b\u0430\u0441\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\">def test_cement(capsys):     with todo_cement.TodoApp(argv=[&quot;add&quot;, &quot;test&quot;]) as app:         app.run()         out, _ = capsys.readouterr()         assert out == EXPECTED         # \u0434\u043b\u044f \u0430\u0441\u0441\u0435\u0440\u0442\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0442 jinja, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f         assert app.last_rendered is None <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0418\u0442\u043e\u0433\u0438<\/h4>\n<p>  \u041f\u043b\u044e\u0441\u044b: \u041d\u0430\u0431\u043e\u0440 API \u043f\u043e\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u043d\u0430\u0431\u043e\u0440 \u0448\u0432\u0435\u0439\u0446\u0430\u0440\u0441\u043a\u043e\u0433\u043e \u043d\u043e\u0436\u0430, \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0445\u0443\u043a\u0438 \u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430.<\/p>\n<p>  \u041c\u0438\u043d\u0443\u0441\u044b: \u041c\u0435\u0441\u0442\u0430\u043c\u0438 \u043f\u0443\u0441\u0442\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f; \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 Cement \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0432\u0430\u0442\u044b\u043c\u0438.<\/p>\n<h2>Cleo<\/h2>\n<p>  Cleo \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043a\u0430\u043a \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u0437\u0434\u0435\u0441\u044c (\u0432\u0441\u0435\u0433\u043e \u043e\u043a\u043e\u043b\u043e 400 \u0437\u0432\u0451\u0437\u0434 \u043d\u0430 GitHub), \u0438 \u0432\u0441\u0451 \u0436\u0435 \u043c\u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c, \u043a\u043e\u0433\u0434\u0430 \u044f \u0438\u0437\u0443\u0447\u0430\u043b, \u043a\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c Poetry \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430.<\/p>\n<p>  \u0422\u0430\u043a \u0432\u043e\u0442, Cleo \u2014 \u044d\u0442\u043e \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0430\u0432\u0442\u043e\u0440\u0430 \u0443\u0436\u0435 \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u043e\u0433\u043e Poetry, \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, virtualenv&#8217;\u0430\u043c\u0438 \u0438 \u0441\u0431\u043e\u0440\u043a\u0430\u043c\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u041f\u0440\u043e Poetry \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 \u0443\u0436\u0435 \u043d\u0435 \u0440\u0430\u0437 \u043f\u0438\u0441\u0430\u043b\u0438, \u0430 \u043f\u0440\u043e \u0435\u0433\u043e \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u2014 \u043d\u0435\u0442.<\/p>\n<h4>\u041f\u0430\u0440\u0441\u0435\u0440<\/h4>\n<p>  Cleo, \u043a\u0430\u043a \u0438 Cement, \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043d\u044b\u0445 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u0445, \u0442.\u0435. \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u043a\u043b\u0430\u0441\u0441 Command \u0438 \u0435\u0433\u043e docstring, \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 option(), \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043c\u0435\u0442\u043e\u0434 line(), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0442\u0435\u043a\u0441\u0442\u0430, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0441\u0442\u0438\u043b\u0438 (\u0442.\u0435. \u0446\u0432\u0435\u0442\u0430) \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044e \u0432\u044b\u0432\u043e\u0434\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 verbose-\u0444\u043b\u0430\u0433\u043e\u0432 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438. \u0410 \u0435\u0449\u0451 \u0443 cleo \u0435\u0441\u0442\u044c \u0432\u044b\u0432\u043e\u0434 \u0442\u0430\u0431\u043b\u0438\u0446. \u0410 \u0435\u0449\u0451 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441-\u0431\u0430\u0440\u044b. \u0410 \u0435\u0449\u0451\u2026 \u0412 \u043e\u0431\u0449\u0435\u043c, \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">from cleo import Command as BaseCommand # cleo \u044d\u0442\u043e \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 clikit, \u0438 \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043a \u043d\u0435\u0439 from clikit.api.io import flags as verbosity  class Command(BaseCommand):     def __init__(self):         super().__init__()         self.todoapp = None      def handle(self):         with todolib.TodoApp.fromenv() as app:             self.todoapp = app             self.do_handle()      def do_handle(self):         raise NotImplementedError   class AddCommand(Command):     &quot;&quot;&quot;     Add new task.      add {task : Task to add}     &quot;&quot;&quot;      def do_handle(self):         title = self.argument(&quot;task&quot;)         task = self.todoapp.add_task(title)         # will be printed only on &quot;-vvv&quot;         self.line(f&quot;Title: {title}&quot;, style=&quot;comment&quot;, verbosity=verbosity.DEBUG)         self.line(f&quot;Task &lt;info&gt;{task.title}&lt;\/&gt; created with number {task.number}.&quot;)   class ShowCommand(Command):     &quot;&quot;&quot;     Show current tasks.      show {--show-done : Include tasks that are done.}     &quot;&quot;&quot;      def do_handle(self):         tasks = self.todoapp.list_tasks(self.option(&quot;show-done&quot;))         if not tasks:             self.line(&quot;There is no TODOs.&quot;, style=&quot;info&quot;)         self.render_table(             [&quot;Number&quot;, &quot;Title&quot;, &quot;Status&quot;],             [                 [str(task.number), task.title, &quot;&quot; if task.done else &quot;\u2718&quot;]                 for task in tasks             ],         )   class DoneCommand(Command):     &quot;&quot;&quot;     Mark task as done.      done {number : Task number}     &quot;&quot;&quot;      def do_handle(self):         task = self.todoapp.task_done(int(self.argument(&quot;number&quot;)))         self.line(f&quot;Task &lt;info&gt;{task.title}&lt;\/&gt; marked as done.&quot;)   class RemoveCommand(Command):     &quot;&quot;&quot;     Removes task from the list.      remove {number : Task number}     &quot;&quot;&quot;      def do_handle(self):         task = self.todoapp.remove_task(int(self.argument(&quot;number&quot;)))         self.line(f&quot;Task &lt;info&gt;{task.title}&lt;\/&gt; removed from the list.&quot;) <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>main<\/h4>\n<p>  \u0412\u0441\u0451, \u0447\u0442\u043e \u043d\u0430\u0434\u043e, \u044d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 <i>cleo.Application<\/i> \u0438 \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u043c\u0443 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 add_commands. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u0432\u0441\u0451 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u043e \u0438\u0437 main \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440:<\/p>\n<pre><code class=\"python\">from cleo import Application as BaseApplication   class TodoApp(BaseApplication):     def __init__(self):         super().__init__(name=&quot;ToDo app - cleo version&quot;, version=todolib.__version__)         self.add_commands(AddCommand(), ShowCommand(), DoneCommand(), RemoveCommand())   def main(args=None):     TodoApp().run(args=args) <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434 \u0432 Cleo \u0435\u0441\u0442\u044c <i>CommandTester<\/i>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u043a\u0430\u043a \u0438 \u0432\u0441\u0435 \u0432\u0437\u0440\u043e\u0441\u043b\u044b\u0435 <s>\u0434\u044f\u0434\u0438<\/s> \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 I\/O \u0438 exit code:<\/p>\n<pre><code class=\"python\">def test_cleo():     app = todo_cleo.TodoApp()     command = app.find(&quot;add&quot;)     tester = cleo.CommandTester(command)     tester.execute(&quot;test&quot;)     assert tester.status_code == 0     assert tester.io.fetch_output() == &quot;Task test created with number 0.\\n&quot; <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0418\u0442\u043e\u0433<\/h4>\n<p>  \u041f\u043b\u044e\u0441\u044b: \u043e\u0431\u044a\u0435\u043a\u0442\u043d\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0441 \u043d\u0430\u043b\u0438\u0447\u0438\u0435\u043c type hints, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 (\u0442.\u043a. \u043c\u043d\u043e\u0433\u0438\u0435 IDE \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u044b \u0438\u043c\u0435\u044e\u0442 \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u041e\u041e\u041f-\u043a\u043e\u0434\u0430 \u0438 \u043c\u043e\u0434\u0443\u043b\u044f typing); \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043e\u0431\u044a\u0451\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438, \u043d\u043e \u0438 I\/O.<\/p>\n<p>  \u041f\u043b\u044e\u0441-\u043c\u0438\u043d\u0443\u0441: \u0441\u0432\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 verbosity, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0441 I\/O Cleo\/CliKit. \u0418 \u0445\u043e\u0442\u044f \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 handler \u0434\u043b\u044f \u043c\u043e\u0434\u0443\u043b\u044f logging, \u0435\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435\u043c cleo.<\/p>\n<p>  \u041c\u0438\u043d\u0443\u0441\u044b: \u044f\u0432\u043d\u043e \u2014 \u043b\u0438\u0447\u043d\u043e\u0435 \u043c\u043d\u0435\u043d\u0438\u0435 \u2014 \u043c\u043e\u043b\u043e\u0434\u043e\u0435 API: \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u00ab\u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e\u00bb \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a\u0440\u043e\u043c\u0435 Poetry, \u0430 \u0442\u0430\u043a Cleo \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435\u043c \u0438 \u043f\u043e\u0434 \u043d\u0443\u0436\u0434\u044b \u0435\u0433\u043e \u043e\u0434\u043d\u043e\u0433\u043e; \u043c\u0435\u0441\u0442\u0430\u043c\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0430\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0440\u043e\u0432\u043d\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043f\u0435\u0440\u044c \u043b\u0435\u0436\u0430\u0442 \u043d\u0435 \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 clikit, \u0430 \u0432 clikit.api.io.flags), \u0434\u0430 \u0438 \u0432 \u0446\u0435\u043b\u043e\u043c \u043e\u043d\u0430 \u0441\u043a\u0443\u0434\u043d\u0430 \u0438 \u043d\u0435 \u043e\u0442\u0440\u0430\u0436\u0430\u0435\u0442 \u0432\u0441\u0435\u0433\u043e API.<\/p>\n<p>  Cleo, \u0432 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0438 \u0441 Cement, \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u043e\u0432\u0430\u043d \u043d\u0430 CLI, \u0438 \u043e\u043d \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439, \u043a\u0442\u043e \u0437\u0430\u0434\u0443\u043c\u0430\u043b\u0441\u044f \u043e \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 (\u0441\u043a\u0440\u044b\u0442\u0438\u0438 \u0441\u0442\u0435\u043a\u0442\u0440\u0435\u0439\u0441\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e) \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0432 \u0432\u044b\u0432\u043e\u0434\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u041d\u043e \u043e\u043d \u2014 \u0441\u043d\u043e\u0432\u0430 \u043b\u0438\u0447\u043d\u043e\u0435 \u043c\u043d\u0435\u043d\u0438\u0435 \u2014 \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442 Cement&#8217;\u0443 \u0432 \u0441\u0432\u043e\u0435\u0439 \u044e\u043d\u043e\u0441\u0442\u0438 \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 API.<\/p>\n<h2>\u0412 \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>  \u041a \u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0443 \u0432\u0441\u0435\u0445 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0451 \u043c\u043d\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c: \u043c\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f Click, \u0437\u0430 \u0442\u043e, \u0447\u0442\u043e \u0432 \u043d\u0451\u043c \u043c\u043d\u043e\u0433\u043e \u0447\u0435\u0433\u043e \u0435\u0441\u0442\u044c \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0441 \u043d\u0438\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u0442\u0430\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u043f\u043e \u043c\u0438\u043d\u0438\u043c\u0443\u043c\u0443 \u2014 \u043d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 Fire. \u0412\u0430\u0448\u0435\u043c\u0443 \u0441\u043a\u0440\u0438\u043f\u0442\u0443 \u043d\u0443\u0436\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f \u0432 Memcached, \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 jinja \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c \u2014 \u0431\u0435\u0440\u0438\u0442\u0435 Cement \u0438 \u043d\u0435 \u043f\u043e\u0436\u0430\u043b\u0435\u0435\u0442\u0435. \u0423 \u0432\u0430\u0441 \u043f\u0435\u0442-\u043f\u0440\u043e\u0434\u0436\u0435\u043a\u0442 \u0438\u043b\u0438 \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u043e\u0435 \u2014 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 cleo.<\/div>\n<p>               <script class=\"js-mediator-script\">!function(e){function t(t,n){if(!(n in e)){for(var r,a=e.document,i=a.scripts,o=i.length;o--;)if(-1!==i[o].src.indexOf(t)){r=i[o];break}if(!r){r=a.createElement(\"script\"),r.type=\"text\/javascript\",r.async=!0,r.defer=!0,r.src=t,r.charset=\"UTF-8\";var d=function(){var e=a.getElementsByTagName(\"script\")[0];e.parentNode.insertBefore(r,e)};\"[object Opera]\"==e.opera?a.addEventListener?a.addEventListener(\"DOMContentLoaded\",d,!1):e.attachEvent(\"onload\",d):d()}}}t(\"\/\/mediator.mail.ru\/script\/2820404\/\",\"_mediator\")}(window);<\/script>     <br \/> \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\/466999\/\"> https:\/\/habr.com\/ru\/post\/466999\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html js-mediator-article\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/rp\/ek\/be\/rpekben_u6jtlwru-flsslxk70e.png\"\/><\/p>\n<p>  Python \u2014 \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 \u044f\u0437\u044b\u043a \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438 \u044d\u0442\u043e \u043f\u043e\u0434\u0447\u0451\u0440\u043a\u0438\u0432\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0437\u0430\u0434\u0430\u0447. <i>\u041d\u043e \u043a\u0430\u043a\u0438\u0435 \u0432\u043e\u043e\u0431\u0449\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442? \u0410 \u043a\u0430\u043a\u0443\u044e \u043b\u0443\u0447\u0448\u0435 \u0432\u0437\u044f\u0442\u044c?<\/i> \u0412 \u044d\u0442\u043e\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0435 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0438 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0438\u0440\u0430 \u0438 \u0434\u0430\u043d\u0430 \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u043f\u0440\u043e\u0441.<\/p>\n<p>  \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0447\u0442\u0435\u043d\u0438\u044f \u043e\u0431\u0437\u043e\u0440 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d \u043d\u0430 \u0434\u0432\u0430 \u043f\u043e\u0441\u0442\u0430: \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0448\u0435\u0441\u0442\u044c \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u2014 \u043c\u0435\u043d\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0435, \u043d\u043e \u0432\u0441\u0451 \u0436\u0435 \u0437\u0430\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u044e\u0449\u0438\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044f.   <\/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-294541","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/294541","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=294541"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/294541\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=294541"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=294541"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=294541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}