Skip to content

Optional dependencies for transports #158

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
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
20 changes: 20 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ jobs:
env:
TOXENV: ${{ matrix.toxenv }}

single_extra:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dependency: ["aiohttp", "requests", "websockets"]

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies with only ${{ matrix.dependency }} extra dependency
run: |
python -m pip install --upgrade pip
pip install .[${{ matrix.dependency }},test_no_transport]
- name: Test with --${{ matrix.dependency }}-only
run: pytest tests --${{ matrix.dependency }}-only

coverage:
runs-on: ubuntu-latest

Expand Down
19 changes: 15 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.PHONY: clean tests docs

SRC_PYTHON := gql tests scripts/gql-cli docs/code_examples

dev-setup:
python pip install -e ".[test]"

Expand All @@ -9,11 +11,20 @@ tests:
all_tests:
pytest tests --cov=gql --cov-report=term-missing --run-online -vv

tests_aiohttp:
pytest tests --aiohttp-only

tests_requests:
pytest tests --requests-only

tests_websockets:
pytest tests --websockets-only

check:
isort --recursive gql tests scripts/gql-cli
black gql tests scripts/gql-cli
flake8 gql tests scripts/gql-cli
mypy gql tests scripts/gql-cli
isort --recursive $(SRC_PYTHON)
black $(SRC_PYTHON)
flake8 $(SRC_PYTHON)
mypy $(SRC_PYTHON)
check-manifest

docs:
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ The main features of GQL are:

> **WARNING**: Please note that the following documentation describes the current version which is currently only available as a pre-release and needs to be installed with

$ pip install --pre gql
$ pip install --pre gql[all]

> **NOTE**: See also [the documentation](https://gql.readthedocs.io/en/latest/intro.html#less-dependencies) to install GQL with less extra dependencies

## Usage

Expand Down
2 changes: 1 addition & 1 deletion docs/async/async_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Example:
IPython
-------

.. Attention::
.. warning::

On some Python environments, like :emphasis:`Jupyter` or :emphasis:`Spyder`,
which are using :emphasis:`IPython`,
Expand Down
19 changes: 12 additions & 7 deletions docs/code_examples/aiohttp_async.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
from gql import gql, AIOHTTPTransport, Client
import asyncio

from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport


async def main():

transport = AIOHTTPTransport(url='https://countries.trevorblades.com/graphql')
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:
transport=transport, fetch_schema_from_transport=True,
) as session:

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

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


asyncio.run(main())
3 changes: 2 additions & 1 deletion docs/code_examples/aiohttp_sync.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from gql import gql, Client, AIOHTTPTransport
from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport

# Select your transport with a defined url endpoint
transport = AIOHTTPTransport(url="https://countries.trevorblades.com/")
Expand Down
19 changes: 8 additions & 11 deletions docs/code_examples/requests_sync.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
from gql import gql, Client
from gql import Client, gql
from gql.transport.requests import RequestsHTTPTransport

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

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

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

result = client.execute(query)
print(result)
27 changes: 17 additions & 10 deletions docs/code_examples/websockets_async.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,48 @@
import asyncio
import logging

from gql import Client, gql
from gql.transport.websockets import WebsocketsTransport

logging.basicConfig(level=logging.INFO)

from gql import gql, Client, WebsocketsTransport
import asyncio

async def main():

transport = WebsocketsTransport(url='wss://countries.trevorblades.com/graphql')
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=transport,
fetch_schema_from_transport=True,
) as session:
transport=transport, fetch_schema_from_transport=True,
) as session:

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

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


asyncio.run(main())
40 changes: 37 additions & 3 deletions docs/intro.rst
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
Introduction
============

`GQL 3`_ is a `GraphQL`_ Client for Python 3.6+ which plays nicely with other graphql implementations compatible with the spec.
`GQL 3`_ is a `GraphQL`_ Client for Python 3.6+ which plays nicely with other
graphql implementations compatible with the spec.

Under the hood, it uses `GraphQL-core`_ which is a Python port of `GraphQL.js`_,
the JavaScript reference implementation for GraphQL.

Installation
------------

You can install GQL 3 using pip_::
You can install GQL 3 and all the extra dependencies using pip_::

pip install --pre gql
pip install --pre gql[all]

.. warning::

Expand All @@ -21,6 +22,39 @@ You can install GQL 3 using pip_::
After installation, you can start using GQL by importing from the top-level
:mod:`gql` package.

Less dependencies
^^^^^^^^^^^^^^^^^

