Skip to content

Commit ad228f0

Browse files
committed
Add way to get/set the default executor.
The default executor is the one that is used by default in the `graphql` and `execute` functions. This commit provides a way to override that executor to one configured to your liking. Additionally, since we are now doing that, the schema must be now passed to the `Executor.execute()` as the first argument, rather than as the first argument to `Executor()`.
1 parent e52093e commit ad228f0

File tree

7 files changed

+86
-47
lines changed

7 files changed

+86
-47
lines changed

graphql/core/execution/__init__.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,32 @@ def execute(schema, root, ast, operation_name='', args=None):
2828
"""
2929
Executes an AST synchronously. Assumes that the AST is already validated.
3030
"""
31-
e = Executor(schema, [SynchronousExecutionMiddleware()])
32-
return e.execute(ast, root, args, operation_name, validate_ast=False)
31+
return get_default_executor().execute(schema, ast, root, args, operation_name, validate_ast=False)
3332

3433

35-
__all__ = ['ExecutionResult', 'Executor', 'execute']
34+
_default_executor = None
35+
36+
37+
def get_default_executor():
38+
"""
39+
Gets the default executor to be used in the `execute` function above.
40+
"""
41+
global _default_executor
42+
if _default_executor is None:
43+
_default_executor = Executor([SynchronousExecutionMiddleware()])
44+
45+
return _default_executor
46+
47+
48+
def set_default_executor(executor):
49+
"""
50+
Sets the default executor to be used in the `execute` function above.
51+
52+
If passed `None` will reset to the original default synchronous executor.
53+
"""
54+
assert isinstance(executor, Executor) or executor is None
55+
global _default_executor
56+
_default_executor = executor
57+
58+
59+
__all__ = ['ExecutionResult', 'Executor', 'execute', 'get_default_executor', 'set_default_executor']

graphql/core/execution/executor.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@
1515

1616

1717
class Executor(object):
18-
def __init__(self, schema, execution_middlewares=None, default_resolver=default_resolve_fn):
18+
def __init__(self, execution_middlewares=None, default_resolver=default_resolve_fn):
1919
self.execution_middlewares = execution_middlewares or []
2020
self.default_resolve_fn = default_resolver
21-
self.schema = schema
2221

