Skip to content

Commit

Permalink
Optional dependencies for transports (#158)
Browse files Browse the repository at this point in the history
* Disallow importing transports directly from gql
  • Loading branch information
leszekhanusz authored Nov 1, 2020
1 parent ec67bbd commit b42fe08
Show file tree
Hide file tree
Showing 31 changed files with 486 additions and 132 deletions.
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

0 comments on commit b42fe08

Please sign in to comment.