Skip to content

Commit fd0fbf6

Browse files
committed
Improved fragment abstractions
1 parent 04b63e6 commit fd0fbf6

File tree

6 files changed

+61
-54
lines changed

6 files changed

+61
-54
lines changed

graphql/execution/querybuilder/executor.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,4 @@ def execute_operation(exe_context, operation, root_value):
7777
execute_serially = operation.operation == 'mutation'
7878

7979
fragment = Fragment(type=type, selection_set=operation.selection_set, context=exe_context, execute_serially=execute_serially)
80-
resolver = type_resolver(type, lambda: root_value, fragment=fragment)
81-
resolved = resolver()
82-
return resolved
80+
return fragment.resolve(root_value)

graphql/execution/querybuilder/fragment.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from functools import partial
22
from ...pyutils.cached_property import cached_property
33
from ..values import get_argument_values, get_variable_values
4-
from ..base import collect_fields
4+
from ..base import collect_fields, ResolveInfo
55
from ...pyutils.default_ordered_dict import DefaultOrderedDict
66

77

@@ -31,6 +31,17 @@ def get_resolvers(context, type, selection_set):
3131
field_def = type.fields[field_name]
3232
field_base_type = get_base_type(field_def.type)
3333
field_fragment = None
34+
info = ResolveInfo(
35+
field_name,
36+
field_asts,
37+
field_base_type,
38+
parent_type=type,
39+
schema=context and context.schema,
40+
fragments=context and context.fragments,
41+
root_value=context and context.root_value,
42+
operation=context and context.operation,
43+
variable_values=context and context.variable_values,
44+
)
3445
if isinstance(field_base_type, GraphQLObjectType):
3546
field_fragment = Fragment(
3647
type=field_base_type,
@@ -41,6 +52,7 @@ def get_resolvers(context, type, selection_set):
4152
field_fragment = AbstractFragment(
4253
abstract_type=field_base_type,
4354
selection_set=field_ast.selection_set,
55+
info=info,
4456
context=context
4557
)
4658
resolver = field_resolver(field_def, fragment=field_fragment)
@@ -49,8 +61,7 @@ def get_resolvers(context, type, selection_set):
4961
field_ast.arguments,
5062
context and context.variable_values
5163
)
52-
resolver = partial(resolver, args=args)
53-
resolvers.append((response_name, resolver))
64+
resolvers.append((response_name, resolver, args, context and context.context_value, info))
5465
return resolvers
5566

5667

@@ -69,11 +80,10 @@ def partial_resolvers(self):
6980
self.selection_set
7081
)
7182

72-
def resolver(self, resolver, *args, **kwargs):
73-
root = resolver(*args, **kwargs)
83+
def resolve(self, root):
7484
return {
75-
field_name: field_resolver(root)
76-
for field_name, field_resolver in self.partial_resolvers
85+
field_name: field_resolver(root, field_args, context, info)
86+
for field_name, field_resolver, field_args, context, info in self.partial_resolvers
7787
}
7888

7989
def __eq__(self, other):
@@ -86,44 +96,35 @@ def __eq__(self, other):
8696

8797

8898
class AbstractFragment(object):
89-
def __init__(self, abstract_type, selection_set, context=None, execute_serially=False):
99+
def __init__(self, abstract_type, selection_set, context=None, info=None): # execute_serially=False
90100
self.abstract_type = abstract_type
91101
self.selection_set = selection_set
92102
self.context = context
93-
# self.execute_serially = execute_serially # Technically impossible
94-
self._type_resolvers = {}
103+
self.info = info
104+
self._fragments = {}
95105

96106
@cached_property
97107
def possible_types(self):
98108
return self.context.schema.get_possible_types(self.abstract_type)
99109

100-
def get_type_resolvers(self, type):
101-
if type not in self._type_resolvers:
110+
def get_fragment(self, type):
111+
if type not in self._fragments:
102112
assert type in self.possible_types
103-
self._type_resolvers[type] = get_resolvers(
104-
self.context,
105-
type,
106-
self.selection_set
107-
)
108-
return self._type_resolvers[type]
113+
self._fragments[type] = Fragment(type, self.selection_set, self.context)
114+
return self._fragments[type]
109115