23-
def execute(self, request='', root=None, args=None, operation_name=None, request_context=None,
22+
def execute(self, schema, request='', root=None, args=None, operation_name=None, request_context=None,
2423
execute_serially=False, validate_ast=True):
2524

2625
curried_execution_function = functools.partial(
2726
self._execute,
27+
schema,
2828
request,
2929
root,
3030
args,
@@ -40,31 +40,32 @@ def execute(self, request='', root=None, args=None, operation_name=None, request
4040

4141
return curried_execution_function()
4242

43-
def _execute(self, request, root, args, operation_name, request_context, execute_serially, validate_ast):
43+
def _execute(self, schema, request, root, args, operation_name, request_context, execute_serially, validate_ast):
4444
if not isinstance(request, ast.Document):
4545
if not isinstance(request, Source):
4646
request = Source(request, 'GraphQL request')
4747

4848
request = parse(request)
4949

5050
if validate_ast:
51-
validation_errors = validate(self.schema, request)
51+
validation_errors = validate(schema, request)
5252
if validation_errors:
5353
return succeed(ExecutionResult(
5454
errors=validation_errors,
5555
invalid=True,
5656
))
5757

5858
return self._execute_graphql_query(
59+
schema,
5960
root or object(),
6061
request,
6162
operation_name,
6263
args or {},
6364
request_context or {},
6465
execute_serially)
6566

66-
def _execute_graphql_query(self, root, ast, operation_name, args, request_context, execute_serially=False):
67-
ctx = ExecutionContext(self.schema, root, ast, operation_name, args, request_context)
67+
def _execute_graphql_query(self, schema, root, ast, operation_name, args, request_context, execute_serially=False):
68+
ctx = ExecutionContext(schema, root, ast, operation_name, args, request_context)
6869

6970
return defer(self._execute_operation, ctx, root, ctx.operation, execute_serially) \
7071
.add_errback(

tests/core_execution/test_concurrent_executor.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ def deeper(self):
112112
})
113113

114114
schema = GraphQLSchema(query=DataType)
115-
executor = Executor(schema)
115+
executor = Executor()
116116

117117
def handle_result(result):
118118
assert not result.errors
119119
assert result.data == expected
120120

121-
raise_callback_results(executor.execute(doc, Data(), {'size': 100}, 'Example'), handle_result)
122-
raise_callback_results(executor.execute(doc, Data(), {'size': 100}, 'Example', execute_serially=True),
121+
raise_callback_results(executor.execute(schema, doc, Data(), {'size': 100}, 'Example'), handle_result)
122+
raise_callback_results(executor.execute(schema, doc, Data(), {'size': 100}, 'Example', execute_serially=True),
123123
handle_result)
124124

125125

@@ -142,9 +142,9 @@ def notPromise(self):
142142
}
143143
'''
144144
schema = GraphQLSchema(query=DataType)
145-
executor = Executor(schema, [SynchronousExecutionMiddleware()])
145+
executor = Executor([SynchronousExecutionMiddleware()])
146146

147-
result = executor.execute(doc, Data(), operation_name='Example')
147+
result = executor.execute(schema, doc, Data(), operation_name='Example')
148148
assert not isinstance(result, Deferred)
149149
assert result.data == {"promise": None, 'notPromise': 'i should work'}
150150
formatted_errors = list(map(format_error, result.errors))
@@ -172,9 +172,9 @@ def notPromise(self):
172172
}
173173
'''
174174
schema = GraphQLSchema(query=DataType)
175-
executor = Executor(schema, [SynchronousExecutionMiddleware()])
175+
executor = Executor([SynchronousExecutionMiddleware()])
176176

177-
result = executor.execute(doc, Data(), operation_name='Example')
177+
result = executor.execute(schema, doc, Data(), operation_name='Example')
178178
assert not isinstance(result, Deferred)
179179
assert result.data is None
180180
formatted_errors = list(map(format_error, result.errors))
@@ -202,9 +202,9 @@ def notPromise(self):
202202
}
203203
'''
204204
schema = GraphQLSchema(query=DataType)
205-
executor = Executor(schema)
205+
executor = Executor()
206206

207-
result = executor.execute(doc, Data(), operation_name='Example')
207+
result = executor.execute(schema, doc, Data(), operation_name='Example')
208208
assert result.called
209209
result = result.result
210210
assert result.data is None
@@ -227,9 +227,9 @@ def promise(self):
227227
}
228228
'''
229229
schema = GraphQLSchema(query=DataType)
230-
executor = Executor(schema, [SynchronousExecutionMiddleware()])
230+
executor = Executor([SynchronousExecutionMiddleware()])
231231

232-
result = executor.execute(doc, Data(), operation_name='Example')
232+
result = executor.execute(schema, doc, Data(), operation_name='Example')
233233
assert not isinstance(result, Deferred)
234234
assert result.data == {"promise": 'I should work'}
235235
assert not result.errors
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from graphql.core.execution import get_default_executor, set_default_executor, Executor
2+
3+
4+
def test_get_and_set_default_executor():
5+
e1 = get_default_executor()
6+
e2 = get_default_executor()
7+
assert e1 is e2
8+
9+
new_executor = Executor()
10+
11+
set_default_executor(new_executor)
12+
assert get_default_executor() is new_executor
13+
14+
set_default_executor(None)
15+
assert get_default_executor() is not e1
16+
assert get_default_executor() is not new_executor

tests/core_execution/test_gevent.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ def resolver_2(context, *_):
3131
'b': GraphQLField(GraphQLString, resolver=resolver_2)
3232
})
3333

34-
executor = Executor(GraphQLSchema(Type), [GeventExecutionMiddleware()])
35-
result = executor.execute(doc)
34+
executor = Executor([GeventExecutionMiddleware()])
35+
result = executor.execute(GraphQLSchema(Type), doc)
3636
assert not result.errors
3737
assert result.data == {'a': 'hey', 'b': 'hey2'}
3838

