Skip to content

chore: generate docs with sphinx #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ include tox.ini
include scripts/gql-cli

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

prune docs/_build
prune gql-checker

global-exclude *.py[co] __pycache__
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: clean tests
.PHONY: clean tests docs

dev-setup:
python pip install -e ".[test]"
Expand All @@ -16,6 +16,10 @@ check:
mypy gql tests
check-manifest

docs:
rm -rf ./docs/_build
cd docs; make html

clean:
find . -name "*.pyc" -delete
find . -name "__pycache__" | xargs -I {} rm -rf {}
Expand All @@ -26,4 +30,5 @@ clean:
rm -rf ./gql.egg-info
rm -rf ./dist
rm -rf ./build
rm -rf ./docs/_build
rm -f ./.coverage
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
57 changes: 57 additions & 0 deletions docs/advanced/async_advanced_usage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.. _async_advanced_usage:

Async advanced usage
====================

It is possible to send multiple GraphQL queries (query, mutation or subscription) in parallel,
on the same websocket connection, using asyncio tasks.

In order to retry in case of connection failure, we can use the great `backoff`_ module.

.. code-block:: python

# First define all your queries using a session argument:

async def execute_query1(session):
result = await session.execute(query1)
print(result)

async def execute_query2(session):
result = await session.execute(query2)
print(result)

async def execute_subscription1(session):
async for result in session.subscribe(subscription1):
print(result)

async def execute_subscription2(session):
async for result in session.subscribe(subscription2):
print(result)

# Then create a couroutine which will connect to your API and run all your queries as tasks.
# We use a `backoff` decorator to reconnect using exponential backoff in case of connection failure.

@backoff.on_exception(backoff.expo, Exception, max_time=300)
async def graphql_connection():

transport = WebsocketsTransport(url="wss://YOUR_URL")

client = Client(transport=transport, fetch_schema_from_transport=True)

async with client as session:
task1 = asyncio.create_task(execute_query1(session))
task2 = asyncio.create_task(execute_query2(session))
task3 = asyncio.create_task(execute_subscription1(session))
task4 = asyncio.create_task(execute_subscription2(session))

await asyncio.gather(task1, task2, task3, task4)

asyncio.run(graphql_connection())

Subscriptions tasks can be stopped at any time by running

.. code-block:: python

task.cancel()

.. _backoff: https://github.com/litl/backoff
34 changes: 34 additions & 0 deletions docs/advanced/dsl_module.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Compose queries dynamically
===========================

Instead of providing the GraphQL queries as a Python String, it is also possible to create GraphQL queries dynamically.
Using the DSL module, we can create a query using a Domain Specific Language which is created from the schema.

.. code-block:: python

from gql.dsl import DSLSchema

client = Client(schema=StarWarsSchema)
ds = DSLSchema(client)

query_dsl = ds.Query.hero.select(
ds.Character.id,
ds.Character.name,
ds.Character.friends.select(ds.Character.name,),
)

will create a query equivalent to:

.. code-block:: python

hero {
id
name
friends {
name
}
}

.. warning::

Please note that the DSL module is still considered experimental in GQL 3 and is subject to changes
9 changes: 9 additions & 0 deletions docs/advanced/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Advanced
========

.. toctree::
:maxdepth: 2

async_advanced_usage
local_schema
dsl_module
25 changes: 25 additions & 0 deletions docs/advanced/local_schema.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Execution on a local schema
===========================

It is also possible to execute queries against a local schema (so without a transport), even
if it is not really useful except maybe for testing.

.. code-block:: python

from gql import gql, Client

from .someSchema import SampleSchema

client = Client(schema=SampleSchema)

query = gql('''
{
hello
}
''')

result = client.execute(query)

See `tests/starwars/test_query.py`_ for an example

.. _tests/starwars/test_query.py: https://github.com/graphql-python/gql/blob/master/tests/starwars/test_query.py
18 changes: 18 additions & 0 deletions docs/async/async_intro.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
On previous versions of GQL, the code was `sync` only , it means that when you ran
`execute` on the Client, you could do nothing else in the current Thread and had to wait for
an answer or a timeout from the backend to continue. The only http library was `requests`, allowing only sync usage.

