1
1
import asyncio
2
+ from inspect import isawaitable
2
3
from typing import Any , AsyncGenerator , Dict , Generator , Optional , Union , cast
3
4
4
5
from graphql import (
@@ -35,11 +36,6 @@ def __init__(
35
36
assert (
36
37
not schema
37
38
), "Cant fetch the schema from transport if is already provided"
38
- if isinstance (transport , Transport ):
39
- # For sync transports, we fetch the schema directly
40
- execution_result = transport .execute (parse (get_introspection_query ()))
41
- execution_result = cast (ExecutionResult , execution_result )
42
- introspection = execution_result .data
43
39
if introspection :
44
40
assert not schema , "Cant provide introspection and schema at the same time"
45
41
schema = build_client_schema (introspection )
@@ -68,6 +64,10 @@ def __init__(
68
64
# Enforced timeout of the execute function
69
65
self .execute_timeout = execute_timeout
70
66
67
+ if isinstance (transport , Transport ) and fetch_schema_from_transport :
68
+ with self as session :
69
+ session .fetch_schema ()
70
+
71
71
def validate (self , document ):
72
72
if not self .schema :
73
73
raise Exception (
@@ -77,6 +77,10 @@ def validate(self, document):
77
77
if validation_errors :
78
78
raise validation_errors [0 ]
79
79
80
+ def execute_sync (self , document : DocumentNode , * args , ** kwargs ) -> Dict :
81
+ with self as session :
82
+ return session .execute (document , * args , ** kwargs )
83
+
80
84
async def execute_async (self , document : DocumentNode , * args , ** kwargs ) -> Dict :
81
85
async with self as session :
82
86
return await session .execute (document , * args , ** kwargs )
@@ -107,22 +111,7 @@ def execute(self, document: DocumentNode, *args, **kwargs) -> Dict:
107
111
return data
108
112
109
113
else : # Sync transports
110
-
111
- if self .schema :
112
- self .validate (document )
113
-
114
- assert self .transport is not None , "Cannot execute without a transport"
115
-
116
- result = self .transport .execute (document , * args , ** kwargs )
117
-
118
- if result .errors :
119
- raise TransportQueryError (str (result .errors [0 ]))
120
-
121
- assert (
122
- result .data is not None
123
- ), "Transport returned an ExecutionResult without data or errors"
124
-
125
- return result .data
114
+ return self .execute_sync (document , * args , ** kwargs )
126
115
127
116
async def subscribe_async (
128
117
self , document : DocumentNode , * args , ** kwargs
@@ -170,30 +159,72 @@ async def __aenter__(self):
170
159
await self .transport .connect ()
171
160
172
161
if not hasattr (self , "session" ):
173
- self .session = ClientSession (client = self )
162
+ self .session = AsyncClientSession (client = self )
174
163
175
164
return self .session
176
165
177
166
async def __aexit__ (self , exc_type , exc , tb ):
178
167
179
168
await self .transport .close ()
180
169
181
- def close (self ):
182
- """Close the client and it's underlying transport (only for Sync transports)"""
183
- if not isinstance (self .transport , AsyncTransport ):
184
- self .transport .close ()
185
-
186
170
def __enter__ (self ):
171
+
187
172
assert not isinstance (
188
173
self .transport , AsyncTransport
189
174
), "Only a sync transport can be use. Use 'async with Client(...)' instead"
190
- return self
175
+
176
+ self .transport .connect ()
177
+
178
+ if not hasattr (self , "session" ):
179
+ self .session = SyncClientSession (client = self )
180
+
181
+ return self .session
191
182
192
183
def __exit__ (self , * args ):
193
- self .close ()
184
+ self .transport .close ()
185
+
186
+
187
+ class SyncClientSession :
188
+ """An instance of this class is created when using 'with' on the client.
189
+
190
+ It contains the sync method execute to send queries
191
+ with the sync transports.
192
+ """
193
+
194
+ def __init__ (self , client : Client ):
195
+ self .client = client
196
+
197
+ def execute (self , document : DocumentNode , * args , ** kwargs ) -> Dict :
198
+
199
+ # Validate document
200
+ if self .client .schema :
201
+ self .client .validate (document )
202
+
203
+ result = self .transport .execute (document , * args , ** kwargs )
204
+
205
+ assert not isawaitable (result ), "Transport returned an awaitable result."
206
+ result = cast (ExecutionResult , result )
207
+
208
+ if result .errors :
209
+ raise TransportQueryError (str (result .errors [0 ]))
210
+
211
+ assert (
212
+ result .data is not None
213
+ ), "Transport returned an ExecutionResult without data or errors"
214
+
215
+ return result .data
216
+
217
+ def fetch_schema (self ) -> None :
218
+ execution_result = self .transport .execute (parse (get_introspection_query ()))
219
+ self .client .introspection = execution_result .data
220
+ self .client .schema = build_client_schema (self .client .introspection )
221
+
222
+ @property
223
+ def transport (self ):
224
+ return self .client .transport
194
225
195
226
196
- class ClientSession :
227
+ class AsyncClientSession :
197
228
"""An instance of this class is created when using 'async with' on the client.
198
229
199
230
It contains the async methods (execute, subscribe) to send queries
@@ -203,7 +234,7 @@ class ClientSession:
203
234
def __init__ (self , client : Client ):
204
235
self .client = client
205
236
206
- async def validate (self , document : DocumentNode ):
237
+ async def fetch_and_validate (self , document : DocumentNode ):
207
238
"""Fetch schema from transport if needed and validate document.
208
239
209
240
If no schema is present, the validation will be skipped.
@@ -222,7 +253,7 @@ async def subscribe(
222
253
) -> AsyncGenerator [Dict , None ]:
223
254
224
255
# Fetch schema from transport if needed and validate document if possible
225
- await self .validate (document )
256
+ await self .fetch_and_validate (document )
226
257
227
258
# Subscribe to the transport and yield data or raise error
228
259
self ._generator : AsyncGenerator [
@@ -243,7 +274,7 @@ async def subscribe(
243
274
async def execute (self , document : DocumentNode , * args , ** kwargs ) -> Dict :
244
275
245
276
# Fetch schema from transport if needed and validate document if possible
246
- await self .validate (document )
277
+ await self .fetch_and_validate (document )
247
278
248
279
# Execute the query with the transport with a timeout
249
280
result = await asyncio .wait_for (
0 commit comments