Skip to content

Commit 2102a27

Browse files
authored
Merge branch 'master' into feature_dsl_allow_omitting_optional_arguments
2 parents 6be9fa8 + 5912f8f commit 2102a27

File tree

3 files changed

+62
-5
lines changed

3 files changed

+62
-5
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ result = client.execute(query)
8282
print(result)
8383
```
8484

85+
Executing the above code should output the following result:
86+
87+
```
88+
$ python basic_example.py
89+
{'continents': [{'code': 'AF', 'name': 'Africa'}, {'code': 'AN', 'name': 'Antarctica'}, {'code': 'AS', 'name': 'Asia'}, {'code': 'EU', 'name': 'Europe'}, {'code': 'NA', 'name': 'North America'}, {'code': 'OC', 'name': 'Oceania'}, {'code': 'SA', 'name': 'South America'}]}
90+
```
91+
8592
> **WARNING**: Please note that this basic example won't work if you have an asyncio event loop running. In some
8693
> python environments (as with Jupyter which uses IPython) an asyncio event loop is created for you. In that case you
8794
> should use instead the [async usage example](https://gql.readthedocs.io/en/latest/async/async_usage.html#async-usage).

gql/transport/aiohttp.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import json
55
import logging
66
from ssl import SSLContext
7-
from typing import Any, AsyncGenerator, Dict, Optional, Tuple, Type, Union
7+
from typing import Any, AsyncGenerator, Callable, Dict, Optional, Tuple, Type, Union
88

99
import aiohttp
1010
from aiohttp.client_exceptions import ClientResponseError
@@ -49,6 +49,7 @@ def __init__(
4949
ssl: Union[SSLContext, bool, Fingerprint] = False,
5050
timeout: Optional[int] = None,
5151
ssl_close_timeout: Optional[Union[int, float]] = 10,
52+
json_serialize: Callable = json.dumps,
5253
client_session_args: Optional[Dict[str, Any]] = None,
5354
) -> None:
5455
"""Initialize the transport with the given aiohttp parameters.
@@ -61,6 +62,8 @@ def __init__(
6162
:param ssl: ssl_context of the connection. Use ssl=False to disable encryption
6263
:param ssl_close_timeout: Timeout in seconds to wait for the ssl connection
6364
to close properly
65+
:param json_serialize: Json serializer callable.
66+
By default json.dumps() function
6467
:param client_session_args: Dict of extra args passed to
6568
`aiohttp.ClientSession`_
6669
@@ -77,6 +80,7 @@ def __init__(
7780
self.client_session_args = client_session_args
7881
self.session: Optional[aiohttp.ClientSession] = None
7982
self.response_headers: Optional[CIMultiDictProxy[str]]
83+
self.json_serialize: Callable = json_serialize
8084

8185
async def connect(self) -> None:
8286
"""Coroutine which will create an aiohttp ClientSession() as self.session.
@@ -96,6 +100,7 @@ async def connect(self) -> None:
96100
"auth": None
97101
if isinstance(self.auth, AppSyncAuthentication)
98102
else self.auth,
103+
"json_serialize": self.json_serialize,
99104
}
100105

101106
if self.timeout is not None:
@@ -248,14 +253,14 @@ async def execute(
248253
file_streams = {str(i): files[path] for i, path in enumerate(files)}
249254

250255
# Add the payload to the operations field
251-
operations_str = json.dumps(payload)
256+
operations_str = self.json_serialize(payload)
252257
log.debug("operations %s", operations_str)
253258
data.add_field(
254259
"operations", operations_str, content_type="application/json"
255260
)
256261

257262
# Add the file map field
258-
file_map_str = json.dumps(file_map)
263+
file_map_str = self.json_serialize(file_map)
259264
log.debug("file_map %s", file_map_str)
260265
data.add_field("map", file_map_str, content_type="application/json")
261266

@@ -270,7 +275,7 @@ async def execute(
270275
payload["variables"] = variable_values
271276

272277
if log.isEnabledFor(logging.INFO):
273-
log.info(">>> %s", json.dumps(payload))
278+
log.info(">>> %s", self.json_serialize(payload))
274279

275280
post_args = {"json": payload}
276281

@@ -281,7 +286,7 @@ async def execute(
281286
# Add headers for AppSync if requested
282287
if isinstance(self.auth, AppSyncAuthentication):
283288
post_args["headers"] = self.auth.get_headers(
284-
json.dumps(payload),
289+
self.json_serialize(payload),
285290
{"content-type": "application/json"},
286291
)
287292

tests/test_aiohttp.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,3 +1339,48 @@ async def handler(request):
13391339
assert expected_warning in caplog.text
13401340

13411341
await client.close_async()
1342+
1343+
1344+
@pytest.mark.asyncio
1345+
async def test_aiohttp_json_serializer(event_loop, aiohttp_server, caplog):
1346+
from aiohttp import web
1347+
from gql.transport.aiohttp import AIOHTTPTransport
1348+
1349+
async def handler(request):
1350+
1351+
request_text = await request.text()
1352+
print("Received on backend: " + request_text)
1353+
1354+
return web.Response(
1355+
text=query1_server_answer,
1356+
content_type="application/json",
1357+
)
1358+
1359+
app = web.Application()
1360+
app.router.add_route("POST", "/", handler)
1361+
server = await aiohttp_server(app)
1362+
1363+
url = server.make_url("/")
1364+
1365+
transport = AIOHTTPTransport(
1366+
url=url,
1367+
timeout=10,
1368+
json_serialize=lambda e: json.dumps(e, separators=(",", ":")),
1369+
)
1370+
1371+
async with Client(transport=transport) as session:
1372+
1373+
query = gql(query1_str)
1374+
1375+
# Execute query asynchronously
1376+
result = await session.execute(query)
1377+
1378+
continents = result["continents"]
1379+
1380+
africa = continents[0]
1381+
1382+
assert africa["code"] == "AF"
1383+
1384+
# Checking that there is no space after the colon in the log
1385+
expected_log = '"query":"query getContinents'
1386+
assert expected_log in caplog.text

0 commit comments

Comments
 (0)