66from typing import TYPE_CHECKING , Any , cast
77
88from .error import GraphQLError
9- from .execution import ExecutionResult , Executor , Middleware , execute
10- from .language import Source , parse
9+ from .execution import ExecutionResult , Executor , Middleware
10+ from .harness import GraphQLHarness , default_harness
1111from .pyutils .is_awaitable import is_awaitable as default_is_awaitable
1212from .type import (
1313 GraphQLFieldResolver ,
2020 from collections .abc import AsyncIterable , Awaitable , Callable , Collection
2121 from typing import TypeGuard
2222
23+ from .language import DocumentNode , Source
2324 from .pyutils import AbortSignal , AwaitableOrValue
2425 from .validation import ASTValidationRule
2526
@@ -46,6 +47,7 @@ async def graphql( # noqa: PLR0913
4647 experimental_fragment_arguments : bool = False ,
4748 rules : Collection [type [ASTValidationRule ]] | None = None ,
4849 max_errors : int | None = None ,
50+ harness : GraphQLHarness = default_harness ,
4951) -> ExecutionResult :
5052 """Execute a GraphQL operation asynchronously.
5153
@@ -109,6 +111,9 @@ async def graphql( # noqa: PLR0913
109111 If not provided, the specified rules are used.
110112 :arg max_errors:
111113 The maximum number of validation errors to report before stopping.
114+ :arg harness:
115+ A custom set of parse/validate/execute/subscribe functions to use when
116+ fulfilling the operation. Defaults to ``default_harness``.
112117 """
113118 # Always return asynchronously for a consistent API.
114119 result = graphql_impl (
@@ -131,6 +136,7 @@ async def graphql( # noqa: PLR0913
131136 experimental_fragment_arguments ,
132137 rules ,
133138 max_errors ,
139+ harness ,
134140 )
135141
136142 if default_is_awaitable (result ):
@@ -168,6 +174,7 @@ def graphql_sync( # noqa: PLR0913
168174 experimental_fragment_arguments : bool = False ,
169175 rules : Collection [type [ASTValidationRule ]] | None = None ,
170176 max_errors : int | None = None ,
177+ harness : GraphQLHarness = default_harness ,
171178) -> ExecutionResult :
172179 """Execute a GraphQL operation synchronously.
173180
@@ -204,6 +211,7 @@ def graphql_sync( # noqa: PLR0913
204211 experimental_fragment_arguments ,
205212 rules ,
206213 max_errors ,
214+ harness ,
207215 )
208216
209217 # Assert that the execution was synchronous.
@@ -235,15 +243,68 @@ def graphql_impl( # noqa: PLR0913
235243 experimental_fragment_arguments : bool = False ,
236244 rules : Collection [type [ASTValidationRule ]] | None = None ,
237245 max_errors : int | None = None ,
246+ harness : GraphQLHarness = default_harness ,
238247) -> AwaitableOrValue [ExecutionResult ]:
239248 """Execute a query, return asynchronously only if necessary."""
240249 # Validate Schema
241250 if schema_validation_errors := validate_schema (schema ):
242251 return ExecutionResult (data = None , errors = schema_validation_errors )
243252
253+ def check_validation_and_execute (
254+ validation_errors : list [GraphQLError ], document : DocumentNode
255+ ) -> AwaitableOrValue [ExecutionResult ]:
256+ if validation_errors :
257+ return ExecutionResult (data = None , errors = validation_errors )
258+
259+ # Execute
260+ return harness .execute (
261+ schema ,
262+ document ,
263+ root_value ,
264+ context_value ,
265+ variable_values ,
266+ operation_name ,
267+ field_resolver ,
268+ type_resolver ,
269+ None ,
270+ 50 ,
271+ False ,
272+ middleware ,
273+ executor_class ,
274+ is_awaitable ,
275+ is_async_iterable ,
276+ hide_suggestions = hide_suggestions ,
277+ abort_signal = abort_signal ,
278+ )
279+
280+ def validate_and_execute (
281+ document : DocumentNode ,
282+ ) -> AwaitableOrValue [ExecutionResult ]:
283+ # Validate
284+ validation_result = harness .validate (
285+ schema , document , rules , max_errors , hide_suggestions = hide_suggestions
286+ )
287+
288+ if default_is_awaitable (validation_result ):
289+
290+ async def await_validation () -> ExecutionResult :
291+ validation_errors = await cast (
292+ "Awaitable[list[GraphQLError]]" , validation_result
293+ )
294+ result = check_validation_and_execute (validation_errors , document )
295+ if default_is_awaitable (result ):
296+ return await cast ("Awaitable[ExecutionResult]" , result )
297+ return cast ("ExecutionResult" , result )
298+
299+ return await_validation ()
300+
301+ return check_validation_and_execute (
302+ cast ("list[GraphQLError]" , validation_result ), document
303+ )
304+
244305 # Parse
245306 try :
246- document = parse (
307+ document = harness . parse (
247308 source ,
248309 no_location = no_location ,
249310 max_tokens = max_tokens ,
@@ -252,31 +313,18 @@ def graphql_impl( # noqa: PLR0913
252313 except GraphQLError as error :
253314 return ExecutionResult (data = None , errors = [error ])
254315
255- # Validate
256- from .validation import validate
316+ if default_is_awaitable (document ):
257317
258- if validation_errors := validate (
259- schema , document , rules , max_errors , hide_suggestions = hide_suggestions
260- ):
261- return ExecutionResult (data = None , errors = validation_errors )
318+ async def await_document () -> ExecutionResult :
319+ try :
320+ resolved_document = await cast ("Awaitable[DocumentNode]" , document )
321+ except GraphQLError as error :
322+ return ExecutionResult (data = None , errors = [error ])
323+ result = validate_and_execute (resolved_document )
324+ if default_is_awaitable (result ):
325+ return await cast ("Awaitable[ExecutionResult]" , result )
326+ return cast ("ExecutionResult" , result )
262327
263- # Execute
264- return execute (
265- schema ,
266- document ,
267- root_value ,
268- context_value ,
269- variable_values ,
270- operation_name ,
271- field_resolver ,
272- type_resolver ,
273- None ,
274- 50 ,
275- False ,
276- middleware ,
277- executor_class ,
278- is_awaitable ,
279- is_async_iterable ,
280- hide_suggestions = hide_suggestions ,
281- abort_signal = abort_signal ,
282- )
328+ return await_document ()
329+
330+ return validate_and_execute (cast ("DocumentNode" , document ))
0 commit comments