110116
def resolve_type(self, result):
111117
return_type = self.abstract_type
112118
context = self.context.context_value
113-
info = None
114119

115120
if return_type.resolve_type:
116-
runtime_type = return_type.resolve_type(result, context, info)
121+
runtime_type = return_type.resolve_type(result, context, self.info)
117122
else:
118123
for type in self.possible_types:
119-
if callable(type.is_type_of) and type.is_type_of(result, context, info):
124+
if callable(type.is_type_of) and type.is_type_of(result, context, self.info):
120125
return type
121126

122-
def resolver(self, resolver, *args, **kwargs):
123-
root = resolver(*args, **kwargs)
127+
def resolve(self, root):
124128
_type = self.resolve_type(root)
125-
126-
return {
127-
field_name: field_resolver(root)
128-
for field_name, field_resolver in self.get_type_resolvers(_type)
129-
}
129+
fragment = self.get_fragment(_type)
130+
return fragment.resolve(root)

graphql/execution/querybuilder/resolver.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,13 @@ def type_resolver(return_type, resolver, fragment=None):
6060

6161
if isinstance(return_type, (GraphQLObjectType)):
6262
assert fragment and fragment.type == return_type
63-
return partial(fragment.resolver, resolver)
63+
return partial(on_complete_resolver, fragment.resolve, resolver)
64+
# return partial(fragment.resolver, resolver)
6465

6566
if isinstance(return_type, (GraphQLInterfaceType, GraphQLUnionType)):
6667
assert fragment
67-
return partial(fragment.resolver, resolver)
68+
return partial(on_complete_resolver, fragment.resolve, resolver)
69+
# return partial(fragment.resolver, resolver)
6870
# return partial(fragment.abstract_resolver, resolver, return_type)
6971

7072
raise Exception("The resolver have to be created for a fragment")

graphql/execution/querybuilder/tests/test_benchmark.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def test_querybuilder_big_list_of_ints(benchmark):
2323
def test_querybuilder_big_list_of_nested_ints(benchmark):
2424
big_int_list = [x for x in range(SIZE)]
2525

