-
-
Notifications
You must be signed in to change notification settings - Fork 138
/
Copy pathgraphql.py
205 lines (178 loc) · 6.89 KB
/
graphql.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
"""Execute a GraphQL operation"""
from __future__ import annotations
from asyncio import ensure_future
from typing import Any, Awaitable, Callable, cast
from .error import GraphQLError
from .execution import ExecutionContext, ExecutionResult, Middleware, execute
from .language import Source, parse
from .pyutils import AwaitableOrValue
from .pyutils import is_awaitable as default_is_awaitable
from .type import (
GraphQLFieldResolver,
GraphQLSchema,
GraphQLTypeResolver,
validate_schema,
)
__all__ = ["graphql", "graphql_sync"]
async def graphql(
schema: GraphQLSchema,
source: str | Source,
root_value: Any = None,
context_value: Any = None,
variable_values: dict[str, Any] | None = None,
operation_name: str | None = None,
field_resolver: GraphQLFieldResolver | None = None,
type_resolver: GraphQLTypeResolver | None = None,
middleware: Middleware | None = None,
execution_context_class: type[ExecutionContext] | None = None,
is_awaitable: Callable[[Any], bool] | None = None,
) -> ExecutionResult:
"""Execute a GraphQL operation asynchronously.
This is the primary entry point function for fulfilling GraphQL operations by
parsing, validating, and executing a GraphQL document along side a GraphQL schema.
More sophisticated GraphQL servers, such as those which persist queries, may wish
to separate the validation and execution phases to a static time tooling step,
and a server runtime step.
This function does not support incremental delivery (`@defer` and `@stream`).
Accepts the following arguments:
:arg schema:
The GraphQL type system to use when validating and executing a query.
:arg source:
A GraphQL language formatted string representing the requested operation.
:arg root_value:
The value provided as the first argument to resolver functions on the top level
type (e.g. the query object type).
:arg context_value:
The context value is provided as an attribute of the second argument
(the resolve info) to resolver functions. It is used to pass shared information
useful at any point during query execution, for example the currently logged in
user and connections to databases or other services.
:arg variable_values:
A mapping of variable name to runtime value to use for all variables defined
in the request string.
:arg operation_name:
The name of the operation to use if request string contains multiple possible
operations. Can be omitted if request string contains only one operation.
:arg field_resolver:
A resolver function to use when one is not provided by the schema.
If not provided, the default field resolver is used (which looks for a value
or method on the source value with the field's name).
:arg type_resolver:
A type resolver function to use when none is provided by the schema.
If not provided, the default type resolver is used (which looks for a
``__typename`` field or alternatively calls the
:meth:`~graphql.type.GraphQLObjectType.is_type_of` method).
:arg middleware:
The middleware to wrap the resolvers with
:arg execution_context_class:
The execution context class to use to build the context
:arg is_awaitable:
The predicate to be used for checking whether values are awaitable
"""
# Always return asynchronously for a consistent API.
result = graphql_impl(
schema,
source,
root_value,
context_value,
variable_values,
operation_name,
field_resolver,
type_resolver,
middleware,
execution_context_class,
is_awaitable,
)
if default_is_awaitable(result):
return await cast(Awaitable[ExecutionResult], result)
return cast(ExecutionResult, result)
def assume_not_awaitable(_value: Any) -> bool:
"""Replacement for is_awaitable if everything is assumed to be synchronous."""
return False
def graphql_sync(
schema: GraphQLSchema,
source: str | Source,
root_value: Any = None,
context_value: Any = None,
variable_values: dict[str, Any] | None = None,
operation_name: str | None = None,
field_resolver: GraphQLFieldResolver | None = None,
type_resolver: GraphQLTypeResolver | None = None,
middleware: Middleware | None = None,
execution_context_class: type[ExecutionContext] | None = None,
check_sync: bool = False,
) -> ExecutionResult:
"""Execute a GraphQL operation synchronously.
The graphql_sync function also fulfills GraphQL operations by parsing, validating,
and executing a GraphQL document along side a GraphQL schema. However, it guarantees
to complete synchronously (or throw an error) assuming that all field resolvers
are also synchronous.
Set check_sync to True to still run checks that no awaitable values are returned.
"""
is_awaitable = (
check_sync
if callable(check_sync)
else (None if check_sync else assume_not_awaitable)
)
result = graphql_impl(
schema,
source,
root_value,
context_value,
variable_values,
operation_name,
field_resolver,
type_resolver,
middleware,
execution_context_class,
is_awaitable,
)
# Assert that the execution was synchronous.
if default_is_awaitable(result):
ensure_future(cast(Awaitable[ExecutionResult], result)).cancel()
msg = "GraphQL execution failed to complete synchronously."
raise RuntimeError(msg)
return cast(ExecutionResult, result)
def graphql_impl(
schema: GraphQLSchema,
source: str | Source,
root_value: Any,
context_value: Any,
variable_values: dict[str, Any] | None,
operation_name: str | None,
field_resolver: GraphQLFieldResolver | None,
type_resolver: GraphQLTypeResolver | None,
middleware: Middleware | None,
execution_context_class: type[ExecutionContext] | None,
is_awaitable: Callable[[Any], bool] | None,
) -> AwaitableOrValue[ExecutionResult]:
"""Execute a query, return asynchronously only if necessary."""
# Validate Schema
schema_validation_errors = validate_schema(schema)
if schema_validation_errors:
return ExecutionResult(data=None, errors=schema_validation_errors)
# Parse
try:
document = parse(source)
except GraphQLError as error:
return ExecutionResult(data=None, errors=[error])
# Validate
from .validation import validate
validation_errors = validate(schema, document)
if validation_errors:
return ExecutionResult(data=None, errors=validation_errors)
# Execute
return execute(
schema,
document,
root_value,
context_value,
variable_values,
operation_name,
field_resolver,
type_resolver,
None,
middleware,
execution_context_class,
is_awaitable,
)