Skip to content

Commit 84540ad

Browse files
committed
Merged in next branch.
2 parents d45fda0 + 9038808 commit 84540ad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+869
-598
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ target/
6262

6363
# IntelliJ
6464
.idea
65+
*.iml
6566

6667
# OS X
6768
.DS_Store

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ python:
66
- 3.4
77
- 3.5
88
- pypy
9-
cache: pip
109
before_install:
1110
- |
1211
if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
@@ -22,8 +21,8 @@ before_install:
2221
source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
2322
fi
2423
install:
25-
- pip install --cache-dir $HOME/.cache/pip pytest-cov pytest-mock coveralls flake8 isort==3.9.6 gevent==1.1b5 six>=1.10.0 promise>=0.4.2
26-
- pip install --cache-dir $HOME/.cache/pip pytest>=2.7.3 --upgrade
24+
- pip install pytest-cov pytest-mock coveralls flake8 isort==3.9.6 gevent==1.1b5 six>=1.10.0 promise>=0.4.2 pytest-benchmark
25+
- pip install pytest==2.9.2
2726
- pip install -e .
2827
script:
2928
- flake8

graphql/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
__GRAPHQL_SETUP__ = False
3434

3535

36-
VERSION = (0, 5, 3, 'final', 0)
36+
VERSION = (1, 0, 0, 'alpha', 0)
3737

3838
__version__ = get_version(VERSION)
3939

@@ -58,6 +58,7 @@
5858
GraphQLList,
5959
GraphQLNonNull,
6060
GraphQLField,
61+
GraphQLInputObjectField,
6162
GraphQLArgument,
6263

6364
# "Enum" of Type Kinds
@@ -135,6 +136,8 @@
135136
# Execute GraphQL queries.
136137
from .execution import ( # no import order
137138
execute,
139+
MiddlewareManager,
140+
middlewares
138141
)
139142

140143
# Validate GraphQL queries.
@@ -213,6 +216,7 @@
213216
'GraphQLList',
214217
'GraphQLNonNull',
215218
'GraphQLField',
219+
'GraphQLInputObjectField',
216220
'GraphQLArgument',
217221
'GraphQLObjectType',
218222
'GraphQLScalarType',
@@ -256,6 +260,8 @@
256260
'print_ast',
257261
'visit',
258262
'execute',
263+
'MiddlewareManager',
264+
'middlewares',
259265
'specified_rules',
260266
'validate',
261267
'GraphQLError',

graphql/execution/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121
from .executor import execute
2222
from .base import ExecutionResult
23+
from .middleware import middlewares, MiddlewareManager
2324

2425

25-
__all__ = ['execute', 'ExecutionResult']
26+
__all__ = ['execute', 'ExecutionResult', 'MiddlewareManager', 'middlewares']