26-
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, args: obj)})
26+
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, args, context, info: obj)})
2727
selection_set = ast.SelectionSet(selections=[
2828
ast.Field(
2929
alias=None,
@@ -48,8 +48,8 @@ def test_querybuilder_big_list_of_objecttypes_with_two_int_fields(benchmark):
4848
big_int_list = [x for x in range(SIZE)]
4949

5050
Node = GraphQLObjectType('Node', fields={
51-
'id': GraphQLField(GraphQLInt, resolver=lambda obj, args: obj),
52-
'ida': GraphQLField(GraphQLInt, resolver=lambda obj, args: obj*2)
51+
'id': GraphQLField(GraphQLInt, resolver=lambda obj, args, context, info: obj),
52+
'ida': GraphQLField(GraphQLInt, resolver=lambda obj, args, context, info: obj*2)
5353
})
5454
selection_set = ast.SelectionSet(selections=[
5555
ast.Field(
@@ -81,7 +81,7 @@ def test_querybuilder_big_list_of_objecttypes_with_two_int_fields(benchmark):
8181

8282
def test_querybuilder_big_list_of_objecttypes_with_one_int_field(benchmark):
8383
big_int_list = [x for x in range(SIZE)]
84-
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, **__: obj)})
84+
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj)})
8585
Query = GraphQLObjectType('Query', fields={'nodes': GraphQLField(GraphQLList(Node), resolver=lambda *_, **__: big_int_list)})
8686
node_selection_set = ast.SelectionSet(selections=[
8787
ast.Field(
Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
from functools import partial
23

34
from ....language import ast
45
from ....type import (GraphQLEnumType, GraphQLInterfaceType, GraphQLList,
@@ -14,16 +15,18 @@
1415
from promise import Promise
1516

1617

17-
def test_fragment_resolver_abstract():
18+
def test_fragment_resolver_abstract(benchmark):
19+
all_slots = range(10000)
20+
1821
Node = GraphQLInterfaceType('Node', fields={'id': GraphQLField(GraphQLInt)})
19-
Person = GraphQLObjectType('Person', interfaces=(Node, ), is_type_of=lambda *_: True, fields={
20-
'id': GraphQLField(GraphQLInt, resolver=lambda obj, **__: obj),
21-
'name': GraphQLField(GraphQLString, resolver=lambda obj, **__: "name:"+str(obj))
22+
Person = GraphQLObjectType('Person', interfaces=(Node, ), is_type_of=lambda *_, **__: True, fields={
23+
'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj),
24+
'name': GraphQLField(GraphQLString, resolver=lambda obj, *_, **__: "name:"+str(obj))
2225
})
23-
Query = GraphQLObjectType('Query', fields={'node': GraphQLField(Node, resolver=lambda *_, **__: 1)})
26+
Query = GraphQLObjectType('Query', fields={'nodes': GraphQLField(GraphQLList(Node), resolver=lambda *_, **__: all_slots)})
2427

2528
document_ast = parse('''query {
26-
node {
29+
nodes {
2730
id
2831
... on Person {
2932
name
@@ -32,11 +35,13 @@ def test_fragment_resolver_abstract():
3235
}''')
3336
# node_fragment = Fragment(type=Node, field_asts=node_field_asts)
3437
schema = GraphQLSchema(query=Query, types=[Person])
35-
resolved = execute(schema, document_ast)
38+
partial_execute = partial(execute, schema, document_ast)
39+
resolved = benchmark(partial_execute)
40+
# resolved = execute(schema, document_ast)
3641
assert not resolved.errors
3742
assert resolved.data == {
38-
'node': {
39-
'id': 1,
40-
'name': 'name:1'
41-
}
43+
'nodes': [{
44+
'id': x,
45+
'name': 'name:'+str(x)
46+
} for x in all_slots]
4247
}

graphql/execution/querybuilder/tests/test_fragment.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,19 @@ def test_fragment_equal():
3636

3737

3838
def test_fragment_resolver():
39-
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda *_, **__: 2)})
39+
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj*2)})
4040
selection_set = ast.SelectionSet(selections=[
4141
ast.Field(
4242
name=ast.Name(value='id'),
4343
)
4444
])
4545
fragment = Fragment(type=Node, selection_set=selection_set)
46-
assert fragment.resolver(lambda: 1) == {'id': 2}
46+
assert fragment.resolve(1) == {'id': 2}
47+
assert fragment.resolve(2) == {'id': 4}
4748

4849

4950
def test_fragment_resolver_list():
50-
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, **__: obj)})
51+
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj)})
5152
selection_set = ast.SelectionSet(selections=[
5253
ast.Field(
5354
name=ast.Name(value='id'),
@@ -64,7 +65,7 @@ def test_fragment_resolver_list():
6465

6566

6667
def test_fragment_resolver_nested():
67-
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, **__: obj)})
68+
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj)})
6869
Query = GraphQLObjectType('Query', fields={'node': GraphQLField(Node, resolver=lambda *_, **__: 1)})
6970
node_selection_set = ast.SelectionSet(selections=[
7071
ast.Field(
@@ -90,7 +91,7 @@ def test_fragment_resolver_nested():
9091

9192
def test_fragment_resolver_abstract():
9293
Node = GraphQLInterfaceType('Node', fields={'id': GraphQLField(GraphQLInt)})
93-
Person = GraphQLObjectType('Person', interfaces=(Node, ), is_type_of=lambda *_: True, fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, **__: obj)})
94+
Person = GraphQLObjectType('Person', interfaces=(Node, ), is_type_of=lambda *_: True, fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj)})
9495
Query = GraphQLObjectType('Query', fields={'node': GraphQLField(Node, resolver=lambda *_, **__: 1)})
9596
node_selection_set = ast.SelectionSet(selections=[
9697
ast.Field(
@@ -139,7 +140,7 @@ def test_fragment_resolver_abstract():
139140

140141

141142
def test_fragment_resolver_nested_list():
142-
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, **__: obj)})
143+
Node = GraphQLObjectType('Node', fields={'id': GraphQLField(GraphQLInt, resolver=lambda obj, *_, **__: obj)})
143144
Query = GraphQLObjectType('Query', fields={'nodes': GraphQLField(GraphQLList(Node), resolver=lambda *_, **__: range(3))})
144145
node_selection_set = ast.SelectionSet(selections=[
145146
ast.Field(

0 commit comments

Comments
 (0)