{"id":363606,"date":"2024-05-21T01:59:39","date_gmt":"2024-05-21T01:59:39","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=363606"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=363606","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c \u0441\u0432\u043e\u0439 GraphQL \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0430 Python<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0440\u043e\u0434\u0438\u043b\u0430\u0441\u044c \u0438\u0437 \u043e\u043f\u044b\u0442\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f <strong>GraphQL<\/strong> \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0438\u0445 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u043e\u0432 \u0420\u0424. \u041f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0439\u0441\u043e\u0432 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c\u0438 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u0430 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (<strong>MRMS<\/strong> \u0441\u0438\u0441\u0442\u0435\u043c\u0430).<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043d\u0430 \u0431\u0430\u0437\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0433\u0434\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0432\u0438\u0434\u0435 GraphQL API, \u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 API, \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 <strong>java<\/strong>. \u041a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438 \u044d\u0442\u043e\u0433\u043e API \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e <strong>web\/mobile<\/strong>, \u043d\u043e \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043d\u0430 <strong>java<\/strong>, <strong>golang<\/strong> \u0438 <strong>python<\/strong>.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043a\u0430\u043a \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0441\u0432\u043e\u0435\u0433\u043e GraphQL \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0430 python \u0441 \u043d\u0443\u043b\u044f, \u0433\u0434\u0435 \u0430\u0432\u0442\u043e\u0440 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0449\u0438\u0435 \u043d\u0430 \u044d\u0442\u043e\u043c \u043f\u0443\u0442\u0438. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u0443\u044e GraphQL \u0441\u0445\u0435\u043c\u0443 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u0430 \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u0443\u044e \u0441\u0445\u0435\u043c\u0443 <a href=\"https:\/\/docs.github.com\/en\/graphql\" rel=\"noopener noreferrer nofollow\"><strong>github GraphQL API<\/strong><\/a>.<\/p>\n<h4>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h4>\n<ol>\n<li>\n<p><a href=\"#%D0%BE_%D1%87%D0%B5%D0%BC_%D1%8D%D1%82%D0%B0_%D1%81%D1%82%D0%B0%D1%82%D1%8C%D1%8F\" rel=\"noopener noreferrer nofollow\">\u0427\u0442\u043e \u043d\u0430\u0441 \u0436\u0434\u0435\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81\" rel=\"noopener noreferrer nofollow\">\u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" rel=\"noopener noreferrer nofollow\">\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%B0%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" rel=\"noopener noreferrer nofollow\">\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D0%BE%D0%BB%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" rel=\"noopener noreferrer nofollow\">\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%B0%D0%BC%D0%B8\" rel=\"noopener noreferrer nofollow\">\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85\" rel=\"noopener noreferrer nofollow\">\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ol>\n<h3>\u0427\u0442\u043e \u043d\u0430\u0441 \u0436\u0434\u0435\u0442<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BE_%D1%87%D0%B5%D0%BC_%D1%8D%D1%82%D0%B0_%D1%81%D1%82%D0%B0%D1%82%D1%8C%D1%8F\" id=\"\u043e_\u0447\u0435\u043c_\u044d\u0442\u0430_\u0441\u0442\u0430\u0442\u044c\u044f\"><\/a><\/p>\n<p>\u0412\u043e\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c<\/p>\n<ul>\n<li>\n<p>\u043a\u0430\u043a \u0441\u043e\u0432\u0435\u0440\u0448\u0438\u0442\u044c GraphQL \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430?<\/p>\n<\/li>\n<li>\n<p>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443;<\/p>\n<\/li>\n<li>\n<p>\u043a\u0430\u043a \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u0432\u043e\u0435\u0439 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432?<\/p>\n<\/li>\n<li>\n<p>\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 \u0432\u0438\u0434\u0435 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432;<\/p>\n<\/li>\n<\/ul>\n<p>\u0423\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043e\u0444. \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e. \u041d\u043e \u0434\u043b\u044f \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e\u0433\u043e \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u044b GraphQL. \u0422\u0430\u043a \u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043b\u044e\u0441\u043e\u043c \u0438\u043c\u0435\u0442\u044c \u043e\u043f\u044b\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 <strong>requests, asyncio<\/strong> \u0438 <strong>aiohttp<\/strong>.<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/github.com\/denisart\/github-graphql-client\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>. \u041a\u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 <strong>python 3.11<\/strong> \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <strong>poetry<\/strong>, \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d \u0438 \u043e\u0442\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/black.readthedocs.io\/en\/stable\/\" rel=\"noopener noreferrer nofollow\">black<\/a>.<\/p>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0\" id=\"\u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430\"><\/a><\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c\u0441\u044f \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 github GraphQL API. \u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0435 python \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435. \u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/python-poetry.org\/\" rel=\"noopener noreferrer nofollow\">poetry<\/a> \u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<pre><code class=\"bash\">\u251c\u2500\u2500 github_graphql_client\/  &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u0434 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u2502   \u2514\u2500\u2500 __init__.py \u251c\u2500\u2500 tests\/                  &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0443\u0442 \u0442\u0435\u0441\u0442\u044b \u2502   \u2514\u2500\u2500 __init__.py \u251c\u2500\u2500 scripts\/                &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u251c\u2500\u2500 pyproject.toml          &lt;- \u0424\u0430\u0439\u043b \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u251c\u2500\u2500 README.md \u251c\u2500\u2500 .env                    &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0443\u0442 \u0432\u0441\u044f\u043a\u0438\u0435 sensitive \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u2514\u2500\u2500 .gitignore<\/code><\/pre>\n<p>\u041a\u043e\u043d\u0444\u0438\u0433 <strong>pyproject.toml<\/strong> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439<\/p>\n<pre><code># `pyproject.toml` file [tool.poetry] name = \"github_graphql_client\" version = \"0.1.0\" description = \"Github GraphQL client for Habr.\" authors = [\"FirstName SecondName &lt;email>\"] readme = \"README.md\" packages = [{include = \"github_graphql_client\"}]  [tool.poetry.dependencies] python = \"^3.11\"   [build-system] requires = [\"poetry-core\"] build-backend = \"poetry.core.masonry.api\"<\/code><\/pre>\n<p>\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, Github GraphQL<strong> endpoint<\/strong> \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"https:\/\/api.github.com\/graphql\" rel=\"noopener noreferrer nofollow\">https:\/\/api.github.com\/graphql<\/a>. \u0427\u0442\u043e\u0431\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f API \u2014 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u043e \u0432\u044b\u043f\u0443\u0441\u043a \u0442\u043e\u043a\u0435\u043d\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0442\u0443\u0442: <a href=\"https:\/\/docs.github.com\/en\/graphql\/guides\/forming-calls-with-graphql\" rel=\"noopener noreferrer nofollow\">Github &#8212; Forming calls with GraphQL<\/a>. \u0412 \u0444\u0430\u0439\u043b\u0435 <strong>.env<\/strong> \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<pre><code class=\"bash\">GITHUB_TOKEN=&lt;YOUR_TOKEN> GITHUB_GRAPHQL_ENDPOINT=https:\/\/api.github.com\/graphql<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u0430\u0439\u043b\u043e\u043c <strong>.env<\/strong> \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442 <a href=\"https:\/\/pypi.org\/project\/python-dotenv\/\" rel=\"noopener noreferrer nofollow\">python-dotenv<\/a>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043d\u0430\u0448\u0435\u0439 \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<pre><code class=\"bash\">$ poetry add  python-dotenv Creating virtualenv blah-blah-py3.11 in \/home\/blah-blah\/pypoetry\/virtualenvs Using version ^1.0.0 for python-dotenv  Updating dependencies Resolving dependencies... (0.1s)  Package operations: 1 install, 0 updates, 0 removals    \u2022 Installing python-dotenv (1.0.0)  Writing lock file<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b <strong>poetry add<\/strong> \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0444\u0430\u0439\u043b <strong>poetry.lock<\/strong>, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430\u0448\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 <strong>pyproject.toml<\/strong><\/p>\n<pre><code># `pyproject.toml` file # ...  [tool.poetry.dependencies] python = \"^3.11\" python-dotenv = \"^1.0.0\"  # ...<\/code><\/pre>\n<p>\u0418, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c <code>poetry install<\/code> \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u0441\u0435\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<h3>\u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81\" id=\"\u043f\u0435\u0440\u0432\u044b\u0439_\u0437\u0430\u043f\u0440\u043e\u0441\"><\/a><\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u043d\u0430\u0448 \u043a\u043b\u0438\u0435\u043d\u0442 \u2014 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443. <a href=\"https:\/\/graphql.org\/learn\/best-practices\/\" rel=\"noopener noreferrer nofollow\">\u041e\u0431\u044b\u0447\u043d\u043e<\/a> GraphQL \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 <strong>HTTP\/HTTPS<\/strong> \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 <strong>POST<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 <strong>json<\/strong> \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 <code>query: str<\/code> \u0438 <code>variables: dict[str, Any]<\/code>. \u0422\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u0432 github GraphQL API. \u0421\u0445\u0435\u043c\u0443 github GraphQL API \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/docs.github.com\/en\/graphql\/overview\/public-schema\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>.  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0437\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u043e\u0432\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<pre><code class=\"bash\">$ mkdir github_graphql_client\/transport $ touch github_graphql_client\/transport\/__init__.py github_graphql_client\/transport\/base.py<\/code><\/pre>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>github_graphql_client\/transport\/base.py<\/strong> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c <strong>BaseTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/base.py` file from typing import Any  class BaseTransport:     \"\"\"An abstract transport.\"\"\"      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query.\"\"\"         raise NotImplementedError      def connect(self) -> None:         \"\"\"Establish a session with the transport.\"\"\"         raise NotImplementedError      def close(self) -> None:         \"\"\"Close a session.\"\"\"         raise NotImplementedError<\/code><\/pre>\n<p>\u041b\u044e\u0431\u043e\u0439 \u043a\u043b\u0430\u0441\u0441 \u0442\u0438\u043f\u0430 <strong>Transport<\/strong> \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 <strong>BaseTransport<\/strong>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430<\/p>\n<ul>\n<li>\n<p><strong>connect<\/strong> \u2014 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c;<\/p>\n<\/li>\n<li>\n<p><strong>close<\/strong> \u2014 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p><strong>execute<\/strong> \u2014 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <strong>query<\/strong> \u0441 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 <strong>variables<\/strong>;<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a, \u043a\u0430\u043a GraphQL \u0437\u0430\u043f\u0440\u043e\u0441 \u0435\u0441\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u044b\u0439 POST \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u043a\u0435\u0442\u0430 <a href=\"https:\/\/pypi.org\/project\/requests\/\" rel=\"noopener noreferrer nofollow\">requests<\/a>. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c<\/p>\n<pre><code class=\"bash\">$ poetry add requests<\/code><\/pre>\n<p>\u0412 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b <strong>github_graphql_client\/transport\/requests.py<\/strong> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/requests.py` file from typing import Any, Optional  import requests as r  from github_graphql_client.transport.base import BaseTransport  class RequestsTransport(BaseTransport):     \"\"\"The transport based on requests library.\"\"\"      session: Optional[r.Session]      def __init__(self, endpoint: str, token: str, **kwargs: Any) -> None:         self.endpoint = endpoint         self.token = token         self.auth_header = {\"Authorization\": f\"Bearer {self.token}\"}          self.session = None      def connect(self) -> None:         \"\"\"Start a `requests.Session` connection.\"\"\"         if self.session is None:             self.session = r.Session()         else:             raise Exception(\"Session already started\")      def close(self) -> None:         \"\"\"Closing `requests.Session` connection.\"\"\"         if self.session is not None:             self.session.close()             self.session = None      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query.\"\"\"         if self.session is None:             raise Exception(f\"RequestsTransport session not connected\")          post_args = {             \"headers\": self.auth_header,             \"json\": {\"query\": query, \"variables\": variables},         }         post_args[\"headers\"][\"Content-Type\"] = \"application\/json\"          response = self.session.request(\"POST\", self.endpoint, **post_args)          result = response.json()         return result.get(\"data\")<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<ul>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>connect<\/strong> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 <strong>requests.Session<\/strong> \u0435\u0441\u043b\u0438 \u043e\u043d \u043d\u0435 \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u0440\u0430\u043d\u0435\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>close<\/strong> \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <strong>requests.Session<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>execute<\/strong> \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <strong>requests.Session<\/strong> \u043e\u0431\u044b\u0447\u043d\u044b\u0439 POST \u0437\u0430\u043f\u0440\u043e\u0441;<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c, \u0447\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u043c\u044b \u0443\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435.  \u041e\u0442\u043f\u0440\u0430\u0432\u0438\u043c query, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0432\u044b\u0445 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0445 <strong>issues<\/strong>  \u0438\u0437 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e github \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f. \u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0430\u0432\u0442\u043e\u0440\u0430 <strong>pydantic<\/strong> &#8212; <a href=\"https:\/\/github.com\/pydantic\/FastUI\" rel=\"noopener noreferrer nofollow\">FastUI<\/a>. \u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u0443\u0434\u0435\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435<\/p>\n<pre><code class=\"bash\">$ mkdir github_graphql_client\/queries $ touch github_graphql_client\/queries\/__init__.py github_graphql_client\/queries\/repository.py<\/code><\/pre>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/repository.py` file repository_issues_query = \"\"\" query {   repository(owner:\"pydantic\", name:\"FastUI\") {     issues(last:2, states:CLOSED) {       edges {         node {           title           url         }       }     }   } } \"\"\"<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043a\u0440\u0438\u043f\u0442 <strong>scripts\/run.py<\/strong><\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file import os import time from typing import Any  from dotenv import load_dotenv  from github_graphql_client.client.requests_client import RequestsClient from github_graphql_client.queries.repository import repository_issues_query  load_dotenv()  # take environment variables from .env  GITHUB_TOKEN = os.environ.get(\"GITHUB_TOKEN\") GITHUB_GRAPHQL_ENDPOINT = os.environ.get(\"GITHUB_GRAPHQL_ENDPOINT\")  def main():     transport = RequestsTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT, token=GITHUB_TOKEN     )     transport.connect()      data = transport.execute(         query=repository_issues_query, variables={},     )     print(data)          transport.close()  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}}<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0436\u0438\u0432\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0435\u0439\u0447\u0430\u0441 issues \u0431\u0443\u0434\u0443\u0442 \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<\/p>\n<h4>\u0422\u0430\u0439\u043c\u0430\u0443\u0442<\/h4>\n<p>\u0425\u043e\u0440\u043e\u0448\u043e, \u0435\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441. \u041f\u0440\u043e\u0446\u0438\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0434\u043d\u0443 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0443\u044e <a href=\"https:\/\/habr.com\/ru\/companies\/oleg-bunin\/articles\/433476\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u044e<\/a><\/p>\n<blockquote>\n<p>\u041d\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0443 \u2014 \u044d\u0442\u043e \u0437\u043b\u043e. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0432\u044b \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0435, \u043a\u0430\u043a \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430.<\/p>\n<\/blockquote>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0432 <strong>RequestsTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/requests.py` file ...  class RequestsTransport(BaseTransport):     \"\"\"The transport based on requests library.\"\"\"      DEFAULT_TIMEOUT: int = 1     session: Optional[r.Session]      def __init__(self, endpoint: str, token: str, **kwargs: Any) -> None:         ...          self.timeout = kwargs.get(\"timeout\", RequestsTransport.DEFAULT_TIMEOUT)      ...      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         ...          post_args = {             \"headers\": self.auth_header,             \"json\": {\"query\": query, \"variables\": variables},             \"timeout\": self.timeout,         }         ...<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043d\u0430\u0448 \u043a\u043e\u0434 \u0443\u043a\u0430\u0437\u0430\u0432 \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 (\u0434\u043b\u044f github GraphQL API) \u0442\u0430\u0439\u043c\u0430\u0443\u0442<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  def main():     transport = RequestsTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,         timeout=0.0001,     )     ...  ...<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0432 <strong>scripts\/run.py<\/strong>, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py ... requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host='api.github.com', port=443): Max retries exceeded with url: \/graphql (Caused by ConnectTimeoutError(&lt;urllib3.connection.HTTPSConnection object at 0x7f5510137d50>, 'Connection to api.github.com timed out. (connect timeout=0.0001)'))<\/code><\/pre>\n<p>\u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f requests, \u0430 \u043c\u044b \u043f\u043e\u0439\u0434\u0435\u043c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<h3>\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/h3>\n<p><a class=\"anchor\" name=\"%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" id=\"\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439_\u043a\u043b\u0438\u0435\u043d\u0442\"><\/a><\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0443\u0447\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 <strong>BaseTransport<\/strong>. \u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434<\/p>\n<pre><code class=\"bash\">$ mkdir github_graphql_client\/client $ touch github_graphql_client\/client\/__init__.py github_graphql_client\/client\/sync_client.py<\/code><\/pre>\n<p>\u0432 \u0444\u0430\u0439\u043b <strong>github_graphql_client\/client\/sync_client.py<\/strong> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/client\/sync_client.py` file from typing import Any  from github_graphql_client.transport.base import BaseTransport  class SyncGraphQLClient:     \"\"\"Sync GraphQL client based on `BaseTransport` transport.\"\"\"      transport: BaseTransport      def __init__(self, transport: BaseTransport) -> None:         self.transport = transport      def __enter__(self):         self.connect_sync()         return self      def __exit__(self, *args):         self.close_sync()      def connect_sync(self) -> None:         \"\"\"Connect to `self.transport`.\"\"\"         self.transport.connect()      def close_sync(self) -> None:         \"\"\"Close `self.transport` connection.\"\"\"         self.transport.close()      def execute_sync(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         return self.transport.execute(query, variables, **kwargs)<\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441 <strong>SyncGraphQLClient<\/strong> \u0443\u043c\u0435\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0435\u0441\u0441\u0438\u044e \u0434\u043b\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 <strong>BaseTransport<\/strong> \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043e\u043f\u0443\u0441\u043a\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0430\u043c\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<ul>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>connect_sync<\/strong> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 <strong>self.transport<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>close_sync<\/strong> \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 <strong>self.transport<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>execute_sync<\/strong> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 <strong>self.transport<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434\u044b <strong>__enter__<\/strong> \u0438 <strong>__exit__<\/strong> \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0435 \u0438 \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435;<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  from github_graphql_client.client.sync_client import SyncGraphQLClient  ...  def main():     transport = RequestsTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )      with SyncGraphQLClient(transport=transport) as client:         data = client.execute_sync(             query=repository_issues_query, variables={},         )         print(data)  ...<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0432 <strong>scripts\/run.py<\/strong>, \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0442\u043e\u0442 \u0436\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}}<\/code><\/pre>\n<blockquote>\n<p>\u0421\u043b\u043e\u0432\u043e <strong>sync<\/strong> \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0438 <strong>SyncGraphQLClient<\/strong> \u043d\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e. \u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0437\u0436\u0435 \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f <strong>AsyncGraphQLClient<\/strong>, \u0438 \u043c\u044b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u0438\u0445 \u0432 \u043e\u0434\u0438\u043d <strong>GraphQLClient<\/strong>.<\/p>\n<\/blockquote>\n<h4>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/h4>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u043d\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0436\u0435\u043b\u0430\u0435\u043c\u043e\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/repository.py` file def get_repository_issues_query(owner: str, name: str) -> str:     return \"\"\"query {   repository(owner:\"%s\", name:\"%s\") {     issues(last:2, states:CLOSED) {       edges {         node {           title           url         }       }     }   } } \"\"\" % (         owner,         name,     )<\/code><\/pre>\n<p>\u041d\u0430\u0448 \u0441\u043a\u0440\u0438\u043f\u0442 <strong>scripts\/run.py<\/strong> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  def main():     transport = RequestsTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )      with SyncGraphQLClient(transport=transport) as client:         data = client.execute_sync(             query=get_repository_issues_query(\"pydantic\", \"FastUI\"),             variables={},         )         print(data)                  data = client.execute_sync(             query=get_repository_issues_query(\"pydantic\", \"pydantic\"),             variables={},         )         print(data)                  data = client.execute_sync(             query=get_repository_issues_query(\"pydantic\", \"pydantic-core\"),             variables={},         )         print(data)  ...<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}} {'repository': {'issues': {'edges': [{'node': {'title': \"__init__.cpython-311-darwin.so  is an incompatible architecture (have 'x86_64', need 'arm64') in M1 mac mini\", 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8396'}}, {'node': {'title': 'Override class used in annotations', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8408'}}]}}} {'repository': {'issues': {'edges': [{'node': {'title': '2.14.4 release upload failed', 'url': 'https:\/\/github.com\/pydantic\/pydantic-core\/issues\/1082'}}, {'node': {'title': \"(?) `ValidationError` can't be instantiated\", 'url': 'https:\/\/github.com\/pydantic\/pydantic-core\/issues\/1115'}}]}}}<\/code><\/pre>\n<h3>\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/h3>\n<p><a class=\"anchor\" name=\"%D0%B0%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" id=\"\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439_\u043a\u043b\u0438\u0435\u043d\u0442\"><\/a><\/p>\n<p>\u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u043e\u0436\u0438\u0434\u0430\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b\u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441. \u0417\u0430\u0447\u0435\u043c \u043d\u0430\u043c \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438? \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>github_graphql_client\/transport\/base.py<\/strong> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <strong>BaseAsyncTransport <\/strong>\u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 <strong>BaseTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/base.py` file ...  class BaseAsyncTransport:     \"\"\"An abstract async transport.\"\"\"      async def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query.\"\"\"         raise NotImplementedError      async def connect(self) -> None:         \"\"\"Establish a session with the transport.\"\"\"         raise NotImplementedError      async def close(self) -> None:         \"\"\"Close a session.\"\"\"         raise NotImplementedError<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 backend \u043d\u0430 <a href=\"https:\/\/docs.aiohttp.org\/en\/stable\/index.html\" rel=\"noopener noreferrer nofollow\"><strong>aiohttp<\/strong><\/a>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u044d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442<\/p>\n<pre><code class=\"bash\">$ poetry add aiohttp<\/code><\/pre>\n<p>\u0412 \u043d\u043e\u0432\u043e\u043c \u0444\u0430\u0439\u043b\u0435 <strong>github_graphql_client\/transport\/aiohttp.py<\/strong> \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <strong>AIOHTTPTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/aiohttp.py` file from typing import Any, Optional  import aiohttp  from github_graphql_client.transport.base import BaseAsyncTransport  class AIOHTTPTransport(BaseAsyncTransport):     \"\"\"The transport based on aiohttp library.\"\"\"      DEFAULT_TIMEOUT = 1     session: Optional[aiohttp.ClientSession]      def __init__(self, endpoint: str, token: str, **kwargs: Any) -> None:         self.endpoint = endpoint         self.token = token         self.auth_header = {\"Authorization\": f\"Bearer {self.token}\"}         self.timeout = kwargs.get(\"timeout\", AIOHTTPTransport.DEFAULT_TIMEOUT)          self.session = None      async def connect(self) -> None:         \"\"\"Coroutine which will create an aiohttp ClientSession() as self.session.\"\"\"         if self.session is None:             self.session = aiohttp.ClientSession(                 timeout=aiohttp.ClientTimeout(total=self.timeout),                 headers=self.auth_header,             )         else:             raise Exception(f\"AIOHTTPTransport is already connected\")      async def close(self) -> None:         \"\"\"Coroutine which will close the aiohttp session.\"\"\"         if self.session is not None:             await self.session.close()             self.session = None      async def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query with aiohttp.\"\"\"         if self.session is None:             raise Exception(f\"AIOHTTPTransport session not connected\")          async with self.session.post(             self.endpoint,             json={\"query\": query, \"variables\": variables},         ) as response:             data = await response.json()          return data.get(\"data\")<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 <strong>RequestsTransport<\/strong><\/p>\n<ul>\n<li>\n<p><strong>connect<\/strong> \u2014 \u044d\u0442\u043e \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u0435\u0441\u0441\u0438\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <strong>aiohttp.ClientSession<\/strong>;<\/p>\n<\/li>\n<li>\n<p><strong>close<\/strong> \u2014 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442 <strong>aiohttp.ClientSession<\/strong>;<\/p>\n<\/li>\n<li>\n<p><strong>execute<\/strong> \u2014 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0439 <strong>aiohttp.ClientSession<\/strong> \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0434\u043b\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>aiohttp.ClientTimeout<\/strong>;<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 <strong>AIOHTTPTransport<\/strong> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/docs.python.org\/3\/library\/asyncio.html\" rel=\"noopener noreferrer nofollow\"><strong>asyncio<\/strong><\/a><\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file import asyncio ...  async def run_aiohttp_transport():     transport = AIOHTTPTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )     await transport.connect()      data = await transport.execute(         query=get_repository_issues_query(\"pydantic\", \"FastUI\"),         variables={},     )     print(data)      await transport.close()  def amain():     asyncio.run(run_aiohttp_transport())  if __name__ == \"__main__\":     amain()<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430\u043c \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}}<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 <strong>BaseAsyncTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/client\/async_client.py` file` from typing import Any  from github_graphql_client.transport.base import BaseAsyncTransport  class AsyncGraphQLClient:     \"\"\"Async GraphQL client based on `BaseAsyncTransport` transport.\"\"\"      transport: BaseAsyncTransport      def __init__(self, transport: BaseAsyncTransport) -> None:         self.transport = transport      async def __aenter__(self):         await self.connect_async()         return self      async def __aexit__(self, *args):         await self.close_async()      async def connect_async(self) -> None:         \"\"\"Connect to `self.transport`.\"\"\"         await self.transport.connect()      async def close_async(self) -> None:         \"\"\"Close `self.transport` connection.\"\"\"         await self.transport.close()      async def execute_async(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         return await self.transport.execute(query, variables, **kwargs)<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0435 \u043f\u043e\u0447\u0442\u0438 \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0432 <strong>SyncGraphQLClient<\/strong>, \u043d\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e. \u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  async def amain():     transport = AIOHTTPTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )      async with AsyncGraphQLClient(transport=transport) as client:         data = await client.execute_async(             query=get_repository_issues_query(\"pydantic\", \"FastUI\", last=2),             variables={},         )         print(data)  def main():     asyncio.run(amain())  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}}<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u043e\u0432\u043e\u0433\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  def check_execute(fn):     async def wrapper(*args, **kwargs):         tic = time.perf_counter()         result = await fn(*args, **kwargs)         toc = time.perf_counter()         print(f\"Duration for {fn.__name__} is {toc - tic:0.4f} seconds\")         return result      return wrapper  @check_execute async def execute(client, query: str, variables: dict[str, Any]) -> None:     data = await client.execute_async(query, variables)     print(data)  @check_execute async def amain():     transport = AIOHTTPTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )      async with AsyncGraphQLClient(transport=transport) as client:         async with asyncio.TaskGroup() as tg:             tg.create_task(                 execute(                     client,                     query=get_repository_issues_query(                         \"pydantic\", \"FastUI\",                     ),                     variables={},                 )             )              tg.create_task(                 execute(                     client,                     query=get_repository_issues_query(                         \"pydantic\", \"pydantic\",                     ),                     variables={},                 )             )                          tg.create_task(                 execute(                     client,                     query=get_repository_issues_query(                         \"pydantic\", \"pydantic-core\",                     ),                     variables={},                 )             )  def main():     asyncio.run(amain())  ...<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 <strong>AsyncGraphQLClient<\/strong> \u043c\u044b \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0434\u0430\u0447 <strong>execute<\/strong>. \u042d\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <strong>asyncio.TaskGroup<\/strong>. \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <strong>check_execute<\/strong> \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}} Duration for execute is 0.5999 seconds {'repository': {'issues': {'edges': [{'node': {'title': '2.14.4 release upload failed', 'url': 'https:\/\/github.com\/pydantic\/pydantic-core\/issues\/1082'}}, {'node': {'title': \"(?) `ValidationError` can't be instantiated\", 'url': 'https:\/\/github.com\/pydantic\/pydantic-core\/issues\/1115'}}]}}} Duration for execute is 0.5987 seconds {'repository': {'issues': {'edges': [{'node': {'title': \"__init__.cpython-311-darwin.so  is an incompatible architecture (have 'x86_64', need 'arm64') in M1 mac mini\", 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8396'}}, {'node': {'title': 'Override class used in annotations', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8408'}}]}}} Duration for execute is 0.5991 seconds Duration for amain is 0.6005 seconds<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438, \u0447\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <strong>amain<\/strong> \u043f\u043e\u0447\u0442\u0438 \u0440\u0430\u0432\u043d\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BF%D0%BE%D0%BB%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" id=\"\u043f\u043e\u043b\u043d\u044b\u0439_\u043a\u043b\u0438\u0435\u043d\u0442\"><\/a><\/p>\n<p>\u041f\u043e\u0440\u0430 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c <strong>SyncGraphQLClient<\/strong> \u0438 <strong>AsyncGraphQLClient<\/strong> \u0432 \u043e\u0434\u0438\u043d <strong>GraphQLClient<\/strong>, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b <strong>github_graphql_client\/client\/client.py<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/client\/client.py` file import asyncio from typing import Any, Union  from github_graphql_client.transport.base import (     BaseAsyncTransport,     BaseTransport, )  from .async_client import AsyncGraphQLClient from .sync_client import SyncGraphQLClient  class GraphQLClient(SyncGraphQLClient, AsyncGraphQLClient):     \"\"\"GraphQL client.\"\"\"      transport: Union[BaseTransport, BaseAsyncTransport]      def __init__(         self, transport: Union[BaseTransport, BaseAsyncTransport]     ) -> None:         super().__init__(transport)      async def _execute_async(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         async with self as client:             data = await client.execute_async(                 query,                 variables,                 **kwargs,             )         return data      async def _execute_batch_async(         self,         queries: list[str],         variables: list[dict[str, Any]],         **kwargs: Any,     ) -> list[dict[str, Any]]:         tasks = []          async with self as client:             for i in range(len(queries)):                 query = queries[i]                 vars = variables[i]                  tasks.append(client.execute_async(query, vars, **kwargs))              result_data = await asyncio.gather(*tasks)          return list(result_data)      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query.\"\"\"          if isinstance(self.transport, BaseAsyncTransport):             return asyncio.run(self._execute_async(query, variables, **kwargs))         else:             with self as client:                 return client.execute_sync(query, variables, **kwargs)      def execute_batch(         self,         queries: list[str],         variables: list[dict[str, Any]],         **kwargs: Any,     ) -> list[dict[str, Any]]:         \"\"\"Execute a batch of GraphQL queries.\"\"\"          if isinstance(self.transport, BaseAsyncTransport):             return asyncio.run(                 self._execute_batch_async(queries, variables, **kwargs)             )         else:             results = []             with self as client:                 for i in range(len(queries)):                     query = queries[i]                     vars = variables[i]                      res = client.execute_sync(query, vars, **kwargs)                      results.append(res)              return results<\/code><\/pre>\n<p>\u0414\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f: <strong>execute<\/strong> \u0438 <strong>execute_batch<\/strong>. \u0412\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442 \u043a\u0430\u043a\u043e\u0439 \u0442\u0438\u043f \u0443 <code>self.transport<\/code>, \u0430 \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u043a\u043e\u0434 \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0448\u043b\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432. \u041f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043c\u043e\u0436\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u0434\u0430<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  def main():     transport_r = RequestsTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )     transport_a = AIOHTTPTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT,         token=GITHUB_TOKEN,     )      client_r = GraphQLClient(transport=transport_r)     client_a = GraphQLClient(transport=transport_a)      q_1 = get_repository_issues_query(         \"pydantic\",         \"FastUI\",     )     q_2 = get_repository_issues_query(         \"pydantic\",         \"pydantic\",     )     q_3 = get_repository_issues_query(         \"pydantic\",         \"pydantic-core\",     )      data_r1 = client_r.execute(q_1, {})     data_a1 = client_a.execute(q_1, {})      data_r3 = client_r.execute_batch([q_1, q_2, q_3], [{}, {}, {}])     data_a3 = client_a.execute_batch([q_1, q_2, q_3], [{}, {}, {}])  ...<\/code><\/pre>\n<p>\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e, \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0441\u0432\u043e\u044e \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/gql.readthedocs.io\/en\/stable\/index.html\" rel=\"noopener noreferrer nofollow\">gql<\/a>. <strong>gql<\/strong> \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0430 \u043d\u0430 \u0442\u043e\u0439 \u0436\u0435 \u0438\u0434\u0435\u0435, \u043d\u043e \u0438\u043c\u0435\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u0432\u044f\u0437\u043a\u0438<\/p>\n<ul>\n<li>\n<p>\u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 backend: <strong>requests<\/strong>, <strong>async<\/strong>, <strong>websockets<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u043e \u043e\u043f\u0443\u0441\u043a\u0430\u043b\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430;<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u0440\u043e\u0431\u0443\u0435\u043c gql<\/h4>\n<p>\u041c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u0432\u0441\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e gql, \u043d\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043e\u0434\u0438\u043d \u043f\u0440\u0438\u043c\u0435\u0440. \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043d\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e gql. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043f\u0430\u043a\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c<\/p>\n<pre><code class=\"bash\">$ poetry add \"gql[all]\"<\/code><\/pre>\n<p>\u0418 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  from gql import Client, gql from gql.transport.aiohttp import AIOHTTPTransport  ...  def check_execute(fn):     def wrapper(*args, **kwargs):         tic = time.perf_counter()         result = fn(*args, **kwargs)         toc = time.perf_counter()         print(f\"Duration for {fn.__name__} is {toc - tic:0.4f} seconds\")         return result      return wrapper  @check_execute def main():     transport = AIOHTTPTransport(         url=GITHUB_GRAPHQL_ENDPOINT,         headers={\"Authorization\": f\"Bearer {GITHUB_TOKEN}\"},     )     client = Client(transport=transport, fetch_schema_from_transport=True)      # Provide a GraphQL query     query = gql(get_repository_issues_query(\"pydantic\", \"FastUI\", last=2))      result = client.execute(query)     print(result)  ...<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}} Duration for main is 8.5059 seconds<\/code><\/pre>\n<p>\u041e\u0433\u043e, 8 \u0441\u0435\u043a\u0443\u043d\u0434! \u041d\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u0442\u043e\u0442 \u0436\u0435 \u0441\u0430\u043c\u044b\u0439. \u0417\u043d\u0430\u0447\u0438\u0442 \u0432\u0440\u0435\u043c\u044f \u0443\u0445\u043e\u0434\u0438\u0442 \u043d\u0435 \u043d\u0430 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0430 \u043d\u0430 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b \u0441\u0430\u043c\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u042d\u0442\u0438 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c <code>fetch_schema_from_transport<\/code>.<\/p>\n<p>\u041f\u0430\u043a\u0435\u0442 gql \u0431\u0430\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 <a href=\"https:\/\/github.com\/graphql-python\/graphql-core\" rel=\"noopener noreferrer nofollow\">graphql-core<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f python \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <a href=\"https:\/\/github.com\/graphql\/graphql-js\" rel=\"noopener noreferrer nofollow\">GraphQL.js<\/a>. \u0418 \u0435\u0441\u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>fetch_schema_from_transport<\/code> \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>True<\/strong>, \u0442\u043e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a\u043b\u0438\u0435\u043d\u0442 \u0444\u0435\u0442\u0447\u0438\u0442 \u0441\u0445\u0435\u043c\u0443 \u0438 \u0431\u0438\u043b\u0434\u0438\u0442 \u0435\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442 <strong>graphql_core.GraphQLSchema<\/strong>.<\/p>\n<p>\u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0441\u0445\u0435\u043c\u0430 github GraphQL API \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 61602 \u0441\u0442\u0440\u043e\u043a\u0438. \u0422.\u0435. \u0432\u0441\u0435 8 \u0441\u0435\u043a\u0443\u043d\u0434 \u0443\u0445\u043e\u0434\u044f\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u0430\u043d\u0430\u043b\u0438\u0437 \u0441\u0445\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u0430\u044f, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0430\u044f. \u0415\u0441\u043b\u0438 <code>fetch_schema_from_transport=False<\/code>, \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c \u0441 \u043d\u0430\u0448\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c.<\/p>\n<h3>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438<\/h3>\n<p><a class=\"anchor\" name=\"%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%B0%D0%BC%D0%B8\" id=\"\u0440\u0430\u0431\u043e\u0442\u0430_\u0441_\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438\"><\/a><\/p>\n<p>\u0422\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0435\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0444\u0435\u0442\u0447\u0438\u043d\u0433\u0430 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u0445\u0435\u043c\u044b. \u0424\u0435\u0442\u0447\u0438\u043d\u0433 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0442\u043e\u0433\u043e,  \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u0415\u0441\u043b\u0438 \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c, \u0442\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043d\u0430 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<h4>\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/h4>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c <strong>pytest<\/strong><\/p>\n<pre><code class=\"bash\">$ poetry add pytest --group test<\/code><\/pre>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u043c GraphQL \u0441\u0445\u0435\u043c\u0443 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>tests\/data\/schema.docs.graphql<\/strong>. \u0422\u0435\u0441\u0442 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <strong>repository<\/strong> \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c<\/p>\n<pre><code class=\"python\"># `tests\/test_queries_validation.py` file from pathlib import Path  from graphql import Source, parse, build_schema, validate, GraphQLSchema  from github_graphql_client.queries.repository import get_repository_issues_query  SCHEMA_FILENAME = Path(__file__).parent \/ Path(\"data\/schema.docs.graphql\")  def get_schema() -> GraphQLSchema:     with SCHEMA_FILENAME.open(\"r\", encoding=\"utf8\") as f:         schema_str = f.read()      return build_schema(schema_str)  schema = get_schema()  def test_repository_issues_query():     query = get_repository_issues_query(\"pydantic\", \"FastUI\")     document = parse(Source(query))      validation_errors = validate(schema, document)     if validation_errors:         raise validation_errors[0]<\/code><\/pre>\n<p>\u0427\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442<\/p>\n<ul>\n<li>\n<p>\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <strong>graphql-core<\/strong> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 <code>schema: graphql.GraphQLSchema<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0445\u0435\u043c\u0443 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0441\u0442\u0440\u043e\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0430\u0440\u0441\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <strong>graphql.parse<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 <code>graphql.validate(schema, document)<\/code> \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430;<\/p>\n<\/li>\n<\/ul>\n<blockquote>\n<p>\u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c gql, \u0442\u043e <strong>gql.Client<\/strong> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438  <strong>gql.Client.validate<\/strong>.<\/p>\n<\/blockquote>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u044f issues \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0442\u0435\u0441\u0442<\/p>\n<pre><code class=\"python\"># \u0424\u0430\u0439\u043b `github_graphql_client\/queries\/repository.py` repository_issues_query = \"\"\" ...   repository(owner:\"pydantic\", name:\"FastUI\") {     issuess(last:2, states:CLOSED) { ... \"\"\"<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442 \u0441\u043a\u0430\u0436\u0435\u0442 \u043d\u0430\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435<\/p>\n<pre><code class=\"bash\">document = DocumentNode at 0:183      def validate(self, document: DocumentNode):         \"\"\":meta private:\"\"\"         assert (             self.schema         ), \"Cannot validate the document locally, you need to pass a schema.\"              validation_errors = validate(self.schema, document)         if validation_errors: >           raise validation_errors[0] E           graphql.error.graphql_error.GraphQLError: Cannot query field 'issuess' on type 'Repository'. Did you mean 'issues' or 'issue'? E            E           GraphQL request:4:5 E           3 |   repository(owner:\"pydantic\", name:\"FastUI\") { E           4 |     issuess(last:2, states:CLOSED) { E             |     ^ E           5 |       edges {<\/code><\/pre>\n<h4>GraphQL variables<\/h4>\n<p>\u041c\u044b \u0435\u0449\u0435 \u043d\u0438 \u0440\u0430\u0437\u0443 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 GraphQL variables. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 <strong>marketplaceCategories<\/strong> \u043a github GraphQL API<\/p>\n<pre><code class=\"javascript\">\"\"\" Get alphabetically sorted list of Marketplace categories \"\"\" marketplaceCategories(   \"\"\"   Exclude categories with no listings.   \"\"\"   excludeEmpty: Boolean    \"\"\"   Returns top level categories only, excluding any subcategories.   \"\"\"   excludeSubcategories: Boolean    \"\"\"   Return only the specified categories.   \"\"\"   includeCategories: [String!] ): [MarketplaceCategory!]!<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 <strong>github_graphql_client\/queries\/<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/marketplaceCategories.py` file def get_marketplace_categories(     exclude_empty: bool,     exclude_subcategories: bool,     include_categories: list[str], ) -> str:     return \"\"\"query {   marketplaceCategories(excludeEmpty: %s, excludeSubcategories: %s, includeCategories: %s) {     id     description   }  }\"\"\" % (         str(exclude_empty).lower(),         str(exclude_subcategories).lower(),         str(include_categories).replace(\"'\", '\"'),     )<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u0440\u043e\u043a, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e<\/p>\n<ul>\n<li>\n<p>\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0443\u043b\u0435\u0432\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>True\/False<\/strong> \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 <strong>true\/false<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u0442\u0440\u043e\u043a <strong>[&#8216;1&#8217;, &#8216;2&#8217;, &#8216;3&#8217;]<\/strong> \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 <strong>&#8216;[&#171;1&#187;, &#171;2&#187;, &#171;3&#187;]&#8217;<\/strong>;<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u044b\u0432\u0430\u044e\u0442 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u043a\u0435\u0439\u0441\u044b, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043c\u0430\u0441\u0441\u0438\u0432 <strong>enum<\/strong> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u0422.\u0435. \u043c\u0430\u0441\u0441\u0438\u0432 <strong>[&#171;A&#187;, &#171;B&#187;]<\/strong> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 <strong>&#8216;[A, B]&#8217;<\/strong>. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e\u0433\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043d\u0443\u0436\u043d\u044b GraphQL variables<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/marketplaceCategories.py` file from typing import Any  def get_marketplace_categories(     exclude_empty: bool,     exclude_subcategories: bool,     include_categories: list[str], ) -> tuple[str, dict[str, Any]]:     variables = {         \"excludeEmpty\": exclude_empty,         \"excludeSubcategories\": exclude_subcategories,         \"includeCategories\": include_categories,     }      query = \"\"\"query(   $excludeEmpty: Boolean   $excludeSubcategories: Boolean   $includeCategories: [String!] ) {   marketplaceCategories(     excludeEmpty: $excludeEmpty,     excludeSubcategories: $excludeSubcategories,     includeCategories: $includeCategories   ) {     id     description   }  }\"\"\"      return query, variables<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <strong>get_marketplace_categories<\/strong> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0438 \u0441\u043b\u043e\u0432\u0430\u0440\u044c variables. \u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0435\u0441\u0442<\/p>\n<pre><code class=\"python\"># `tests\/test_queries_validation.py` file ...  from github_graphql_client.queries.marketplaceCategories import \\     get_marketplace_categories  ...  def test_get_marketplace_categories():     query, _ = get_marketplace_categories(True, False, [\"1\", \"2\", \"3\"])     document = parse(Source(query))      validation_errors = validate(schema, document)     if validation_errors:         raise validation_errors[0]<\/code><\/pre>\n<h4>\u041f\u0430\u043a\u0435\u0442 graphql-query<\/h4>\n<p>\u041a\u0430\u043a\u0438\u0435 \u0435\u0449\u0435 \u043c\u043e\u0433\u0443\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043f\u0440\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438<\/p>\n<ul>\n<li>\n<p>\u0448\u0430\u0440\u0438\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u043a\u0443\u0441\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u0438 \u0438\u043d\u043b\u0430\u0439\u043d-\u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0433\u043e;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u043b\u0438\u0430\u0441\u044b \u0434\u043b\u044f \u0438\u043c\u0435\u043d\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430;<\/p>\n<\/li>\n<\/ul>\n<p>\u0427\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u0442\u0435\u043c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0432\u0441\u0435\u043c \u044d\u0442\u0438\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c. \u041f\u043e \u044d\u0442\u0438\u043c \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c \u0431\u044b\u043b\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <strong>graphql-query<\/strong>. \u041e\u043d\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0445 \u0441\u0442\u0440\u043e\u043a \u0441 GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f python \u043a\u043b\u0430\u0441\u0441\u044b. \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0430 \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 <a href=\"https:\/\/jinja.palletsprojects.com\/en\/2.10.x\/\" rel=\"noopener noreferrer nofollow\">jinja2<\/a>.<\/p>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e graphql-query \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0442\u0443\u0442: <a href=\"https:\/\/denisart.github.io\/graphql-query\/\" rel=\"noopener noreferrer nofollow\">https:\/\/denisart.github.io\/graphql-query\/<\/a>. \u0422\u0430\u043a \u0436\u0435 \u0435\u0441\u0442\u044c <a href=\"https:\/\/habr.com\/ru\/articles\/707374\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u044f<\/a> \u043d\u0430 habr \u0441 \u043f\u0435\u0440\u0435\u0441\u043a\u0430\u0437\u043e\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435.<\/p>\n<p>\u0414\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448\u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f graphql-query<\/p>\n<pre><code class=\"bash\">$ poetry add graphql-query<\/code><\/pre>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d \u0437\u0430\u043f\u0440\u043e\u0441<\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 <strong>graphql_query.Operation<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0435\u0441\u0442\u044c \u043d\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441;<\/p>\n<\/li>\n<li>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0442\u0438\u043f \u0434\u043b\u044f <code>graphql_query.Operation: query, mutation, subscription<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043c\u044f <strong>graphql_query.Operation<\/strong>. \u042d\u0442\u043e \u0448\u0430\u0433 \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439, \u043d\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0445\u043e\u0440\u043e\u0448\u0435\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u043e\u0439. \u0421\u0435\u0440\u0432\u0435\u0440 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u043e\u0442 \u0432\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 <strong>graphql_query.Operation<\/strong> \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 <strong>variables<\/strong> \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <strong>graphql_query.Variable<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u0421\u0430\u043c\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <strong>graphql_query.Query<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u0423 <strong>graphql_query.Query<\/strong> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043c\u044f, \u0441\u043f\u0438\u0441\u043e\u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u0430\u043a \u043c\u0430\u0441\u0441\u0438\u0432 <strong>graphql_query.Argument<\/strong> \u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0445 \u043f\u043e\u043b\u0435\u0439.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u043b\u044f, \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435, \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438. \u0410 \u0437\u043d\u0430\u0447\u0438\u0442 \u0438\u0445 \u043b\u0435\u0433\u043a\u043e \u0448\u0430\u0440\u0438\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441, \u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043c\u043e\u0436\u0435\u0442 \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u043e\u0434\u0430 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 marketplaceCategories \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 graphql-query<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/marketplaceCategories.py` file from typing import Any  from graphql_query import Operation, Argument, Variable, Field, Query  var_exclude_empty = Variable(name=\"excludeEmpty\", type=\"Boolean\") var_exclude_subcategories = Variable(     name=\"excludeSubcategories\", type=\"Boolean\" ) var_include_categories = Variable(name=\"includeCategories\", type=\"[String!]\")  def get_marketplace_categories(     exclude_empty: bool,     exclude_subcategories: bool,     include_categories: list[str], ) -> tuple[str, dict[str, Any]]:     marketplace_categories_query = Query(         name=\"marketplaceCategories\",         arguments=[             Argument(name=\"excludeEmpty\", value=var_exclude_empty),             Argument(                 name=\"excludeSubcategories\",                 value=var_exclude_subcategories,             ),             Argument(name=\"includeCategories\", value=var_include_categories),         ],         fields=[\"id\", \"description\"],     )      operation = Operation(         type=\"query\",         name=\"getMarketplaceCategories\",         variables=[             var_exclude_empty,             var_exclude_subcategories,             var_include_categories,         ],         queries=[marketplace_categories_query],     )      variables = {         var_exclude_empty.name: exclude_empty,         var_exclude_subcategories.name: exclude_subcategories,         var_include_categories.name: include_categories,     }      return operation.render(), variables<\/code><\/pre>\n<p>\u0412\u044b\u0437\u043e\u0432 <code>operation.render()<\/code> \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<pre><code>query getMarketplaceCategories(   $excludeEmpty: Boolean   $excludeSubcategories: Boolean   $includeCategories: [String!] ) {   marketplaceCategories(     excludeEmpty: $excludeEmpty     excludeSubcategories: $excludeSubcategories     includeCategories: $includeCategories   ) {     id     description   } }<\/code><\/pre>\n<p>\u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0442\u0435\u0441\u0442 <strong>test_get_marketplace_categories<\/strong> \u0432\u0441\u0435 \u0442\u0430\u043a \u0436\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f (\u0432\u043a\u043b\u044e\u0447\u0430\u044f <strong>graphql_query.Fragment<\/strong> \u0438 <strong>graphql_query.InlineFragment<\/strong>) \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.<\/p>\n<h3>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85\" id=\"\u043c\u043e\u0434\u0435\u043b\u044c_\u0434\u0430\u043d\u043d\u044b\u0445\"><\/a><\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u043c 20 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0445 issues \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 pydantic. \u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u043d\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441 <strong>repository<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/repository.py` file ... var_issue_state = Variable(name=\"IssueState\", type=\"[IssueState!]\")  ...  def get_repository_issues_query(     owner: str,     name: str,     last: int,     state: str, ) -> tuple[str, dict[str, Any]]:     ...          operation = Operation(         ...         variables=[var_owner, var_name, var_last, var_issue_state],         ...     )      return operation.render(), {         var_owner.name: owner,         var_name.name: name,         var_last.name: last,         var_issue_state.name: state,     }<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <code>get_repository_issues_query(\"pydantic\", \"pydantic\", 20, \"OPEN\")<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': \"Can't use config keyword argument with TypeAdapter.__init__ on stdlib dataclass\", 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8326'}}, {'node': {'title': 'Constructor for model with `Json[list[int]]` field should accept `list[int]`, like mypy already expects', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8336'}}, {'node': {'title': 'JSON serialization issue with ipaddress classes as alternative', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8343'}}, {'node': {'title': 'Indeterminate: LookupError when generating custom schema output', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8359'}}, {'node': {'title': 'Implement more clear warning\/error when using constraints on compound types', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8362'}}, {'node': {'title': 'Magic validation methods needs to be documented for version 2', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8374'}}, {'node': {'title': 'More consistent and intuitive `alias` behavior for validation and serialization', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8379'}}, {'node': {'title': 'Common mistakes docs section', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8380'}}, {'node': {'title': 'Deprecate `update_json_schema` function', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8381'}}, {'node': {'title': 'coerce_numbers_to_str needs a per-field variant', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8383'}}, {'node': {'title': 'Bus error when using custom type impl with SQLAlchemy ', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8385'}}, {'node': {'title': 'Allow optional properties to be truely optional', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8394'}}, {'node': {'title': '`@property` changes behavior when using dataclass-ish classes', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8401'}}, {'node': {'title': \"Regex's pattern is not serialized when creating a model's JSON schema\", 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8405'}}, {'node': {'title': 'cached_property is not ignored when a model is copied and updated', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8406'}}, {'node': {'title': 'BaseModel causes information loss with Generic classes', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8410'}}, {'node': {'title': \"JSON Schema is wrong when `mode='serialization'` and fields have a default\", 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8413'}}, {'node': {'title': 'Bytes and Bits Conversion Type', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8415'}}, {'node': {'title': 'Debian\/ubuntu packages for v2', 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8416'}}, {'node': {'title': \"Union discriminator tag fails if '_' is in the literal value\", 'url': 'https:\/\/github.com\/pydantic\/pydantic\/issues\/8417'}}]}}} <\/code><\/pre>\n<p>\u041f\u0440\u043e\u0431\u0435\u0436\u0438\u043c\u0441\u044f \u043f\u043e issues \u0438 \u043f\u0440\u0438\u043d\u0442\u0430\u043d\u0435\u043c \u0432\u0441\u0435 title. \u041a\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a\u0438\u043c<\/p>\n<pre><code class=\"python\">for issue in result[\"repository\"][\"issues\"][\"edges\"]:     print(issue[\"node\"][\"title\"])<\/code><\/pre>\n<p>\u041d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e. \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438 \u043e\u0442 IDE \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0442\u0438\u043f\u0430<\/p>\n<pre><code class=\"python\">for issue in repository.issues.edges:     print(issue.node.title)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u044b, \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0438\u0435 \u0442\u0438\u043f\u044b \u0438\u0437 GraphQL \u0441\u0445\u0435\u043c\u044b. \u0415\u0441\u0442\u044c \u0434\u0432\u0430 \u0432\u043e\u043f\u0440\u043e\u0441\u0430<\/p>\n<ul>\n<li>\n<p>\u043a\u0430\u043a\u043e\u0439 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c?<\/p>\n<\/li>\n<li>\n<p>\u043a\u0430\u043a, \u0438\u043c\u0435\u044f \u0441\u0445\u0435\u043c\u0443, \u0433\u0435\u043d\u0435\u0440\u0438\u0442\u044c \u044d\u0442\u0438 \u043a\u043b\u0430\u0441\u0441\u044b?<\/p>\n<\/li>\n<\/ul>\n<h4>\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441<\/h4>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u2014 <strong>dataclasses.dataclass<\/strong>. \u042d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u0440\u0438 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432. \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 <a href=\"https:\/\/github.com\/lidatong\/dataclasses-json\" rel=\"noopener noreferrer nofollow\">dataclasses-json<\/a>. \u042d\u0442\u043e \u0434\u0430\u0442\u0430-\u043a\u043b\u0430\u0441\u0441\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 <strong>from_json<\/strong>, <strong>from_dict<\/strong>. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043f\u0440\u0438 \u0442\u0430\u043a\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435<\/p>\n<ul>\n<li>\n<p>\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u044b \u0441\u0432\u043e\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b\/\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b \u0434\u043b\u044f \u0442\u0438\u043f\u0430 <strong>union<\/strong> (\u0432 \u043d\u043e\u0432\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0430);<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f;<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434, \u0440\u0435\u0448\u0430\u044e\u0449\u0438\u0439 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u2014 <a href=\"https:\/\/pydantic.dev\/\" rel=\"noopener noreferrer nofollow\"><strong>pydantic<\/strong><\/a>. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430\u0448 \u043f\u0440\u0438\u043c\u0435\u0440 (\u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c pydantic \u0432 \u043f\u0440\u043e\u0435\u043a\u0442)<\/p>\n<pre><code class=\"python\">from pydantic import BaseModel  class Issue(BaseModel):     title: str     url: str  class IssueEdge(BaseModel):     node: Issue  class IssueConnection(BaseModel):     edges: list[IssueEdge]  class Repository(BaseModel):     issues: IssueConnection  repository = Repository.model_validate(result[\"repository\"])  for issue in repository.issues.edges:     print(issue.node.title)<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<pre><code>Can't use config keyword argument with TypeAdapter.__init__ on stdlib dataclass Constructor for model with `Json[list[int]]` field should accept `list[int]`, like mypy already expects JSON serialization issue with ipaddress classes as alternative Indeterminate: LookupError when generating custom schema output Implement more clear warning\/error when using constraints on compound types Magic validation methods needs to be documented for version 2 More consistent and intuitive `alias` behavior for validation and serialization Common mistakes docs section Deprecate `update_json_schema` function coerce_numbers_to_str needs a per-field variant Bus error when using custom type impl with SQLAlchemy  Allow optional properties to be truely optional `@property` changes behavior when using dataclass-ish classes Regex's pattern is not serialized when creating a model's JSON schema cached_property is not ignored when a model is copied and updated BaseModel causes information loss with Generic classes JSON Schema is wrong when `mode='serialization'` and fields have a default Bytes and Bits Conversion Type Debian\/ubuntu packages for v2 Union discriminator tag fails if '_' is in the literal value<\/code><\/pre>\n<h4>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u0441\u0435 \u043a\u043b\u0430\u0441\u0441\u044b, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432 \u0441\u0445\u0435\u043c\u0435 github GraphQL API, \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u0436\u0435 \u0441\u0445\u0435\u043c\u0430 \u0447\u0430\u0441\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f (\u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0430 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0445 \u044d\u0442\u0430\u043f\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438) \u2014 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0440\u0443\u0442\u0438\u043d\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u043e \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u0423\u0434\u043e\u0431\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0433\u0435\u043d\u0435\u0440\u0438\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0445\u0435\u043c\u044b.<\/p>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u0440\u0438\u0432\u0435\u043b\u0430 \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/denisart.github.io\/graphql2python\/\" rel=\"noopener noreferrer nofollow\">graphql2python<\/a> \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 pydantic \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 GraphQL \u0441\u043c\u0435\u0445\u0435. \u041d\u0435\u0434\u0430\u0432\u043d\u043e \u0432 \u0431\u0435\u0442\u0430 \u0440\u0435\u0436\u0438\u043c\u0435 \u044d\u0442\u0438 \u043d\u0430\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u044b \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/koxudaxi\/datamodel-code-generator\" rel=\"noopener noreferrer nofollow\">datamodel-code-generator<\/a> \u2014 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e Open API \u0441\u0445\u0435\u043c\u0435.<\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c datamodel-code-generator \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0441\u0445\u0435\u043c\u044b<\/p>\n<pre><code class=\"bash\">$ poetry add \"datamodel-code-generator[graphql]\" $ datamodel-codegen --input tests\/data\/schema.docs.graphql --input-file-type graphql --output github_graphql_client\/model.py<\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441\u044b \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0432\u044b\u0448\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 datamodel-code-generator \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/model.py` file ...  class Issue(     Assignable,     Closable,     Comment,     Deletable,     Labelable,     Lockable,     Node,     ProjectV2Owner,     Reactable,     RepositoryNode,     Subscribable,     SubscribableThread,     UniformResourceLocatable,     Updatable,     UpdatableComment, ):     \"\"\"     An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.     \"\"\"      ...  ...  class IssueEdge(BaseModel):     \"\"\"     An edge in a connection.     \"\"\"      cursor: String     node: Optional[Issue] = None     typename__: Optional[Literal['IssueEdge']] = Field(         'IssueEdge', alias='__typename'     )  ...  class IssueConnection(BaseModel):     \"\"\"     The connection type for Issue.     \"\"\"      edges: Optional[List[Optional[IssueEdge]]] = Field(default_factory=list)     nodes: Optional[List[Optional[Issue]]] = Field(default_factory=list)     pageInfo: PageInfo     totalCount: Int     typename__: Optional[Literal['IssueConnection']] = Field(         'IssueConnection', alias='__typename'     )  ...  class Repository(     Node,     PackageOwner,     ProjectOwner,     ProjectV2Recent,     RepositoryInfo,     Starrable,     Subscribable,     UniformResourceLocatable, ):     \"\"\"     A repository contains the content for a project.     \"\"\"          ...  ...<\/code><\/pre>\n<p>\u041f\u0430\u043a\u0435\u0442 datamodel-code-generator \u0438\u043c\u0435\u0435\u0442 \u0433\u0438\u0431\u043a\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u043e\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c<\/p>\n<ul>\n<li>\n<p>\u0432\u0435\u0440\u0441\u0438\u044e pydantic;<\/p>\n<\/li>\n<li>\n<p>\u0432\u0435\u0440\u0441\u0438\u044e python;<\/p>\n<\/li>\n<li>\n<p>\u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, dataclasses.dataclass);<\/p>\n<\/li>\n<li>\n<p>\u0438\u043c\u0435\u043d\u0430 \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0442\u0438\u043f\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0432\u043e\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u044b (\u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430 jinja2);<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 datamodel-code-generator \u043c\u043e\u0436\u043d\u043e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.<\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p><a class=\"anchor\" name=\"%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5\" id=\"\u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\"><\/a><\/p>\n<p>\u041c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043a\u043b\u0438\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043d\u043e\u0441\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u0443 \u043f\u0440\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0437\u0430\u0434\u0430\u0447. \u041d\u043e \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u0440\u044f\u0434 \u0442\u0435\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u0448\u043b\u0438 \u0437\u0430 \u0440\u0430\u043c\u043a\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440,<\/p>\n<ul>\n<li>\n<p>\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u043a\u043b\u043e\u043d\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438\u0437-\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0432 \u043d\u0435\u043c;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0441\u043a\u0430\u043b\u044f\u0440\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0431\u043e\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 websockets;<\/p>\n<\/li>\n<li>\n<p>\u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0441\u043e\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0442\u0435\u043d\/\u0442\u044b\u0441\u044f\u0447 \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0445 \u043c\u0443\u0442\u0430\u0446\u0438\u0439?<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u043d\u0434\u0443\u0441\u0442\u0440\u0438\u044f \u0442\u0430\u043a \u0436\u0435 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435. \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f GraphQL \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0442\u0443\u0442: <a href=\"https:\/\/graphql.org\/code\/#python-client\" rel=\"noopener noreferrer nofollow\"><em>https:\/\/graphql.org\/code\/#python-client<\/em><\/a>. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043c\u043e\u043b\u043e\u0434\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 <a href=\"https:\/\/github.com\/mirumee\/ariadne-codegen\" rel=\"noopener noreferrer nofollow\"><em>ariadne-codegen<\/em><\/a> \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e GraphQL \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u043e \u0432\u0430\u0448\u0438\u043c \u0441\u0445\u0435\u043c\u0430\u043c \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c.<\/p>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u0443\u0441\u043b\u044b\u0448\u0430\u0442\u044c \u0432\u0430\u0448 \u043e\u043f\u044b\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 GraphQL \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438 \u043d\u0430 python \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u044b \u0440\u0435\u0448\u0430\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0435 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u043e \u043d\u0435\u0439 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c.<\/p>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><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\/articles\/782252\/\"> https:\/\/habr.com\/ru\/articles\/782252\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0440\u043e\u0434\u0438\u043b\u0430\u0441\u044c \u0438\u0437 \u043e\u043f\u044b\u0442\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f <strong>GraphQL<\/strong> \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0438\u0445 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u043e\u0432 \u0420\u0424. \u041f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0439\u0441\u043e\u0432 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c\u0438 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u0430 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (<strong>MRMS<\/strong> \u0441\u0438\u0441\u0442\u0435\u043c\u0430).<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043d\u0430 \u0431\u0430\u0437\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0433\u0434\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0432\u0438\u0434\u0435 GraphQL API, \u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 API, \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 <strong>java<\/strong>. \u041a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438 \u044d\u0442\u043e\u0433\u043e API \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e <strong>web\/mobile<\/strong>, \u043d\u043e \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043d\u0430 <strong>java<\/strong>, <strong>golang<\/strong> \u0438 <strong>python<\/strong>.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043a\u0430\u043a \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0441\u0432\u043e\u0435\u0433\u043e GraphQL \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0430 python \u0441 \u043d\u0443\u043b\u044f, \u0433\u0434\u0435 \u0430\u0432\u0442\u043e\u0440 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0449\u0438\u0435 \u043d\u0430 \u044d\u0442\u043e\u043c \u043f\u0443\u0442\u0438. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u0443\u044e GraphQL \u0441\u0445\u0435\u043c\u0443 \u0430\u044d\u0440\u043e\u043f\u043e\u0440\u0442\u0430 \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u0443\u044e \u0441\u0445\u0435\u043c\u0443 <a href=\"https:\/\/docs.github.com\/en\/graphql\" rel=\"noopener noreferrer nofollow\"><strong>github GraphQL API<\/strong><\/a>.<\/p>\n<h4>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h4>\n<ol>\n<li>\n<p><a href=\"#%D0%BE_%D1%87%D0%B5%D0%BC_%D1%8D%D1%82%D0%B0_%D1%81%D1%82%D0%B0%D1%82%D1%8C%D1%8F\" rel=\"noopener noreferrer nofollow\">\u0427\u0442\u043e \u043d\u0430\u0441 \u0436\u0434\u0435\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81\" rel=\"noopener noreferrer nofollow\">\u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" rel=\"noopener noreferrer nofollow\">\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%B0%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" rel=\"noopener noreferrer nofollow\">\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BF%D0%BE%D0%BB%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82\" rel=\"noopener noreferrer nofollow\">\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0_%D1%81_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%B0%D0%BC%D0%B8\" rel=\"noopener noreferrer nofollow\">\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85\" rel=\"noopener noreferrer nofollow\">\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ol>\n<h3>\u0427\u0442\u043e \u043d\u0430\u0441 \u0436\u0434\u0435\u0442<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BE_%D1%87%D0%B5%D0%BC_%D1%8D%D1%82%D0%B0_%D1%81%D1%82%D0%B0%D1%82%D1%8C%D1%8F\" id=\"\u043e_\u0447\u0435\u043c_\u044d\u0442\u0430_\u0441\u0442\u0430\u0442\u044c\u044f\">\u0441\u0442\u0430\u0442\u044c\u044f&#187;<\/a><\/p>\n<p>\u0412\u043e\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c<\/p>\n<ul>\n<li>\n<p>\u043a\u0430\u043a \u0441\u043e\u0432\u0435\u0440\u0448\u0438\u0442\u044c GraphQL \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430?<\/p>\n<\/li>\n<li>\n<p>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443;<\/p>\n<\/li>\n<li>\n<p>\u043a\u0430\u043a \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u0432\u043e\u0435\u0439 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432?<\/p>\n<\/li>\n<li>\n<p>\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 \u0432\u0438\u0434\u0435 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432;<\/p>\n<\/li>\n<\/ul>\n<p>\u0423\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043e\u0444. \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e. \u041d\u043e \u0434\u043b\u044f \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e\u0433\u043e \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u044b GraphQL. \u0422\u0430\u043a \u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043b\u044e\u0441\u043e\u043c \u0438\u043c\u0435\u0442\u044c \u043e\u043f\u044b\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 <strong>requests, asyncio<\/strong> \u0438 <strong>aiohttp<\/strong>.<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/github.com\/denisart\/github-graphql-client\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>. \u041a\u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 <strong>python 3.11<\/strong> \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <strong>poetry<\/strong>, \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d \u0438 \u043e\u0442\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/black.readthedocs.io\/en\/stable\/\" rel=\"noopener noreferrer nofollow\">black<\/a>.<\/p>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0\" id=\"\u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430\">\u043e\u0432\u043a\u0430&#187;><\/a><\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c\u0441\u044f \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 github GraphQL API. \u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0435 python \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435. \u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/python-poetry.org\/\" rel=\"noopener noreferrer nofollow\">poetry<\/a> \u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<pre><code class=\"bash\">\u251c\u2500\u2500 github_graphql_client\/  &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u0434 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u2502   \u2514\u2500\u2500 __init__.py \u251c\u2500\u2500 tests\/                  &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0443\u0442 \u0442\u0435\u0441\u0442\u044b \u2502   \u2514\u2500\u2500 __init__.py \u251c\u2500\u2500 scripts\/                &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u251c\u2500\u2500 pyproject.toml          &lt;- \u0424\u0430\u0439\u043b \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u251c\u2500\u2500 README.md \u251c\u2500\u2500 .env                    &lt;- \u0422\u0443\u0442 \u0431\u0443\u0434\u0443\u0442 \u0432\u0441\u044f\u043a\u0438\u0435 sensitive \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u2514\u2500\u2500 .gitignore<\/code><\/pre>\n<p>\u041a\u043e\u043d\u0444\u0438\u0433 <strong>pyproject.toml<\/strong> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439<\/p>\n<pre><code># `pyproject.toml` file [tool.poetry] name = \"github_graphql_client\" version = \"0.1.0\" description = \"Github GraphQL client for Habr.\" authors = [\"FirstName SecondName &lt;email>\"] readme = \"README.md\" packages = [{include = \"github_graphql_client\"}]  [tool.poetry.dependencies] python = \"^3.11\"   [build-system] requires = [\"poetry-core\"] build-backend = \"poetry.core.masonry.api\"<\/code><\/pre>\n<p>\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, Github GraphQL<strong> endpoint<\/strong> \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"https:\/\/api.github.com\/graphql\" rel=\"noopener noreferrer nofollow\">https:\/\/api.github.com\/graphql<\/a>. \u0427\u0442\u043e\u0431\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f API \u2014 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u043e \u0432\u044b\u043f\u0443\u0441\u043a \u0442\u043e\u043a\u0435\u043d\u0430 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0442\u0443\u0442: <a href=\"https:\/\/docs.github.com\/en\/graphql\/guides\/forming-calls-with-graphql\" rel=\"noopener noreferrer nofollow\">Github &#8212; Forming calls with GraphQL<\/a>. \u0412 \u0444\u0430\u0439\u043b\u0435 <strong>.env<\/strong> \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<pre><code class=\"bash\">GITHUB_TOKEN=&lt;YOUR_TOKEN> GITHUB_GRAPHQL_ENDPOINT=https:\/\/api.github.com\/graphql<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u0430\u0439\u043b\u043e\u043c <strong>.env<\/strong> \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442 <a href=\"https:\/\/pypi.org\/project\/python-dotenv\/\" rel=\"noopener noreferrer nofollow\">python-dotenv<\/a>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043d\u0430\u0448\u0435\u0439 \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<pre><code class=\"bash\">$ poetry add  python-dotenv Creating virtualenv blah-blah-py3.11 in \/home\/blah-blah\/pypoetry\/virtualenvs Using version ^1.0.0 for python-dotenv  Updating dependencies Resolving dependencies... (0.1s)  Package operations: 1 install, 0 updates, 0 removals    \u2022 Installing python-dotenv (1.0.0)  Writing lock file<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b <strong>poetry add<\/strong> \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0444\u0430\u0439\u043b <strong>poetry.lock<\/strong>, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430\u0448\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 <strong>pyproject.toml<\/strong><\/p>\n<pre><code># `pyproject.toml` file # ...  [tool.poetry.dependencies] python = \"^3.11\" python-dotenv = \"^1.0.0\"  # ...<\/code><\/pre>\n<p>\u0418, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c <code>poetry install<\/code> \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u0441\u0435\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<h3>\u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441<\/h3>\n<p><a class=\"anchor\" name=\"%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81\" id=\"\u043f\u0435\u0440\u0432\u044b\u0439_\u0437\u0430\u043f\u0440\u043e\u0441\">\u0430\u043f\u0440\u043e\u0441&#187;><\/a><\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u043d\u0430\u0448 \u043a\u043b\u0438\u0435\u043d\u0442 \u2014 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443. <a href=\"https:\/\/graphql.org\/learn\/best-practices\/\" rel=\"noopener noreferrer nofollow\">\u041e\u0431\u044b\u0447\u043d\u043e<\/a> GraphQL \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 <strong>HTTP\/HTTPS<\/strong> \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 <strong>POST<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 <strong>json<\/strong> \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 <code>query: str<\/code> \u0438 <code>variables: dict[str, Any]<\/code>. \u0422\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u0432 github GraphQL API. \u0421\u0445\u0435\u043c\u0443 github GraphQL API \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/docs.github.com\/en\/graphql\/overview\/public-schema\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>.  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0437\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u043e\u0432\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<pre><code class=\"bash\">$ mkdir github_graphql_client\/transport $ touch github_graphql_client\/transport\/__init__.py github_graphql_client\/transport\/base.py<\/code><\/pre>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>github_graphql_client\/transport\/base.py<\/strong> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c <strong>BaseTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/base.py` file from typing import Any  class BaseTransport:     \"\"\"An abstract transport.\"\"\"      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query.\"\"\"         raise NotImplementedError      def connect(self) -> None:         \"\"\"Establish a session with the transport.\"\"\"         raise NotImplementedError      def close(self) -> None:         \"\"\"Close a session.\"\"\"         raise NotImplementedError<\/code><\/pre>\n<p>\u041b\u044e\u0431\u043e\u0439 \u043a\u043b\u0430\u0441\u0441 \u0442\u0438\u043f\u0430 <strong>Transport<\/strong> \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 <strong>BaseTransport<\/strong>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430<\/p>\n<ul>\n<li>\n<p><strong>connect<\/strong> \u2014 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c;<\/p>\n<\/li>\n<li>\n<p><strong>close<\/strong> \u2014 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p><strong>execute<\/strong> \u2014 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <strong>query<\/strong> \u0441 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 <strong>variables<\/strong>;<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a, \u043a\u0430\u043a GraphQL \u0437\u0430\u043f\u0440\u043e\u0441 \u0435\u0441\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u044b\u0439 POST \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u043a\u0435\u0442\u0430 <a href=\"https:\/\/pypi.org\/project\/requests\/\" rel=\"noopener noreferrer nofollow\">requests<\/a>. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c<\/p>\n<pre><code class=\"bash\">$ poetry add requests<\/code><\/pre>\n<p>\u0412 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b <strong>github_graphql_client\/transport\/requests.py<\/strong> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434<\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/requests.py` file from typing import Any, Optional  import requests as r  from github_graphql_client.transport.base import BaseTransport  class RequestsTransport(BaseTransport):     \"\"\"The transport based on requests library.\"\"\"      session: Optional[r.Session]      def __init__(self, endpoint: str, token: str, **kwargs: Any) -> None:         self.endpoint = endpoint         self.token = token         self.auth_header = {\"Authorization\": f\"Bearer {self.token}\"}          self.session = None      def connect(self) -> None:         \"\"\"Start a `requests.Session` connection.\"\"\"         if self.session is None:             self.session = r.Session()         else:             raise Exception(\"Session already started\")      def close(self) -> None:         \"\"\"Closing `requests.Session` connection.\"\"\"         if self.session is not None:             self.session.close()             self.session = None      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         \"\"\"Execute GraphQL query.\"\"\"         if self.session is None:             raise Exception(f\"RequestsTransport session not connected\")          post_args = {             \"headers\": self.auth_header,             \"json\": {\"query\": query, \"variables\": variables},         }         post_args[\"headers\"][\"Content-Type\"] = \"application\/json\"          response = self.session.request(\"POST\", self.endpoint, **post_args)          result = response.json()         return result.get(\"data\")<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<ul>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>connect<\/strong> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 <strong>requests.Session<\/strong> \u0435\u0441\u043b\u0438 \u043e\u043d \u043d\u0435 \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u0440\u0430\u043d\u0435\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>close<\/strong> \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <strong>requests.Session<\/strong>;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u043e\u0434 <strong>execute<\/strong> \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <strong>requests.Session<\/strong> \u043e\u0431\u044b\u0447\u043d\u044b\u0439 POST \u0437\u0430\u043f\u0440\u043e\u0441;<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c, \u0447\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u043c\u044b \u0443\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435.  \u041e\u0442\u043f\u0440\u0430\u0432\u0438\u043c query, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0432\u044b\u0445 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0445 <strong>issues<\/strong>  \u0438\u0437 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e github \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f. \u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0430\u0432\u0442\u043e\u0440\u0430 <strong>pydantic<\/strong> &#8212; <a href=\"https:\/\/github.com\/pydantic\/FastUI\" rel=\"noopener noreferrer nofollow\">FastUI<\/a>. \u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u0443\u0434\u0435\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435<\/p>\n<pre><code class=\"bash\">$ mkdir github_graphql_client\/queries $ touch github_graphql_client\/queries\/__init__.py github_graphql_client\/queries\/repository.py<\/code><\/pre>\n<pre><code class=\"python\"># `github_graphql_client\/queries\/repository.py` file repository_issues_query = \"\"\" query {   repository(owner:\"pydantic\", name:\"FastUI\") {     issues(last:2, states:CLOSED) {       edges {         node {           title           url         }       }     }   } } \"\"\"<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043a\u0440\u0438\u043f\u0442 <strong>scripts\/run.py<\/strong><\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file import os import time from typing import Any  from dotenv import load_dotenv  from github_graphql_client.client.requests_client import RequestsClient from github_graphql_client.queries.repository import repository_issues_query  load_dotenv()  # take environment variables from .env  GITHUB_TOKEN = os.environ.get(\"GITHUB_TOKEN\") GITHUB_GRAPHQL_ENDPOINT = os.environ.get(\"GITHUB_GRAPHQL_ENDPOINT\")  def main():     transport = RequestsTransport(         endpoint=GITHUB_GRAPHQL_ENDPOINT, token=GITHUB_TOKEN     )     transport.connect()      data = transport.execute(         query=repository_issues_query, variables={},     )     print(data)          transport.close()  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435<\/p>\n<pre><code class=\"bash\">$ python3 scripts\/run.py {'repository': {'issues': {'edges': [{'node': {'title': 'More PageEvent Triggers', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/104'}}, {'node': {'title': 'TypeError: Interval() takes no arguments', 'url': 'https:\/\/github.com\/pydantic\/FastUI\/issues\/105'}}]}}}<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0436\u0438\u0432\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0435\u0439\u0447\u0430\u0441 issues \u0431\u0443\u0434\u0443\u0442 \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<\/p>\n<h4>\u0422\u0430\u0439\u043c\u0430\u0443\u0442<\/h4>\n<p>\u0425\u043e\u0440\u043e\u0448\u043e, \u0435\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441. \u041f\u0440\u043e\u0446\u0438\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0434\u043d\u0443 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0443\u044e <a href=\"https:\/\/habr.com\/ru\/companies\/oleg-bunin\/articles\/433476\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u044e<\/a><\/p>\n<blockquote>\n<p>\u041d\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0443 \u2014 \u044d\u0442\u043e \u0437\u043b\u043e. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0432\u044b \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u0437\u0430\u0434\u0430\u0447\u0435, \u043a\u0430\u043a \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430.<\/p>\n<\/blockquote>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0432 <strong>RequestsTransport<\/strong><\/p>\n<pre><code class=\"python\"># `github_graphql_client\/transport\/requests.py` file ...  class RequestsTransport(BaseTransport):     \"\"\"The transport based on requests library.\"\"\"      DEFAULT_TIMEOUT: int = 1     session: Optional[r.Session]      def __init__(self, endpoint: str, token: str, **kwargs: Any) -> None:         ...          self.timeout = kwargs.get(\"timeout\", RequestsTransport.DEFAULT_TIMEOUT)      ...      def execute(         self, query: str, variables: dict[str, Any], **kwargs: Any     ) -> dict[str, Any]:         ...          post_args = {             \"headers\": self.auth_header,             \"json\": {\"query\": query, \"variables\": variables},             \"timeout\": self.timeout,         }         ...<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043d\u0430\u0448 \u043a\u043e\u0434 \u0443\u043a\u0430\u0437\u0430\u0432 \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 (\u0434\u043b\u044f github GraphQL API) \u0442\u0430\u0439\u043c\u0430\u0443\u0442<\/p>\n<pre><code class=\"python\"># `scripts\/run.py` file ...  def<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\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-363606","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/363606","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=363606"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/363606\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=363606"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=363606"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=363606"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}