graphql/execution/base.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from ..error import GraphQLError
33
from ..language import ast
4+
from ..pyutils.default_ordered_dict import DefaultOrderedDict
45
from ..type.definition import GraphQLInterfaceType, GraphQLUnionType
56
from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective
67
from ..type.introspection import (SchemaMetaFieldDef, TypeMetaFieldDef,
@@ -18,9 +19,9 @@ class ExecutionContext(object):
1819
and the fragments defined in the query document"""
1920

2021
__slots__ = 'schema', 'fragments', 'root_value', 'operation', 'variable_values', 'errors', 'context_value', \
21-
'argument_values_cache', 'executor'
22+
'argument_values_cache', 'executor', 'middleware_manager', '_subfields_cache'
2223

23-
def __init__(self, schema, document_ast, root_value, context_value, variable_values, operation_name, executor):
24+
def __init__(self, schema, document_ast, root_value, context_value, variable_values, operation_name, executor, middleware_manager):
2425
"""Constructs a ExecutionContext object from the arguments passed
2526
to execute, which we will pass throughout the other execution
2627
methods."""
@@ -63,6 +64,13 @@ def __init__(self, schema, document_ast, root_value, context_value, variable_val
6364
self.context_value = context_value
6465
self.argument_values_cache = {}
6566
self.executor = executor
67+
self.middleware_manager = middleware_manager
68+
self._subfields_cache = {}
69+
70+
def get_field_resolver(self, field_resolver):
71+
if not self.middleware_manager:
72+
return field_resolver
73+
return self.middleware_manager.get_field_resolver(field_resolver)
6674

6775
def get_argument_values(self, field_def, field_ast):
6876
k = field_def, field_ast
@@ -74,6 +82,21 @@ def get_argument_values(self, field_def, field_ast):
7482

7583
return result
7684

85+
def get_sub_fields(self, return_type, field_asts):
86+
k = return_type, tuple(field_asts)
87+
if k not in self._subfields_cache:
88+
subfield_asts = DefaultOrderedDict(list)
89+
visited_fragment_names = set()
90+
for field_ast in field_asts:
91+
selection_set = field_ast.selection_set
92+
if selection_set:
93+
subfield_asts = collect_fields(
94+
self, return_type, selection_set,
95+
subfield_asts, visited_fragment_names
96+
)
97+
self._subfields_cache[k] = subfield_asts
98+
return self._subfields_cache[k]
99+
77100

78101
class ExecutionResult(object):
79102
"""The result of execution. `data` is the result of executing the
@@ -228,7 +251,7 @@ def does_fragment_condition_match(ctx, fragment, type_):
228251
return True
229252

230253
conditional_type = type_from_ast(ctx.schema, type_condition_ast)
231-
if conditional_type == type_:
254+
if conditional_type.is_same_type(type_):
232255
return True
233256

234257
if isinstance(conditional_type, (GraphQLInterfaceType, GraphQLUnionType)):
@@ -245,6 +268,8 @@ def get_field_entry_key(node):
245268

246269

247270
class ResolveInfo(object):
271+
__slots__ = ('field_name', 'field_asts', 'return_type', 'parent_type',
272+
'schema', 'fragments', 'root_value', 'operation', 'variable_values')
248273

249274
def __init__(self, field_name, field_asts, return_type, parent_type,
250275
schema, fragments, root_value, operation, variable_values):
@@ -277,10 +302,10 @@ def get_field_def(schema, parent_type, field_name):
277302
are allowed, like on a Union. __schema could get automatically
278303
added to the query type, but that would require mutating type
279304
definitions, which would cause issues."""
280-
if field_name == SchemaMetaFieldDef.name and schema.get_query_type() == parent_type:
305+
if field_name == '__schema' and schema.get_query_type() == parent_type:
281306
return SchemaMetaFieldDef
282-
elif field_name == TypeMetaFieldDef.name and schema.get_query_type() == parent_type:
307+
elif field_name == '__type' and schema.get_query_type() == parent_type:
283308
return TypeMetaFieldDef
284-
elif field_name == TypeNameMetaFieldDef.name:
309+
elif field_name == '__typename':
285310
return TypeNameMetaFieldDef
286-
return parent_type.get_fields().get(field_name)
311+
return parent_type.fields.get(field_name)

graphql/execution/executor.py

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,40 @@
33
import logging
44
import sys
55

6-
from promise import Promise, is_thenable, promise_for_dict, promisify
6+
from promise import Promise, promise_for_dict, promisify
77

88
from ..error import GraphQLError, GraphQLLocatedError
99
from ..pyutils.default_ordered_dict import DefaultOrderedDict
10+
from ..pyutils.ordereddict import OrderedDict
1011
from ..type import (GraphQLEnumType, GraphQLInterfaceType, GraphQLList,
1112
GraphQLNonNull, GraphQLObjectType, GraphQLScalarType,
1213
GraphQLSchema, GraphQLUnionType)
1314
from .base import (ExecutionContext, ExecutionResult, ResolveInfo, Undefined,
1415
collect_fields, default_resolve_fn, get_field_def,
1516
get_operation_root_type)
1617
from .executors.sync import SyncExecutor
18+
from .middleware import MiddlewareManager
1719

1820
logger = logging.getLogger(__name__)
1921

2022

23+
def is_promise(obj):
24+
return type(obj) == Promise
25+
26+
2127
def execute(schema, document_ast, root_value=None, context_value=None,
2228
variable_values=None, operation_name=None, executor=None,
23-
return_promise=False):
29+
return_promise=False, middlewares=None):
2430
assert schema, 'Must provide schema'
2531
assert isinstance(schema, GraphQLSchema), (
2632
'Schema must be an instance of GraphQLSchema. Also ensure that there are ' +
2733
'not multiple versions of GraphQL installed in your node_modules directory.'
2834
)
35+
if middlewares:
36+
assert isinstance(middlewares, MiddlewareManager), (
37+
'middlewares have to be an instance'
38+
' of MiddlewareManager. Received "{}".'.format(middlewares)
39+
)
2940

3041
if executor is None:
3142
executor = SyncExecutor()
@@ -37,7 +48,8 @@ def execute(schema, document_ast, root_value=None, context_value=None,
3748
context_value,
3849
variable_values,
3950
operation_name,
40-
executor
51+
executor,
52+
middlewares
4153
)
4254

4355
def executor(resolve, reject):
@@ -85,7 +97,7 @@ def execute_field_callback(results, response_name):
8597
if result is Undefined:
8698
return results
8799

88-
if is_thenable(result):
100+
if is_promise(result):
89101
def collect_result(resolved_result):
90102
results[response_name] = resolved_result
91103
return results
@@ -104,15 +116,15 @@ def execute_field(prev_promise, response_name):
104116
def execute_fields(exe_context, parent_type, source_value, fields):
105117
contains_promise = False
106118

107-
final_results = collections.OrderedDict()
119+
final_results = OrderedDict()
108120

109121
for response_name, field_asts in fields.items():
110122
result = resolve_field(exe_context, parent_type, source_value, field_asts)
111123
if result is Undefined:
112124
continue
113125

114126
final_results[response_name] = result
115-
if is_thenable(result):
127+
if is_promise(result):
116128
contains_promise = True
117129

118130
if not contains_promise:
@@ -132,6 +144,9 @@ def resolve_field(exe_context, parent_type, source, field_asts):
132144
return_type = field_def.type
133145
resolve_fn = field_def.resolver or default_resolve_fn
134146

147+
# We wrap the resolve_fn from the middleware
148+
resolve_fn_middleware = exe_context.get_field_resolver(resolve_fn)
149+
135150
# Build a dict of arguments from the field.arguments AST, using the variables scope to
136151
# fulfill any variable references.
137152
args = exe_context.get_argument_values(field_def, field_ast)
@@ -156,7 +171,7 @@ def resolve_field(exe_context, parent_type, source, field_asts):
156171
)
157172

158173
executor = exe_context.executor
159-
result = resolve_or_error(resolve_fn, source, args, context, info, executor)
174+
result = resolve_or_error(resolve_fn_middleware, source, args, context, info, executor)
160175

161176
return complete_value_catching_error(
162177
exe_context,
@@ -188,7 +203,7 @@ def complete_value_catching_error(exe_context, return_type, field_asts, info, re
188203
# resolving a null value for this field if one is encountered.
189204
try:
190205
completed = complete_value(exe_context, return_type, field_asts, info, result)
191-
if is_thenable(completed):
206+
if is_promise(completed):
192207
def handle_error(error):
193208
exe_context.errors.append(error)
194209
return Promise.fulfilled(None)
@@ -222,7 +237,7 @@ def complete_value(exe_context, return_type, field_asts, info, result):
222237
"""
223238
# If field type is NonNull, complete for inner type, and throw field error if result is null.
224239

225-
if is_thenable(result):
240+
if is_promise(result):
226241
return promisify(result).then(
227242
lambda resolved: complete_value(
228243
exe_context,
@@ -238,16 +253,7 @@ def complete_value(exe_context, return_type, field_asts, info, result):
238253
raise GraphQLLocatedError(field_asts, original_error=result)
239254

240255
if isinstance(return_type, GraphQLNonNull):
241-
completed = complete_value(
242-
exe_context, return_type.of_type, field_asts, info, result
243-
)
244-
if completed is None:
245-
raise GraphQLError(
246-
'Cannot return null for non-nullable field {}.{}.'.format(info.parent_type, info.field_name),
247-
field_asts
248-
)
249-
250-
return completed
256+
return complete_nonnull_value(exe_context, return_type, field_asts, info, result)
251257

252258
# If result is null-like, return null.
253259
if result is None:
@@ -283,7 +289,7 @@ def complete_list_value(exe_context, return_type, field_asts, info, result):
283289
contains_promise = False
284290
for item in result:
285291
completed_item = complete_value_catching_error(exe_context, item_type, field_asts, info, item)
286-
if not contains_promise and is_thenable(completed_item):
292+
if not contains_promise and is_promise(completed_item):
287293
contains_promise = True
288294

289295
completed_results.append(completed_item)
@@ -295,15 +301,10 @@ def complete_leaf_value(return_type, result):
295301
"""
296302
Complete a Scalar or Enum by serializing to a valid value, returning null if serialization is not possible.
297303
"""
298-
serialize = getattr(return_type, 'serialize', None)
299-
assert serialize, 'Missing serialize method on type'
300-
301-
serialized_result = serialize(result)
302-
303-
if serialized_result is None:
304-
return None
304+
# serialize = getattr(return_type, 'serialize', None)
305+
# assert serialize, 'Missing serialize method on type'
305306

306-
return serialized_result
307+
return return_type.serialize(result)
307308

308309

309310
def complete_abstract_value(exe_context, return_type, field_asts, info, result):
@@ -360,14 +361,21 @@ def complete_object_value(exe_context, return_type, field_asts, info, result):
360361
)
361362

362363
# Collect sub-fields to execute to complete this value.
363-
subfield_asts = DefaultOrderedDict(list)
364-
visited_fragment_names = set()
365-
for field_ast in field_asts:
366-
selection_set = field_ast.selection_set
367-
if selection_set:
368-
subfield_asts = collect_fields(
369-
exe_context, return_type, selection_set,
370-
subfield_asts, visited_fragment_names
371-
)
372-
364+
subfield_asts = exe_context.get_sub_fields(return_type, field_asts)
373365
return execute_fields(exe_context, return_type, result, subfield_asts)
366+
367+
368+
def complete_nonnull_value(exe_context, return_type, field_asts, info, result):
369+
"""
370+
Complete a NonNull value by completing the inner type
371+
"""
372+
completed = complete_value(
373+
exe_context, return_type.of_type, field_asts, info, result
374+
)
375+
if completed is None:
376+
raise GraphQLError(
377+
'Cannot return null for non-nullable field {}.{}.'.format(info.parent_type, info.field_name),
378+
field_asts
379+
)
380+
381+
return completed

graphql/execution/executors/asyncio.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from asyncio import Future, get_event_loop, iscoroutine, wait
44

5+
from promise import promisify
6+
57
try:
68
from asyncio import ensure_future
79
except ImportError:
@@ -45,5 +47,5 @@ def execute(self, fn, *args, **kwargs):
4547
if isinstance(result, Future) or iscoroutine(result):
4648
future = ensure_future(result, loop=self.loop)
4749
self.futures.append(future)
48-
return future
50+
return promisify(future)
4951
return result

0 commit comments

Comments
 (0)