From the version 3 of GQL, we support `sync` and `async` :ref:`transports <transports>` using `asyncio`_.

With the :ref:`async transports <async_transports>`, there is now the possibility to execute GraphQL requests
asynchronously, :ref:`allowing to execute multiple requests in parallel if needed <async_advanced_usage>`.

If you don't care or need async functionality, it is still possible, with :ref:`async transports <async_transports>`,
to run the `execute` or `subscribe` methods directly from the Client
(as described in the :ref:`Basic Usage <basic_usage>` example) and GQL will execute the request
in a synchronous manner by running an asyncio event loop itself.

This won't work though if you already have an asyncio event loop running. In that case you should use
:ref:`Async Usage <async_usage>`

.. _asyncio: https://docs.python.org/3/library/asyncio.html
17 changes: 17 additions & 0 deletions docs/async/async_usage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.. _async_usage:

Async Usage
===========

If you use an :ref:`async transport <async_transports>`, you can use GQL asynchronously using `asyncio`_.

* put your code in an asyncio coroutine (method starting with :code:`async def`)
* use :code:`async with client as session:` to connect to the backend and provide a session instance
* use the :code:`await` keyword to execute requests: :code:`await session.execute(...)`
* then run your coroutine in an asyncio event loop by running :code:`asyncio.run`

Example:

.. literalinclude:: ../code_examples/aiohttp_async.py

.. _asyncio: https://docs.python.org/3/library/asyncio.html
10 changes: 10 additions & 0 deletions docs/async/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Async vs Sync
=============

.. include:: async_intro.rst

.. toctree::
:hidden:
:maxdepth: 1

async_usage
28 changes: 28 additions & 0 deletions docs/code_examples/aiohttp_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from gql import gql, AIOHTTPTransport, Client
import asyncio

async def main():

transport = AIOHTTPTransport(url='https://countries.trevorblades.com/graphql')

# Using `async with` on the client will start a connection on the transport
# and provide a `session` variable to execute queries on this connection
async with Client(
transport=transport,
fetch_schema_from_transport=True,
) as session:

# Execute single query
query = gql('''
query getContinents {
continents {
code
name
}
}
''')

result = await session.execute(query)
print(result)

asyncio.run(main())
23 changes: 23 additions & 0 deletions docs/code_examples/aiohttp_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from gql import gql, Client, AIOHTTPTransport

# Select your transport with a defined url endpoint
transport = AIOHTTPTransport(url="https://countries.trevorblades.com/")

# Create a GraphQL client using the defined transport
client = Client(transport=transport, fetch_schema_from_transport=True)

# Provide a GraphQL query
query = gql(
"""
query getContinents {
continents {
code
name
}
}
"""
)

# Execute the query on the transport
result = client.execute(query)
print(result)
25 changes: 25 additions & 0 deletions docs/code_examples/requests_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport

sample_transport=RequestsHTTPTransport(
url='https://countries.trevorblades.com/',
verify=True,
retries=3,
)

client = Client(
transport=sample_transport,
fetch_schema_from_transport=True,
)

query = gql('''
query getContinents {
continents {
code
name
}
}
''')

result = client.execute(query)
print(result)
41 changes: 41 additions & 0 deletions docs/code_examples/websockets_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import logging
logging.basicConfig(level=logging.INFO)

from gql import gql, Client, WebsocketsTransport
import asyncio

async def main():

transport = WebsocketsTransport(url='wss://countries.trevorblades.com/graphql')

# Using `async with` on the client will start a connection on the transport
# and provide a `session` variable to execute queries on this connection
async with Client(
transport=sample_transport,
fetch_schema_from_transport=True,
) as session:

# Execute single query
query = gql('''
query getContinents {
continents {
code
name
}
}
''')
result = await session.execute(query)
print(result)

# Request subscription
subscription = gql('''
subscription {
somethingChanged {
id
}
}
''')
async for result in session.subscribe(subscription):
print(result)

asyncio.run(main())
Loading