Skip to content

Commit b48b374

Browse files
committed
First phase of list working
1 parent ea4463e commit b48b374

File tree

4 files changed

+723
-407
lines changed

4 files changed

+723
-407
lines changed

graphql/execution/querybuilder/resolver.py

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,15 @@ def is_promise(value):
1717
return type(value) == Promise
1818

1919

20-
def on_complete_resolver(catch_error, __func, exe_context, info, __resolver, *args, **kwargs):
20+
def on_complete_resolver(on_error, __func, exe_context, info, __resolver, *args, **kwargs):
2121
try:
22+
# print 'on_complete_resolver'
2223
result = __resolver(*args, **kwargs)
24+
# print 'result', result
2325
except Exception, e:
24-
# print e, __resolver, info.field_name, traceback.print_tb(sys.exc_info()[2])
25-
error = GraphQLLocatedError(info.field_asts, original_error=e)
26-
if catch_error:
27-
exe_context.errors.append(error)
28-
return None
29-
raise error
26+
return on_error(e)
3027

3128
if is_promise(result):
32-
def on_error(e):
33-
error = GraphQLLocatedError(info.field_asts, original_error=e)
34-
if catch_error:
35-
exe_context.errors.append(error)
36-
return None
37-
raise error
38-
3929
return result.then(__func, on_error)
4030
return __func(result)
4131

@@ -49,27 +39,45 @@ def complete_list_value(inner_resolver, exe_context, info, result):
4939
'for field {}.{}.').format(info.parent_type, info.field_name)
5040

5141
completed_results = []
42+
contains_promise = False
5243
for item in result:
5344
completed_item = inner_resolver(item)
45+
if not contains_promise and is_promise(completed_item):
46+
contains_promise = True
47+
5448
completed_results.append(completed_item)
5549

56-
return completed_results
50+
return Promise.all(completed_results) if contains_promise else completed_results
51+
5752

5853

5954
def complete_nonnull_value(exe_context, info, result):
55+
print 'complete_nonnull_value', result, result is None
6056
if result is None:
61-
field_asts = 'TODO'
57+
print 'b'
6258
raise GraphQLError(
6359
'Cannot return null for non-nullable field {}.{}.'.format(info.parent_type, info.field_name),
6460
info.field_asts
6561
)
6662
return result
6763

6864

69-
def complete_object_value(fragment_resolve, result):
65+
def complete_leaf_value(serialize, result):
66+
if result is None:
67+
return None
68+
return serialize(result)
69+
70+
71+
def complete_object_value(fragment_resolve, exe_context, on_error, result):
7072
if result is None:
7173
return None
72-
return fragment_resolve(result)
74+
try:
75+
result = fragment_resolve(result)
76+
if is_promise(result):
77+
return result.catch(on_error)
78+
return result
79+
except Exception, e:
80+
on_error(e)
7381

7482

