Skip to content

Commit b42fe08

Browse files
authored
Optional dependencies for transports (#158)
* Disallow importing transports directly from gql
1 parent ec67bbd commit b42fe08

31 files changed

+486
-132
lines changed

.github/workflows/tests.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@ jobs:
3535
env:
3636
TOXENV: ${{ matrix.toxenv }}
3737

38+
single_extra:
39+
runs-on: ubuntu-latest
40+
strategy:
41+
fail-fast: false
42+
matrix:
43+
dependency: ["aiohttp", "requests", "websockets"]
44+
45+
steps:
46+
- uses: actions/checkout@v2
47+
- name: Set up Python 3.8
48+
uses: actions/setup-python@v2
49+
with:
50+
python-version: 3.8
51+
- name: Install dependencies with only ${{ matrix.dependency }} extra dependency
52+
run: |
53+
python -m pip install --upgrade pip
54+
pip install .[${{ matrix.dependency }},test_no_transport]
55+
- name: Test with --${{ matrix.dependency }}-only
56+
run: pytest tests --${{ matrix.dependency }}-only
57+
3858
coverage:
3959
runs-on: ubuntu-latest
4060

Makefile

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.PHONY: clean tests docs
22

3+
SRC_PYTHON := gql tests scripts/gql-cli docs/code_examples
4+
35
dev-setup:
46
python pip install -e ".[test]"
57

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

14+
tests_aiohttp:
15+
pytest tests --aiohttp-only
16+
17+
tests_requests:
18+
pytest tests --requests-only
19+
20+
tests_websockets:
21+
pytest tests --websockets-only
22+
1223
check:
13-
isort --recursive gql tests scripts/gql-cli
14-
black gql tests scripts/gql-cli
15-
flake8 gql tests scripts/gql-cli
16-
mypy gql tests scripts/gql-cli
24+
isort --recursive $(SRC_PYTHON)
25+
black $(SRC_PYTHON)
26+
flake8 $(SRC_PYTHON)
27+
mypy $(SRC_PYTHON)
1728
check-manifest
1829

1930
docs:

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ The main features of GQL are:
4545

4646
> **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
4747
48-
$ pip install --pre gql
48+
$ pip install --pre gql[all]
49+
50+
> **NOTE**: See also [the documentation](https://gql.readthedocs.io/en/latest/intro.html#less-dependencies) to install GQL with less extra dependencies
4951
5052
## Usage
5153

docs/async/async_usage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Example:
1717
IPython
1818
-------
1919

20-
.. Attention::
20+
.. warning::
2121

2222
On some Python environments, like :emphasis:`Jupyter` or :emphasis:`Spyder`,
2323
which are using :emphasis:`IPython`,

docs/code_examples/aiohttp_async.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
1-
from gql import gql, AIOHTTPTransport, Client
21
import asyncio
32

3+
from gql import Client, gql
4+
from gql.transport.aiohttp import AIOHTTPTransport
5+
6+
47
async def main():
58

6-
transport = AIOHTTPTransport(url='https://countries.trevorblades.com/graphql')
9+
transport = AIOHTTPTransport(url="https://countries.trevorblades.com/graphql")
710

811
# Using `async with` on the client will start a connection on the transport
912
# and provide a `session` variable to execute queries on this connection
1013
async with Client(
11-
transport=transport,
12-
fetch_schema_from_transport=True,
13-
) as session:
14+
transport=transport, fetch_schema_from_transport=True,
15+
) as session:
1416

1517
# Execute single query
16-
query = gql('''
18+
query = gql(
19+
"""
1720
query getContinents {
1821
continents {
1922
code
2023
name
2124
}
2225
}
23-
''')
26+
"""
27+
)
2428

2529
result = await session.execute(query)
2630
print(result)
2731

32+
2833
asyncio.run(main())

docs/code_examples/aiohttp_sync.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from gql import gql, Client, AIOHTTPTransport
1+
from gql import Client, gql
2+
from gql.transport.aiohttp import AIOHTTPTransport
23

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

docs/code_examples/requests_sync.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
1-
from gql import gql, Client
1+
from gql import Client, gql
22
from gql.transport.requests import RequestsHTTPTransport
33

4-
sample_transport=RequestsHTTPTransport(
5-
url='https://countries.trevorblades.com/',
6-
verify=True,
7-
retries=3,
4+
sample_transport = RequestsHTTPTransport(
5+
url="https://countries.trevorblades.com/", verify=True, retries=3,
86
)
97

10-
client = Client(
11-
transport=sample_transport,
12-
fetch_schema_from_transport=True,
13-
)
8+
client = Client(transport=sample_transport, fetch_schema_from_transport=True,)
149

15-
query = gql('''
10+
query = gql(
11+
"""
1612
query getContinents {
1713
continents {
1814
code
1915
name
2016
}
2117
}
22-
''')
18+
"""
19+
)
2320

2421
result = client.execute(query)
2522
print(result)
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,48 @@
1+
import asyncio
12
import logging
3+
4+
from gql import Client, gql
5+
from gql.transport.websockets import WebsocketsTransport
6+
27
logging.basicConfig(level=logging.INFO)
38

4-
from gql import gql, Client, WebsocketsTransport
5-
import asyncio
69

710
async def main():
811

9-
transport = WebsocketsTransport(url='wss://countries.trevorblades.com/graphql')
12+
transport = WebsocketsTransport(url="wss://countries.trevorblades.com/graphql")
1013

1114
# Using `async with` on the client will start a connection on the transport
1215
# and provide a `session` variable to execute queries on this connection
1316
async with Client(
14-
transport=transport,
15-
fetch_schema_from_transport=True,
16-
) as session:
17+
transport=transport, fetch_schema_from_transport=True,
18+
) as session:
1719

1820
# Execute single query
19-
query = gql('''
21+
query = gql(
22+
"""
2023
query getContinents {
2124
continents {
2225
code
2326
name
2427
}
2528
}
26-
''')
29+
"""
30+
)
2731
result = await session.execute(query)
2832
print(result)
2933

3034
# Request subscription
31-
subscription = gql('''
35+
subscription = gql(
36+
"""
3237
subscription {
3338
somethingChanged {
3439
id
3540
}
3641
}
37-
''')
42+
"""
43+
)
3844
async for result in session.subscribe(subscription):
3945
print(result)
4046

47+
4148
asyncio.run(main())

docs/intro.rst

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
Introduction
22
============
33

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

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

910
Installation
1011
------------
1112

12-
You can install GQL 3 using pip_::
13+
You can install GQL 3 and all the extra dependencies using pip_::
1314

14-
pip install --pre gql
15+
pip install --pre gql[all]
1516

1617
.. warning::
1718

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

25+
Less dependencies
26+
^^^^^^^^^^^^^^^^^
27+
28+
GQL supports multiple :ref:`transports <transports>` to communicate with the backend.
29+
Each transport can each necessitate specific dependencies.
30+
If you only need one transport, instead of using the "`all`" extra dependency
31+
as described above which installs everything,
32+
you might want to install only the dependency needed for your transport.
33+
34+
If for example you only need the :ref:`AIOHTTPTransport <aiohttp_transport>`,
35+
which needs the :code:`aiohttp` dependency, then you can install GQL with::
36+
37+
pip install --pre gql[aiohttp]
38+
39+
The corresponding between extra dependencies required and the GQL transports is:
40+
41+
+-------------------+----------------------------------------------------------------+
42+
| Extra dependency | Transports |
43+
+===================+================================================================+
44+
| aiohttp | :ref:`AIOHTTPTransport <aiohttp_transport>` |
45+
+-------------------+----------------------------------------------------------------+
46+
| websockets | :ref:`WebsocketsTransport <websockets_transport>` |
47+
| | |
48+
| | :ref:`PhoenixChannelWebsocketsTransport <phoenix_transport>` |
49+
+-------------------+----------------------------------------------------------------+
50+
| requests | :ref:`RequestsHTTPTransport <requests_transport>` |
51+
+-------------------+----------------------------------------------------------------+
52+
53+
.. note::
54+
55+
It is also possible to install multiple extra dependencies if needed
56+
using commas: :code:`gql[aiohttp,websockets]`
57+
2458
Reporting Issues and Contributing
2559
---------------------------------
2660

docs/transports/phoenix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _phoenix_transport:
2+
13
PhoenixChannelWebsocketsTransport
24
=================================
35

docs/transports/requests.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _requests_transport:
2+
13
RequestsHTTPTransport
24
=====================
35

docs/usage/subscriptions.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Using the :ref:`websockets transport <websockets_transport>`, it is possible to
55

66
.. code-block:: python
77
8-
from gql import gql, Client, WebsocketsTransport
8+
from gql import gql, Client
9+
from gql.transport.websockets import WebsocketsTransport
910
1011
transport = WebsocketsTransport(url='wss://your_server/graphql')
1112

gql/__init__.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
11
"""The primary :mod:`gql` package includes everything you need to
2-
execute GraphQL requests:
2+
execute GraphQL requests, with the exception of the transports
3+
which are optional:
34
45
- the :func:`gql <gql.gql>` method to parse a GraphQL query
56
- the :class:`Client <gql.Client>` class as the entrypoint to execute requests
67
and create sessions
7-
- all the transports classes implementing different communication protocols
88
"""
99

1010
from .__version__ import __version__
1111
from .client import Client
1212
from .gql import gql
13-
from .transport.aiohttp import AIOHTTPTransport
14-
from .transport.phoenix_channel_websockets import PhoenixChannelWebsocketsTransport
15-
from .transport.requests import RequestsHTTPTransport
16-
from .transport.websockets import WebsocketsTransport
1713

1814
__all__ = [
1915
"__version__",
2016
"gql",
21-
"AIOHTTPTransport",
2217
"Client",
23-
"PhoenixChannelWebsocketsTransport",
24-
"RequestsHTTPTransport",
25-
"WebsocketsTransport",
2618
]

gql/cli.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99

1010
from gql import Client, __version__, gql
1111
from gql.transport import AsyncTransport
12-
from gql.transport.aiohttp import AIOHTTPTransport
1312
from gql.transport.exceptions import TransportQueryError
14-
from gql.transport.websockets import WebsocketsTransport
1513

1614
description = """
1715
Send GraphQL queries from the command line using http(s) or websockets.
@@ -201,10 +199,14 @@ def get_transport(args: Namespace) -> AsyncTransport:
201199
# Instanciate transport depending on url scheme
202200
transport: AsyncTransport
203201
if scheme in ["ws", "wss"]:
202+
from gql.transport.websockets import WebsocketsTransport
203+
204204
transport = WebsocketsTransport(
205205
url=args.server, ssl=(scheme == "wss"), **transport_args
206206
)
207207
elif scheme in ["http", "https"]:
208+
from gql.transport.aiohttp import AIOHTTPTransport
209+
208210
transport = AIOHTTPTransport(url=args.server, **transport_args)
209211
else:
210212
raise ValueError("URL protocol should be one of: http, https, ws, wss")
@@ -261,13 +263,12 @@ async def main(args: Namespace) -> int:
261263

262264
# Execute or Subscribe the query depending on transport
263265
try:
264-
if isinstance(transport, WebsocketsTransport):
265-
try:
266-
async for result in session.subscribe(query, **execute_args):
267-
print(json.dumps(result))
268-
except KeyboardInterrupt: # pragma: no cover
269-
pass
270-
else:
266+
try:
267+
async for result in session.subscribe(query, **execute_args):
268+
print(json.dumps(result))
269+
except KeyboardInterrupt: # pragma: no cover
270+
pass
271+
except NotImplementedError:
271272
result = await session.execute(query, **execute_args)
272273
print(json.dumps(result))
273274
except (GraphQLError, TransportQueryError) as e:

0 commit comments

Comments
 (0)