@@ -55,8 +55,8 @@ def resolver_2(context, *_):
5555
'b': GraphQLField(GraphQLString, resolver=resolver_2)
5656
})
5757

58-
executor = Executor(GraphQLSchema(Type), [GeventExecutionMiddleware()])
59-
result = executor.execute(doc)
58+
executor = Executor([GeventExecutionMiddleware()])
59+
result = executor.execute(GraphQLSchema(Type), doc)
6060
formatted_errors = list(map(format_error, result.errors))
6161
assert formatted_errors == [{'locations': [{'line': 1, 'column': 20}], 'message': 'resolver_2 failed!'}]
6262
assert result.data == {'a': 'hey', 'b': None}

tests/core_execution/utils.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
from graphql.core.pyutils.defer import Deferred, DeferredException
1+
from graphql.core.pyutils.defer import Deferred, DeferredException, _passthrough
22

33

44
class RaisingDeferred(Deferred):
55
def _next(self):
66
"""Process the next callback."""
77
if self._running or self.paused:
88
return
9+
910
while self.callbacks:
1011
# Get the next callback pair
1112
next_pair = self.callbacks.pop(0)
1213
# Continue with the errback if the last result was an exception
1314
callback, args, kwargs = next_pair[isinstance(self.result,
1415
DeferredException)]
15-
try:
16-
self.result = callback(self.result, *args, **kwargs)
17-
except:
18-
self.result = DeferredException()
19-
finally:
20-
self._running = False
21-
22-
if isinstance(self.result, Deferred):
23-
# If a Deferred was returned add this deferred as callbacks to
24-
# the returned one. As a result the processing of this Deferred
25-
# will be paused until all callbacks of the returned Deferred
26-
# have been performed
27-
self.result.add_callbacks(self._continue, self._continue)
28-
self.paused == True
29-
break
16+
17+
if callback is not _passthrough:
18+
self._running = True
19+
try:
20+
self.result = callback(self.result, *args, **kwargs)
21+
22+
except:
23+
self.result = DeferredException()
24+
25+
finally:
26+
self._running = False
27+
28+
if isinstance(self.result, Exception):
29+
self.result = DeferredException(self.result)
3030

3131
if isinstance(self.result, DeferredException):
3232
# Print the exception to stderr and stop if there aren't any
@@ -39,5 +39,3 @@ def raise_callback_results(deferred, callback):
3939
d.add_callback(lambda r: r)
4040
d.callback(deferred)
4141
d.add_callback(callback)
42-
43-

tests_py35/core_execution/test_asyncio_executor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ async def resolver_2(context, *_):
3838
'b': GraphQLField(GraphQLString, resolver=resolver_2)
3939
})
4040

41-
executor = Executor(GraphQLSchema(Type), [AsyncioExecutionMiddleware()])
42-
result = await executor.execute(doc)
41+
executor = Executor([AsyncioExecutionMiddleware()])
42+
result = await executor.execute(GraphQLSchema(Type), doc)
4343
assert not result.errors
4444
assert result.data == {'a': 'hey', 'b': 'hey2'}
4545

@@ -60,8 +60,8 @@ async def resolver_2(context, *_):
6060
'b': GraphQLField(GraphQLString, resolver=resolver_2)
6161
})
6262

63-
executor = Executor(GraphQLSchema(Type), [AsyncioExecutionMiddleware()])
64-
result = await executor.execute(doc)
63+
executor = Executor([AsyncioExecutionMiddleware()])
64+
result = await executor.execute(GraphQLSchema(Type), doc)
6565
formatted_errors = list(map(format_error, result.errors))
6666
assert formatted_errors == [{'locations': [{'line': 1, 'column': 20}], 'message': 'resolver_2 failed!'}]
6767
assert result.data == {'a': 'hey', 'b': None}

0 commit comments

Comments
 (0)