7583
def field_resolver(field, fragment=None, exe_context=None, info=None):
@@ -110,23 +118,39 @@ def type_resolver(return_type, resolver, fragment=None, exe_context=None, info=N
110118
raise Exception("The resolver have to be created for a fragment")
111119

112120

121+
def on_error(exe_context, info, catch_error, e):
122+
error = e
123+
if not isinstance(e, (GraphQLLocatedError, GraphQLError)):
124+
error = GraphQLLocatedError(info.field_asts, original_error=e)
125+
if catch_error:
126+
exe_context.errors.append(error)
127+
return None
128+
raise error
129+
130+
113131
def type_resolver_type(return_type, resolver, fragment, exe_context, info, catch_error):
114-
complete_object_value_resolve = partial(complete_object_value, fragment.resolve)
115-
return partial(on_complete_resolver, catch_error, complete_object_value_resolve, exe_context, info, resolver)
132+
on_complete_type_error = partial(on_error, exe_context, info, catch_error)
133+
complete_object_value_resolve = partial(complete_object_value, fragment.resolve, exe_context, on_complete_type_error)
134+
on_resolve_error = partial(on_error, exe_context, info, catch_error)
135+
return partial(on_complete_resolver, on_resolve_error, complete_object_value_resolve, exe_context, info, resolver)
116136

117137

118138
def type_resolver_non_null(return_type, resolver, fragment, exe_context, info): # no catch_error
119139
resolver = type_resolver(return_type.of_type, resolver, fragment, exe_context, info)
120140
nonnull_complete = partial(complete_nonnull_value, exe_context, info)
121-
return partial(on_complete_resolver, False, nonnull_complete, exe_context, info, resolver)
141+
on_resolve_error = partial(on_error, exe_context, info, False)
142+
return partial(on_complete_resolver, on_resolve_error, nonnull_complete, exe_context, info, resolver)
122143

123144

124145
def type_resolver_leaf(return_type, resolver, exe_context, info, catch_error):
125-
return partial(on_complete_resolver, catch_error, return_type.serialize, exe_context, info, resolver)
146+
leaf_complete = partial(complete_leaf_value, return_type.serialize)
147+
on_resolve_error = partial(on_error, exe_context, info, catch_error)
148+
return partial(on_complete_resolver, on_resolve_error, leaf_complete, exe_context, info, resolver)
126149

127150

128151
def type_resolver_list(return_type, resolver, fragment, exe_context, info, catch_error):
129152
item_type = return_type.of_type
130-
inner_resolver = type_resolver(item_type, lambda item: item, fragment, exe_context, info)
153+
inner_resolver = type_resolver(item_type, lambda item: item, fragment, exe_context, info, catch_error=True)
131154
list_complete = partial(complete_list_value, inner_resolver, exe_context, info)
132-
return partial(on_complete_resolver, catch_error, list_complete, exe_context, info, resolver)
155+
on_resolve_error = partial(on_error, exe_context, info, catch_error)
156+
return partial(on_complete_resolver, on_resolve_error, list_complete, exe_context, info, resolver)
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
from collections import namedtuple
2+
3+
from graphql.error import format_error
4+
from graphql.execution.querybuilder.executor import execute
5+
from graphql.language.parser import parse
6+
from graphql.type import (GraphQLField, GraphQLInt, GraphQLList,
7+
GraphQLNonNull, GraphQLObjectType, GraphQLSchema)
8+
9+
from .utils import rejected, resolved
10+
11+
Data = namedtuple('Data', 'test')
12+
ast = parse('{ nest { test } }')
13+
14+
15+
def check(test_data, expected):
16+
def run_check(self):
17+
test_type = self.type
18+
19+
data = Data(test=test_data)
20+
DataType = GraphQLObjectType(
21+
name='DataType',
22+
fields=lambda: {
23+
'test': GraphQLField(test_type),
24+
'nest': GraphQLField(DataType, resolver=lambda *_: data)
25+
}
26+
)
27+
28+
schema = GraphQLSchema(query=DataType)
29+
response = execute(schema, ast, data)
30+
31+
if response.errors:
32+
result = {
33+
'data': response.data,
34+
'errors': [format_error(e) for e in response.errors]
35+
}
36+
else:
37+
result = {
38+
'data': response.data
39+
}
40+
41+
assert result == expected
42+
43+
return run_check
44+
45+
46+
class Test_ListOfT_Array_T: # [T] Array<T>
47+
type = GraphQLList(GraphQLInt)
48+
49+
test_contains_values = check([1, 2], {'data': {'nest': {'test': [1, 2]}}})
50+
test_contains_null = check([1, None, 2], {'data': {'nest': {'test': [1, None, 2]}}})
51+
test_returns_null = check(None, {'data': {'nest': {'test': None}}})
52+
53+
54+
class Test_ListOfT_Promise_Array_T: # [T] Promise<Array<T>>
55+
type = GraphQLList(GraphQLInt)
56+
57+
test_contains_values = check(resolved([1, 2]), {'data': {'nest': {'test': [1, 2]}}})
58+
test_contains_null = check(resolved([1, None, 2]), {'data': {'nest': {'test': [1, None, 2]}}})
59+
test_returns_null = check(resolved(None), {'data': {'nest': {'test': None}}})
60+
test_rejected = check(lambda: rejected(Exception('bad')), {
61+
'data': {'nest': {'test': None}},
62+
'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
63+
})
64+
65+
66+
class Test_ListOfT_Array_Promise_T: # [T] Array<Promise<T>>
67+
type = GraphQLList(GraphQLInt)
68+
69+
test_contains_values = check([resolved(1), resolved(2)], {'data': {'nest': {'test': [1, 2]}}})
70+
test_contains_null = check([resolved(1), resolved(None), resolved(2)], {'data': {'nest': {'test': [1, None, 2]}}})
71+
test_contains_reject = check(lambda: [resolved(1), rejected(Exception('bad')), resolved(2)], {
72+
'data': {'nest': {'test': [1, None, 2]}},
73+
'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
74+
})
75+
76+
77+
class Test_NotNullListOfT_Array_T: # [T]! Array<T>
78+
type = GraphQLNonNull(GraphQLList(GraphQLInt))
79+
80+
test_contains_values = check(resolved([1, 2]), {'data': {'nest': {'test': [1, 2]}}})
81+
test_contains_null = check(resolved([1, None, 2]), {'data': {'nest': {'test': [1, None, 2]}}})
82+
test_returns_null = check(resolved(None), {
83+
'data': {'nest': None},
84+
'errors': [{'locations': [{'column': 10, 'line': 1}],
85+
'message': 'Cannot return null for non-nullable field DataType.test.'}]
86+
})
87+
88+
89+
class Test_NotNullListOfT_Promise_Array_T: # [T]! Promise<Array<T>>>
90+
type = GraphQLNonNull(GraphQLList(GraphQLInt))
91+
92+
test_contains_values = check(resolved([1, 2]), {'data': {'nest': {'test': [1, 2]}}})
93+
test_contains_null = check(resolved([1, None, 2]), {'data': {'nest': {'test': [1, None, 2]}}})
94+
test_returns_null = check(resolved(None), {
95+
'data': {'nest': None},
96+
'errors': [{'locations': [{'column': 10, 'line': 1}],
97+
'message': 'Cannot return null for non-nullable field DataType.test.'}]
98+
})
99+
100+
test_rejected = check(lambda: rejected(Exception('bad')), {
101+
'data': {'nest': None},
102+
'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
103+
})
104+
105+
106+
class Test_NotNullListOfT_Array_Promise_T: # [T]! Promise<Array<T>>>
107+
type = GraphQLNonNull(GraphQLList(GraphQLInt))
108+
test_contains_values = check([resolved(1), resolved(2)], {'data': {'nest': {'test': [1, 2]}}})
109+
test_contains_null = check([resolved(1), resolved(None), resolved(2)], {'data': {'nest': {'test': [1, None, 2]}}})
110+
test_contains_reject = check(lambda: [resolved(1), rejected(Exception('bad')), resolved(2)], {
111+
'data': {'nest': {'test': [1, None, 2]}},
112+
'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
113+
})
114+
115+
116+
# class TestListOfNotNullT_Array_T: # [T!] Array<T>
117+
# type = GraphQLList(GraphQLNonNull(GraphQLInt))
118+
119+
# test_contains_values = check([1, 2], {'data': {'nest': {'test': [1, 2]}}})
120+
# test_contains_null = check([1, None, 2], {
121+
# 'data': {'nest': {'test': None}},
122+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
123+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
124+
# })
125+
# test_returns_null = check(None, {'data': {'nest': {'test': None}}})
126+
127+
128+
# class TestListOfNotNullT_Promise_Array_T: # [T!] Promise<Array<T>>
129+
# type = GraphQLList(GraphQLNonNull(GraphQLInt))
130+
131+
# test_contains_value = check(resolved([1, 2]), {'data': {'nest': {'test': [1, 2]}}})
132+
# test_contains_null = check(resolved([1, None, 2]), {
133+
# 'data': {'nest': {'test': None}},
134+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
135+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
136+
# })
137+
138+
# test_returns_null = check(resolved(None), {'data': {'nest': {'test': None}}})
139+
140+
# test_rejected = check(lambda: rejected(Exception('bad')), {
141+
# 'data': {'nest': {'test': None}},
142+
# 'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
143+
# })
144+
145+
146+
# class TestListOfNotNullT_Array_Promise_T: # [T!] Array<Promise<T>>
147+
# type = GraphQLList(GraphQLNonNull(GraphQLInt))
148+
149+
# test_contains_values = check([resolved(1), resolved(2)], {'data': {'nest': {'test': [1, 2]}}})
150+
# test_contains_null = check([resolved(1), resolved(None), resolved(2)], {
151+
# 'data': {'nest': {'test': None}},
152+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
153+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
154+
# })
155+
# test_contains_reject = check(lambda: [resolved(1), rejected(Exception('bad')), resolved(2)], {
156+
# 'data': {'nest': {'test': None}},
157+
# 'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
158+
# })
159+
160+
161+
# class TestNotNullListOfNotNullT_Array_T: # [T!]! Array<T>
162+
# type = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
163+
164+
# test_contains_values = check([1, 2], {'data': {'nest': {'test': [1, 2]}}})
165+
# test_contains_null = check([1, None, 2], {
166+
# 'data': {'nest': None},
167+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
168+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
169+
# })
170+
# test_returns_null = check(None, {
171+
# 'data': {'nest': None},
172+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
173+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
174+
# })
175+
176+
177+
# class TestNotNullListOfNotNullT_Promise_Array_T: # [T!]! Promise<Array<T>>
178+
# type = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
179+
180+
# test_contains_value = check(resolved([1, 2]), {'data': {'nest': {'test': [1, 2]}}})
181+
# test_contains_null = check(resolved([1, None, 2]), {
182+
# 'data': {'nest': None},
183+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
184+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
185+
# })
186+
187+
# test_returns_null = check(resolved(None), {
188+
# 'data': {'nest': None},
189+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
190+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
191+
# })
192+
193+
# test_rejected = check(lambda: rejected(Exception('bad')), {
194+
# 'data': {'nest': None},
195+
# 'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
196+
# })
197+
198+
199+
# class TestNotNullListOfNotNullT_Array_Promise_T: # [T!]! Array<Promise<T>>
200+
# type = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
201+
202+
# test_contains_values = check([resolved(1), resolved(2)], {'data': {'nest': {'test': [1, 2]}}})
203+
# test_contains_null = check([resolved(1), resolved(None), resolved(2)], {
204+
# 'data': {'nest': None},
205+
# 'errors': [{'locations': [{'column': 10, 'line': 1}],
206+
# 'message': 'Cannot return null for non-nullable field DataType.test.'}]
207+
# })
208+
# test_contains_reject = check(lambda: [resolved(1), rejected(Exception('bad')), resolved(2)], {
209+
# 'data': {'nest': None},
210+
# 'errors': [{'locations': [{'column': 10, 'line': 1}], 'message': 'bad'}]
211+
# })

0 commit comments

Comments
 (0)