Skip to content

Commit 29aba29

Browse files
chore: generate docs with sphinx (#117)
* chore: generate docs with sphinx * chore: avoid documenting private members * chore: add docs to manifiest * chore: manually document every class on transport * Write docs in sphinx rst format * fix manifest * Improve classes reference documentation Co-authored-by: Hanusz Leszek <[email protected]>
1 parent 706f789 commit 29aba29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+982
-32
lines changed

MANIFEST.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ include tox.ini
1313
include scripts/gql-cli
1414

1515
recursive-include tests *.py *.graphql *.cnf *.yaml *.pem
16+
recursive-include docs *.txt *.rst conf.py Makefile make.bat *.jpg *.png *.gif
17+
recursive-include docs/code_examples *.py
1618

19+
prune docs/_build
1720
prune gql-checker
1821

1922
global-exclude *.py[co] __pycache__

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: clean tests
1+
.PHONY: clean tests docs
22

33
dev-setup:
44
python pip install -e ".[test]"
@@ -16,6 +16,10 @@ check:
1616
mypy gql tests
1717
check-manifest
1818

19+
docs:
20+
rm -rf ./docs/_build
21+
cd docs; make html
22+
1923
clean:
2024
find . -name "*.pyc" -delete
2125
find . -name "__pycache__" | xargs -I {} rm -rf {}
@@ -26,4 +30,5 @@ clean:
2630
rm -rf ./gql.egg-info
2731
rm -rf ./dist
2832
rm -rf ./build
33+
rm -rf ./docs/_build
2934
rm -f ./.coverage

docs/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Minimal makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line, and also
5+
# from the environment for the first two.
6+
SPHINXOPTS ?=
7+
SPHINXBUILD ?= sphinx-build
8+
SOURCEDIR = .
9+
BUILDDIR = _build
10+
11+
# Put it first so that "make" without argument is like "make help".
12+
help:
13+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14+
15+
.PHONY: help Makefile
16+
17+
# Catch-all target: route all unknown targets to Sphinx using the new
18+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19+
%: Makefile
20+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
.. _async_advanced_usage:
2+
3+
Async advanced usage
4+
====================
5+
6+
It is possible to send multiple GraphQL queries (query, mutation or subscription) in parallel,
7+
on the same websocket connection, using asyncio tasks.
8+
9+
In order to retry in case of connection failure, we can use the great `backoff`_ module.
10+
11+
.. code-block:: python
12+
13+
# First define all your queries using a session argument:
14+
15+
async def execute_query1(session):
16+
result = await session.execute(query1)
17+
print(result)
18+
19+
async def execute_query2(session):
20+
result = await session.execute(query2)
21+
print(result)
22+
23+
async def execute_subscription1(session):
24+
async for result in session.subscribe(subscription1):
25+
print(result)
26+
27+
async def execute_subscription2(session):
28+
async for result in session.subscribe(subscription2):
29+
print(result)
30+
31+
# Then create a couroutine which will connect to your API and run all your queries as tasks.
32+
# We use a `backoff` decorator to reconnect using exponential backoff in case of connection failure.
33+
34+
@backoff.on_exception(backoff.expo, Exception, max_time=300)
35+
async def graphql_connection():
36+
37+
transport = WebsocketsTransport(url="wss://YOUR_URL")
38+
39+
client = Client(transport=transport, fetch_schema_from_transport=True)
40+
41+
async with client as session:
42+
task1 = asyncio.create_task(execute_query1(session))
43+
task2 = asyncio.create_task(execute_query2(session))
44+
task3 = asyncio.create_task(execute_subscription1(session))
45+
task4 = asyncio.create_task(execute_subscription2(session))
46+
47+
await asyncio.gather(task1, task2, task3, task4)
48+
49+
asyncio.run(graphql_connection())
50+
51+
Subscriptions tasks can be stopped at any time by running
52+
53+
.. code-block:: python
54+
55+
task.cancel()
56+
57+
.. _backoff: https://github.com/litl/backoff

docs/advanced/dsl_module.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Compose queries dynamically
2+
===========================
3+
4+
Instead of providing the GraphQL queries as a Python String, it is also possible to create GraphQL queries dynamically.
5+
Using the DSL module, we can create a query using a Domain Specific Language which is created from the schema.
6+
7+
.. code-block:: python
8+
9+
from gql.dsl import DSLSchema
10+
11+
client = Client(schema=StarWarsSchema)
12+
ds = DSLSchema(client)
13+
14+
query_dsl = ds.Query.hero.select(
15+
ds.Character.id,
16+
ds.Character.name,
17+
ds.Character.friends.select(ds.Character.name,),
18+
)
19+
20+
will create a query equivalent to:
21+
22+
.. code-block:: python
23+
24+
hero {
25+
id
26+
name
27+
friends {
28+
name
29+
}
30+
}
31+
32+
.. warning::
33+
34+
Please note that the DSL module is still considered experimental in GQL 3 and is subject to changes

docs/advanced/index.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Advanced
2+
========
3+
4+
.. toctree::
5+
:maxdepth: 2
6+
7+
async_advanced_usage
8+
local_schema
9+
dsl_module

docs/advanced/local_schema.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Execution on a local schema
2+
===========================
3+
4+
It is also possible to execute queries against a local schema (so without a transport), even
5+
if it is not really useful except maybe for testing.
6+
7+
.. code-block:: python
8+
9+
from gql import gql, Client
10+
11+
from .someSchema import SampleSchema
12+
13+
client = Client(schema=SampleSchema)
14+
15+
query = gql('''
16+
{
17+
hello
18+
}
19+
''')
20+
21+
result = client.execute(query)
22+
23+
See `tests/starwars/test_query.py`_ for an example
24+
25+
.. _tests/starwars/test_query.py: https://github.com/graphql-python/gql/blob/master/tests/starwars/test_query.py

docs/async/async_intro.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
On previous versions of GQL, the code was `sync` only , it means that when you ran
2+
`execute` on the Client, you could do nothing else in the current Thread and had to wait for
3+
an answer or a timeout from the backend to continue. The only http library was `requests`, allowing only sync usage.
4+
5+
From the version 3 of GQL, we support `sync` and `async` :ref:`transports <transports>` using `asyncio`_.
6+
7+
With the :ref:`async transports <async_transports>`, there is now the possibility to execute GraphQL requests
8+
asynchronously, :ref:`allowing to execute multiple requests in parallel if needed <async_advanced_usage>`.
9+
10+
If you don't care or need async functionality, it is still possible, with :ref:`async transports <async_transports>`,
11+
to run the `execute` or `subscribe` methods directly from the Client
12+
(as described in the :ref:`Basic Usage <basic_usage>` example) and GQL will execute the request
13+
in a synchronous manner by running an asyncio event loop itself.
14+
15+
This won't work though if you already have an asyncio event loop running. In that case you should use
16+
:ref:`Async Usage <async_usage>`
17+
18+
.. _asyncio: https://docs.python.org/3/library/asyncio.html

docs/async/async_usage.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.. _async_usage:
2+
3+
Async Usage
4+
===========
5+
6+
If you use an :ref:`async transport <async_transports>`, you can use GQL asynchronously using `asyncio`_.
7+
8+
* put your code in an asyncio coroutine (method starting with :code:`async def`)
9+
* use :code:`async with client as session:` to connect to the backend and provide a session instance
10+
* use the :code:`await` keyword to execute requests: :code:`await session.execute(...)`
11+
* then run your coroutine in an asyncio event loop by running :code:`asyncio.run`
12+
13+
Example:
14+
15+
.. literalinclude:: ../code_examples/aiohttp_async.py
16+
17+
.. _asyncio: https://docs.python.org/3/library/asyncio.html

docs/async/index.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Async vs Sync
2+
=============
3+
4+
.. include:: async_intro.rst
5+
6+
.. toctree::
7+
:hidden:
8+
:maxdepth: 1
9+
10+
async_usage

0 commit comments

Comments
 (0)