{ "info": { "author": "Robert Myers", "author_email": "robert@julython.org", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6" ], "description": "# Cannula\n\n[![CircleCI](https://circleci.com/gh/rmyers/cannula.svg?style=shield)](https://circleci.com/gh/rmyers/cannula)\n[![Documentation Status](https://readthedocs.org/projects/cannula/badge/?version=latest)](https://cannula.readthedocs.io/en/latest/?badge=latest)\n\n> GraphQL for people who like Python!\n\n* [Why Cannula](#why)\n* [Installation](#install)\n* [Quick Start](#start)\n* [Examples](#examples)\n* [Documentation](https://cannula.readthedocs.io/)\n\n

Why Cannula?

\n\nWe wanted to make the world a better place, but we are programmers so we settled\non making the web fun again. Too much attention has been given to Javascript\nclient libraries. They all seem to compete on size and speed and features but\nmost of them do not solve any of the actual problems you have. So while the\ntodo application is quick and easy to follow the hard parts take a long time\nto complete.\n\nNow a days if you want a fancy single page application you need to invest a\ngood week or so planning out all the tools you will need to assemble your site.\nEvery decision is full of sorrow and doubt as you google for the latest trends\nor how to setup unit tests. Or searching for a bootstrapped version of the\nlibrary you like.\n\nUsing GraphQL you can simplify your web application stack and reduce\ndependencies to achieve the same customer experience without regret. By using\njust a few core libraries you can increase productivity and make your\napplication easier to maintain.\n\nOur Philosophy:\n1. Make your site easy to maintain.\n2. Document your code.\n3. Don't lock yourself into a framework.\n4. Be happy!\n\n

Installation

\n\nRequires Python 3.6 or greater! The only dependency is\n[graphql-core-next](https://graphql-core-next.readthedocs.io/en/latest/).\n\n```bash\npip3 install cannula\n```\n\n

Quick Start

\n\nHere is a small [hello world example](examples/hello.py):\n\n```python\nimport logging\nimport typing\nimport sys\n\nimport cannula\nfrom cannula.middleware import DebugMiddleware\n\nSCHEMA = cannula.gql(\"\"\"\n type Message {\n text: String\n }\n type Query {\n hello(who: String): Message\n }\n\"\"\")\n\nlogging.basicConfig(level=logging.DEBUG)\n\napi = cannula.API(\n __name__,\n schema=SCHEMA,\n middleware=[\n DebugMiddleware()\n ]\n)\n\n\nclass Message(typing.NamedTuple):\n text: str\n\n\n# The query resolver takes a source and info objects\n# and any arguments defined by the schema. Here we\n# only accept a single argument `who`.\n@api.resolver('Query')\nasync def hello(source, info, who):\n return Message(f\"Hello, {who}!\")\n\n# Pre-parse your query to speed up your requests.\n# Here is an example of how to pass arguments to your\n# query functions.\nSAMPLE_QUERY = cannula.gql(\"\"\"\n query HelloWorld ($who: String!) {\n hello(who: $who) {\n text\n }\n }\n\"\"\")\n\n\nwho = 'world'\nif len(sys.argv) > 1:\n who = sys.argv[1]\n\nprint(api.call_sync(SAMPLE_QUERY, variables={'who': who}))\n```\n\nNow you should see the results if you run the sample on the command line:\n\n```bash\n$ python3 examples/hello.py\nDEBUG:asyncio:Using selector: KqueueSelector\nDEBUG:cannula.schema:Adding default empty Mutation type\nDEBUG:cannula.middleware.debug:Resolving Query.hello expecting type Message\nDEBUG:cannula.middleware.debug:Field Query.hello resolved: Message(text='Hello, world!') in 0.000108 seconds\nDEBUG:cannula.middleware.debug:Resolving Message.text expecting type String\nDEBUG:cannula.middleware.debug:Field Message.text resolved: 'Hello, world!' in 0.000067 seconds\nExecutionResult(\n data={'hello': {'text': 'Hello, world!'}},\n errors=None\n)\n\n$ python3 examples/hello.py Bob\nDEBUG:asyncio:Using selector: KqueueSelector\nDEBUG:cannula.schema:Adding default empty Mutation type\nDEBUG:cannula.middleware.debug:Resolving Query.hello expecting type Message\nDEBUG:cannula.middleware.debug:Field Query.hello resolved: Message(text='Hello, Bob!') in 0.000104 seconds\nDEBUG:cannula.middleware.debug:Resolving Message.text expecting type String\nDEBUG:cannula.middleware.debug:Field Message.text resolved: 'Hello, Bob!' in 0.000101 seconds\nExecutionResult(\n data={'hello': {'text': 'Hello, Bob!'}},\n errors=None\n)\n```\n\nBut what about Django integration or flask?\n\n```python\n# pip install channels, Django\nimport cannula\nfrom channels.db import database_sync_to_async\nfrom django.contrib.auth.models import User\n\nschema = cannula.gql(\"\"\"\n type User {\n username: String # Only expose the fields you actually use\n first_name: String\n last_name: String\n made_up_field: String\n }\n extend type Query {\n getUserById(user_id: String): User\n }\n\"\"\")\n\n@api.query()\nasync def getUserById(source, info, user_id):\n return await get_user(user_id)\n\n@database_sync_to_async\ndef get_user(user_id):\n return User.objects.get(pk=user_id)\n\n@api.resolve('User')\nasync def made_up_field(source, info):\n return f\"{source.get_full_name()} is a lying lier there is no 'made_up_field'\"\n```\n\nSince GraphQL is agnostic about where or how you store your data all you need\nto do is provide a function to resolve a query. The results you return just\nneed to match the schema and you are done.\n\nDjango and sqlalchemy already provide tools to query the database. And they\nwork quite well. Or you may choose to use an async database library to make\nconcurrent requests work even better. Try them all and see what works best for\nyour team and your use case.\n\n

Examples and Documentation

\n\n* [hello world](examples/hello.py)\n* [using mocks](examples/mocks.py)\n* [Real World Example](examples/cloud)\n\n[Documentation](https://cannula.readthedocs.io/)\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/rmyers/cannula", "keywords": "graphql", "license": "MIT License", "maintainer": "", "maintainer_email": "", "name": "cannula", "package_url": "https://pypi.org/project/cannula/", "platform": "", "project_url": "https://pypi.org/project/cannula/", "project_urls": { "Homepage": "https://github.com/rmyers/cannula" }, "release_url": "https://pypi.org/project/cannula/0.0.2/", "requires_dist": [ "graphql-core-next" ], "requires_python": "", "summary": "Async GraphQL Helper Library", "version": "0.0.2" }, "last_serial": 5264508, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "2fe66c9d2e365eedae9ceb30b8129e0a", "sha256": "c992a3dee9e4aa240ab3a77114ed7745b99087a72d1c7db91bb6e3c78fa0d82c" }, "downloads": -1, "filename": "cannula-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "2fe66c9d2e365eedae9ceb30b8129e0a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 13324, "upload_time": "2019-02-27T15:53:05", "url": "https://files.pythonhosted.org/packages/c6/ad/a531b05cc49b6d309832e9d5e1de6c35c5f1d3c6caad80eaad54c49bdf57/cannula-0.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "43f3ef5ce7d41c198cebd33a011c2471", "sha256": "e03f522a2e535bec06ef80b80b9c2e4a02e1eff00fd7aabb64b5260aa48767d7" }, "downloads": -1, "filename": "cannula-0.0.1.tar.gz", "has_sig": false, "md5_digest": "43f3ef5ce7d41c198cebd33a011c2471", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12804, "upload_time": "2019-02-27T15:53:07", "url": "https://files.pythonhosted.org/packages/c5/af/572c14b1d90bb4ca71c75b8eb686337987432572b85658941e373bffd7f9/cannula-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "81cb28e2fd12c677bf4accfe650329dd", "sha256": "83b37cfcbed4e231c4e9703498d90ab8bdb0c2ff07a1fab3cd33914a8221ee95" }, "downloads": -1, "filename": "cannula-0.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "81cb28e2fd12c677bf4accfe650329dd", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 23122, "upload_time": "2019-05-13T21:20:44", "url": "https://files.pythonhosted.org/packages/14/a5/6516e808e5edb4f0e8356d95cd4809aa1444f750e579584e474a1ca30e29/cannula-0.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3544ee6f5476b74e692b2602e9e6dfec", "sha256": "27a4a940fb19a34dc427befc5e6230f0233d31aac7a623130edbf084802d4da2" }, "downloads": -1, "filename": "cannula-0.0.2.tar.gz", "has_sig": false, "md5_digest": "3544ee6f5476b74e692b2602e9e6dfec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18580, "upload_time": "2019-05-13T21:20:46", "url": "https://files.pythonhosted.org/packages/dd/03/b3d86660879d792b27eb51e49daf8219b0fce78217037c76e77a41875115/cannula-0.0.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "81cb28e2fd12c677bf4accfe650329dd", "sha256": "83b37cfcbed4e231c4e9703498d90ab8bdb0c2ff07a1fab3cd33914a8221ee95" }, "downloads": -1, "filename": "cannula-0.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "81cb28e2fd12c677bf4accfe650329dd", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 23122, "upload_time": "2019-05-13T21:20:44", "url": "https://files.pythonhosted.org/packages/14/a5/6516e808e5edb4f0e8356d95cd4809aa1444f750e579584e474a1ca30e29/cannula-0.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3544ee6f5476b74e692b2602e9e6dfec", "sha256": "27a4a940fb19a34dc427befc5e6230f0233d31aac7a623130edbf084802d4da2" }, "downloads": -1, "filename": "cannula-0.0.2.tar.gz", "has_sig": false, "md5_digest": "3544ee6f5476b74e692b2602e9e6dfec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18580, "upload_time": "2019-05-13T21:20:46", "url": "https://files.pythonhosted.org/packages/dd/03/b3d86660879d792b27eb51e49daf8219b0fce78217037c76e77a41875115/cannula-0.0.2.tar.gz" } ] }