Skip to content

Commit 6f327a9

Browse files
authored
Merge branch 'master' into fix_aiohttp_wait_for_closed_connections
2 parents 5632d6c + d3f79e7 commit 6f327a9

Some content is hidden

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

53 files changed

+1830
-892
lines changed

.github/workflows/tests.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ jobs:
88
strategy:
99
max-parallel: 4
1010
matrix:
11-
python-version: ["3.6", "3.7", "3.8", "3.9-dev", "pypy3"]
11+
python-version: ["3.6", "3.7", "3.8", "3.9", "pypy3"]
1212
os: [ubuntu-latest, windows-latest]
1313
exclude:
1414
- os: windows-latest
1515
python-version: "3.6"
1616
- os: windows-latest
1717
python-version: "3.7"
1818
- os: windows-latest
19-
python-version: "3.9-dev"
19+
python-version: "3.9"
2020
- os: windows-latest
2121
python-version: "pypy3"
2222

@@ -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: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,27 @@ The main features of GQL are:
3636

3737
* Execute GraphQL queries using [different protocols](https://gql.readthedocs.io/en/latest/transports/index.html) (http, websockets, ...)
3838
* Possibility to [validate the queries locally](https://gql.readthedocs.io/en/latest/usage/validation.html) using a GraphQL schema provided locally or fetched from the backend using an instrospection query
39-
* Supports GraphQL queries, mutations and subscriptions
39+
* Supports GraphQL queries, mutations and [subscriptions](https://gql.readthedocs.io/en/latest/usage/subscriptions.html)
4040
* Supports [sync or async usage](https://gql.readthedocs.io/en/latest/async/index.html), [allowing concurrent requests](https://gql.readthedocs.io/en/latest/advanced/async_advanced_usage.html#async-advanced-usage)
4141
* Supports [File uploads](https://gql.readthedocs.io/en/latest/usage/file_upload.html)
42+
* [gql-cli script](https://gql.readthedocs.io/en/latest/gql-cli/intro.html) to execute GraphQL queries from the command line
43+
* [DSL module](https://gql.readthedocs.io/en/latest/advanced/dsl_module.html) to compose GraphQL queries dynamically
4244

4345
## Installation
4446

4547
> **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
4648
47-
$ pip install --pre gql
49+
$ pip install --pre gql[all]
50+
51+
> **NOTE**: See also [the documentation](https://gql.readthedocs.io/en/latest/intro.html#less-dependencies) to install GQL with less extra dependencies
4852
4953
## Usage
5054

5155
### Basic usage
5256

5357
```python
54-
from gql import gql, Client, AIOHTTPTransport
58+
from gql import gql, Client
59+
from gql.transport.aiohttp import AIOHTTPTransport
5560

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

docs/advanced/dsl_module.rst

Lines changed: 208 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,225 @@ Compose queries dynamically
22
===========================
33

44
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.
5+
Using the :mod:`DSL module <gql.dsl>`, we can create a query using a Domain Specific Language which is created from the schema.
6+
7+
The following code:
68

79
.. code-block:: python
810
9-
from gql.dsl import DSLSchema
11+
ds = DSLSchema(StarWarsSchema)
12+
13+
query = dsl_gql(
14+
DSLQuery(
15+
ds.Query.hero.select(
16+
ds.Character.id,
17+
ds.Character.name,
18+
ds.Character.friends.select(ds.Character.name),
19+
)
20+
)
21+
)
22+
23+
will generate a query equivalent to:
24+
25+
.. code-block:: python
26+
27+
query = gql("""
28+
query {
29+
hero {
30+
id
31+
name
32+
friends {
33+
name
34+
}
35+
}
36+
}
37+
""")
38+
39+
How to use
40+
----------
41+
42+
First generate the root using the :class:`DSLSchema <gql.dsl.DSLSchema>`::
43+
44+
ds = DSLSchema(client.schema)
45+
46+
Then use auto-generated attributes of the :code:`ds` instance
47+
to get a root type (Query, Mutation or Subscription).
48+
This will generate a :class:`DSLType <gql.dsl.DSLType>` instance::
49+
50+
ds.Query
51+
52+
From this root type, you use auto-generated attributes to get a field.
53+
This will generate a :class:`DSLField <gql.dsl.DSLField>` instance::
54+
55+
ds.Query.hero
56+
57+
hero is a GraphQL object type and needs children fields. By default,
58+
there is no children fields selected. To select the fields that you want
59+
in your query, you use the :meth:`select <gql.dsl.DSLField.select>` method.
60+
61+
To generate the children fields, we use the same method as above to auto-generate the fields
62+
from the :code:`ds` instance
63+
(ie :code:`ds.Character.name` is the field `name` of the type `Character`)::
64+
65+
ds.Query.hero.select(ds.Character.name)
1066

11-
client = Client(schema=StarWarsSchema)
12-
ds = DSLSchema(client)
67+
The select method return the same instance, so it is possible to chain the calls::
1368

14-
query_dsl = ds.Query.hero.select(
69+
ds.Query.hero.select(ds.Character.name).select(ds.Character.id)
70+
71+
Or do it sequencially::
72+
73+
hero_query = ds.Query.hero
74+
75+
hero_query.select(ds.Character.name)
76+
hero_query.select(ds.Character.id)
77+
78+
As you can select children fields of any object type, you can construct your complete query tree::
79+
80+
ds.Query.hero.select(
1581
ds.Character.id,
1682
ds.Character.name,
17-
ds.Character.friends.select(ds.Character.name,),
83+
ds.Character.friends.select(ds.Character.name),
1884
)
1985

20-
will create a query equivalent to:
86+
Once your root query fields are defined, you can put them in an operation using
87+
:class:`DSLQuery <gql.dsl.DSLQuery>`,
88+
:class:`DSLMutation <gql.dsl.DSLMutation>` or
89+
:class:`DSLSubscription <gql.dsl.DSLSubscription>`::
2190

22-
.. code-block:: python
91+
DSLQuery(
92+
ds.Query.hero.select(
93+
ds.Character.id,
94+
ds.Character.name,
95+
ds.Character.friends.select(ds.Character.name),
96+
)
97+
)
98+
99+
100+
Once your operations are defined,
101+
use the :func:`dsl_gql <gql.dsl.dsl_gql>` function to convert your operations into
102+
a document which will be able to get executed in the client or a session::
103+
104+
query = dsl_gql(
105+
DSLQuery(
106+
ds.Query.hero.select(
107+
ds.Character.id,
108+
ds.Character.name,
109+
ds.Character.friends.select(ds.Character.name),
110+
)
111+
)
112+
)
113+
114+
result = client.execute(query)
115+
116+
Arguments
117+
^^^^^^^^^
118+
119+
It is possible to add arguments to any field simply by calling it
120+
with the required arguments::
121+
122+
ds.Query.human(id="1000").select(ds.Human.name)
123+
124+
It can also be done using the :meth:`args <gql.dsl.DSLField.args>` method::
125+
126+
ds.Query.human.args(id="1000").select(ds.Human.name)
23127

24-
hero {
25-
id
26-
name
27-
friends {
28-
name
29-
}
128+
Aliases
129+
^^^^^^^
130+
131+
You can set an alias of a field using the :meth:`alias <gql.dsl.DSLField.alias>` method::
132+
133+
ds.Query.human.args(id=1000).alias("luke").select(ds.Character.name)
134+
135+
It is also possible to set the alias directly using keyword arguments of an operation::
136+
137+
DSLQuery(
138+
luke=ds.Query.human.args(id=1000).select(ds.Character.name)
139+
)
140+
141+
Or using keyword arguments in the :meth:`select <gql.dsl.DSLField.select>` method::
142+
143+
ds.Query.hero.select(
144+
my_name=ds.Character.name
145+
)
146+
147+
Mutations
148+
^^^^^^^^^
149+
150+
For the mutations, you need to start from root fields starting from :code:`ds.Mutation`
151+
then you need to create the GraphQL operation using the class
152+
:class:`DSLMutation <gql.dsl.DSLMutation>`. Example::
153+
154+
query = dsl_gql(
155+
DSLMutation(
156+
ds.Mutation.createReview.args(
157+
episode=6, review={"stars": 5, "commentary": "This is a great movie!"}
158+
).select(ds.Review.stars, ds.Review.commentary)
159+
)
160+
)
161+
162+
Subscriptions
163+
^^^^^^^^^^^^^
164+
165+
For the subscriptions, you need to start from root fields starting from :code:`ds.Subscription`
166+
then you need to create the GraphQL operation using the class
167+
:class:`DSLSubscription <gql.dsl.DSLSubscription>`. Example::
168+
169+
query = dsl_gql(
170+
DSLSubscription(
171+
ds.Subscription.reviewAdded(episode=6).select(ds.Review.stars, ds.Review.commentary)
172+
)
173+
)
174+
175+
Multiple fields in an operation
176+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
177+
178+
It is possible to create an operation with multiple fields::
179+
180+
DSLQuery(
181+
ds.Query.hero.select(ds.Character.name),
182+
hero_of_episode_5=ds.Query.hero(episode=5).select(ds.Character.name),
183+
)
184+
185+
Operation name
186+
^^^^^^^^^^^^^^
187+
188+
You can set the operation name of an operation using a keyword argument
189+
to :func:`dsl_gql <gql.dsl.dsl_gql>`::
190+
191+
query = dsl_gql(
192+
GetHeroName=DSLQuery(ds.Query.hero.select(ds.Character.name))
193+
)
194+
195+
will generate the request::
196+
197+
query GetHeroName {
198+
hero {
199+
name
200+
}
30201
}
31202

32-
.. warning::
203+
Multiple operations in a document
204+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
205+
206+
It is possible to create an Document with multiple operations::
207+
208+
query = dsl_gql(
209+
operation_name_1=DSLQuery( ... ),
210+
operation_name_2=DSLQuery( ... ),
211+
operation_name_3=DSLMutation( ... ),
212+
)
213+
214+
Executable examples
215+
-------------------
216+
217+
Async example
218+
^^^^^^^^^^^^^
219+
220+
.. literalinclude:: ../code_examples/aiohttp_async_dsl.py
221+
222+
Sync example
223+
^^^^^^^^^^^^^
224+
225+
.. literalinclude:: ../code_examples/requests_sync_dsl.py
33226

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ Advanced
55
:maxdepth: 2
66

77
async_advanced_usage
8+
logging
89
local_schema
910
dsl_module

docs/advanced/logging.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Logging
2+
=======
3+
4+
GQL use the python `logging`_ module.
5+
6+
In order to debug a problem, you can enable logging to see the messages exchanged between the client and the server.
7+
To do that, set the loglevel at **INFO** at the beginning of your code:
8+
9+
.. code-block:: python
10+
11+
import logging
12+
logging.basicConfig(level=logging.INFO)
13+
14+
For even more logs, you can set the loglevel at **DEBUG**:
15+
16+
.. code-block:: python
17+
18+
import logging
19+
logging.basicConfig(level=logging.DEBUG)
20+
21+
.. _logging: https://docs.python.org/3/howto/logging.html

0 commit comments

Comments
 (0)