GQL supports multiple :ref:`transports <transports>` to communicate with the backend.
Each transport can each necessitate specific dependencies.
If you only need one transport, instead of using the "`all`" extra dependency
as described above which installs everything,
you might want to install only the dependency needed for your transport.

If for example you only need the :ref:`AIOHTTPTransport <aiohttp_transport>`,
which needs the :code:`aiohttp` dependency, then you can install GQL with::

pip install --pre gql[aiohttp]

The corresponding between extra dependencies required and the GQL transports is:

+-------------------+----------------------------------------------------------------+
| Extra dependency | Transports |
+===================+================================================================+
| aiohttp | :ref:`AIOHTTPTransport <aiohttp_transport>` |
+-------------------+----------------------------------------------------------------+
| websockets | :ref:`WebsocketsTransport <websockets_transport>` |
| | |
| | :ref:`PhoenixChannelWebsocketsTransport <phoenix_transport>` |
+-------------------+----------------------------------------------------------------+
| requests | :ref:`RequestsHTTPTransport <requests_transport>` |
+-------------------+----------------------------------------------------------------+

.. note::

It is also possible to install multiple extra dependencies if needed
using commas: :code:`gql[aiohttp,websockets]`

Reporting Issues and Contributing
---------------------------------

Expand Down
2 changes: 2 additions & 0 deletions docs/transports/phoenix.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _phoenix_transport:

PhoenixChannelWebsocketsTransport
=================================

Expand Down
2 changes: 2 additions & 0 deletions docs/transports/requests.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _requests_transport:

RequestsHTTPTransport
=====================

Expand Down
3 changes: 2 additions & 1 deletion docs/usage/subscriptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Using the :ref:`websockets transport <websockets_transport>`, it is possible to

.. code-block:: python

from gql import gql, Client, WebsocketsTransport
from gql import gql, Client
from gql.transport.websockets import WebsocketsTransport

transport = WebsocketsTransport(url='wss://your_server/graphql')

Expand Down
12 changes: 2 additions & 10 deletions gql/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
"""The primary :mod:`gql` package includes everything you need to
execute GraphQL requests:
execute GraphQL requests, with the exception of the transports
which are optional:

- the :func:`gql <gql.gql>` method to parse a GraphQL query
- the :class:`Client <gql.Client>` class as the entrypoint to execute requests
and create sessions
- all the transports classes implementing different communication protocols
"""

from .__version__ import __version__
from .client import Client
from .gql import gql
from .transport.aiohttp import AIOHTTPTransport
from .transport.phoenix_channel_websockets import PhoenixChannelWebsocketsTransport
from .transport.requests import RequestsHTTPTransport
from .transport.websockets import WebsocketsTransport

__all__ = [
"__version__",
"gql",
"AIOHTTPTransport",
"Client",
"PhoenixChannelWebsocketsTransport",
"RequestsHTTPTransport",
"WebsocketsTransport",
]
19 changes: 10 additions & 9 deletions gql/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

from gql import Client, __version__, gql
from gql.transport import AsyncTransport
from gql.transport.aiohttp import AIOHTTPTransport
from gql.transport.exceptions import TransportQueryError
from gql.transport.websockets import WebsocketsTransport

description = """
Send GraphQL queries from the command line using http(s) or websockets.
Expand Down Expand Up @@ -201,10 +199,14 @@ def get_transport(args: Namespace) -> AsyncTransport:
# Instanciate transport depending on url scheme
transport: AsyncTransport
if scheme in ["ws", "wss"]:
from gql.transport.websockets import WebsocketsTransport

transport = WebsocketsTransport(
url=args.server, ssl=(scheme == "wss"), **transport_args
)
elif scheme in ["http", "https"]:
from gql.transport.aiohttp import AIOHTTPTransport

transport = AIOHTTPTransport(url=args.server, **transport_args)
else:
raise ValueError("URL protocol should be one of: http, https, ws, wss")
Expand Down Expand Up @@ -261,13 +263,12 @@ async def main(args: Namespace) -> int:

# Execute or Subscribe the query depending on transport
try:
if isinstance(transport, WebsocketsTransport):
try:
async for result in session.subscribe(query, **execute_args):
print(json.dumps(result))
except KeyboardInterrupt: # pragma: no cover
pass
else:
try:
async for result in session.subscribe(query, **execute_args):
print(json.dumps(result))
except KeyboardInterrupt: # pragma: no cover
pass
except NotImplementedError:
result = await session.execute(query, **execute_args)
print(json.dumps(result))
except (GraphQLError, TransportQueryError) as e:
Expand Down
Loading