Skip to content

Commit 654c17d

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents ff3151e + f6791b0 commit 654c17d

File tree

8 files changed

+103
-64
lines changed

8 files changed

+103
-64
lines changed

graphql/core/execution/base.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,24 +82,31 @@ def __init__(self, data=None, errors=None, invalid=False):
8282
error.value if isinstance(error, DeferredException) else error
8383
for error in errors
8484
]
85+
8586
self.errors = errors
87+
8688
if invalid:
8789
assert data is None
90+
8891
self.invalid = invalid
8992

9093

9194
def get_operation_root_type(schema, operation):
9295
op = operation.operation
9396
if op == 'query':
9497
return schema.get_query_type()
98+
9599
elif op == 'mutation':
96100
mutation_type = schema.get_mutation_type()
101+
97102
if not mutation_type:
98103
raise GraphQLError(
99104
'Schema is not configured for mutations',
100105
[operation]
101106
)
107+
102108
return mutation_type
109+
103110
raise GraphQLError(
104111
'Can only execute queries and mutations',
105112
[operation]
@@ -109,35 +116,36 @@ def get_operation_root_type(schema, operation):
109116
def collect_fields(ctx, type, selection_set, fields, prev_fragment_names):
110117
for selection in selection_set.selections:
111118
directives = selection.directives
119+
112120
if isinstance(selection, ast.Field):
113121
if not should_include_node(ctx, directives):
114122
continue
123+
115124
name = get_field_entry_key(selection)
116-
if name not in fields:
117-
fields[name] = []
118125
fields[name].append(selection)
126+
119127
elif isinstance(selection, ast.InlineFragment):
120-
if not should_include_node(ctx, directives) or \
121-
not does_fragment_condition_match(ctx, selection, type):
128+
if not should_include_node(ctx, directives) or not does_fragment_condition_match(ctx, selection, type):
122129
continue
123-
collect_fields(
124-
ctx, type, selection.selection_set,
125-
fields, prev_fragment_names)
130+
131+
collect_fields(ctx, type, selection.selection_set, fields, prev_fragment_names)
132+
126133
elif isinstance(selection, ast.FragmentSpread):
127134
frag_name = selection.name.value
128-
if frag_name in prev_fragment_names or \
129-
not should_include_node(ctx, directives):
135+
136+
if frag_name in prev_fragment_names or not should_include_node(ctx, directives):
130137
continue
138+
131139
prev_fragment_names.add(frag_name)
132140
fragment = ctx.fragments.get(frag_name)
133141
frag_directives = fragment.directives
134-
if not fragment or \
135-
not should_include_node(ctx, frag_directives) or \
136-
not does_fragment_condition_match(ctx, fragment, type):
142+
if not fragment or not \
143+
should_include_node(ctx, frag_directives) or not \
144+
does_fragment_condition_match(ctx, fragment, type):
137145
continue
138-
collect_fields(
139-
ctx, type, fragment.selection_set,
140-
fields, prev_fragment_names)
146+
147+
collect_fields(ctx, type, fragment.selection_set, fields, prev_fragment_names)
148+
141149
return fields
142150

143151

@@ -146,10 +154,12 @@ def should_include_node(ctx, directives):
146154
@skip directives, where @skip has higher precidence than @include."""
147155
if directives:
148156
skip_ast = None
157+
149158
for directive in directives:
150159
if directive.name.value == GraphQLSkipDirective.name:
151160
skip_ast = directive
152161
break
162+
153163
if skip_ast:
154164
args = get_argument_values(
155165
GraphQLSkipDirective.args,
@@ -159,16 +169,19 @@ def should_include_node(ctx, directives):
159169
return not args.get('if')
160170

161171
include_ast = None
172+
162173
for directive in directives:
163174
if directive.name.value == GraphQLIncludeDirective.name:
164175
include_ast = directive
165176
break
177+
166178
if include_ast:
167179
args = get_argument_values(
168180
GraphQLIncludeDirective.args,
169181
include_ast.arguments,
170182
ctx.variables,
171183
)
184+
172185
return bool(args.get('if'))
173186

174187
return True

graphql/core/execution/executor.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ..language import ast
77
from ..language.parser import parse
88
from ..language.source import Source
9+
from ..pyutils.default_ordered_dict import DefaultOrderedDict
910
from ..type import GraphQLEnumType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, \
1011
GraphQLScalarType, GraphQLUnionType
1112
from ..validation import validate
@@ -75,9 +76,14 @@ def _execute_graphql_query(self, root, ast, operation_name, args, request_contex
7576

7677
def _execute_operation(self, ctx, root, operation, execute_serially):
7778
type = get_operation_root_type(ctx.schema, operation)
78-
fields = collect_fields(ctx, type, operation.selection_set, {}, set())
7979

8080
if operation.operation == 'mutation' or execute_serially:
81+
execute_serially = True
82+
83+
fields = DefaultOrderedDict(list) if execute_serially else collections.defaultdict(list)
84+
fields = collect_fields(ctx, type, operation.selection_set, fields, set())
85+
86+
if execute_serially:
8187
return self._execute_fields_serially(ctx, type, root, fields)
8288

8389
return self._execute_fields(ctx, type, root, fields)
@@ -276,7 +282,7 @@ def complete_value(self, ctx, return_type, field_asts, info, result):
276282
)
277283

278284
# Collect sub-fields to execute to complete this value.
279-
subfield_asts = {}
285+
subfield_asts = collections.defaultdict(list)
280286
visited_fragment_names = set()
281287
for field_ast in field_asts:
282288
selection_set = field_ast.selection_set

graphql/core/pyutils/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class PairSet(object):
2+
def __init__(self):
3+
self._data = set()
4+
5+
def __contains__(self, item):
6+
return item in self._data
7+
8+
def has(self, a, b):
9+
return (a, b) in self._data
10+
11+
def add(self, a, b):
12+
self._data.add((a, b))
13+
self._data.add((b, a))
14+
return self
15+
16+
def remove(self, a, b):
17+
self._data.discard((a, b))
18+
self._data.discard((b, a))

graphql/core/validation/rules/utils.py renamed to graphql/core/pyutils/default_ordered_dict.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,6 @@
11
from collections import Callable, OrderedDict
22

33

4-
class PairSet(object):
5-
def __init__(self):
6-
self._data = set()
7-
8-
def __contains__(self, item):
9-
return item in self._data
10-
11-
def has(self, a, b):
12-
return (a, b) in self._data
13-
14-
def add(self, a, b):
15-
self._data.add((a, b))
16-
self._data.add((b, a))
17-
return self
18-
19-
def remove(self, a, b):
20-
self._data.discard((a, b))
21-
self._data.discard((b, a))
22-
23-
244
class DefaultOrderedDict(OrderedDict):
255
# Source: http://stackoverflow.com/a/6190500/562769
266
def __init__(self, default_factory=None, *a, **kw):

graphql/core/pyutils/pair_set.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class PairSet(object):
2+
def __init__(self):
3+
self._data = set()
4+
5+
def __contains__(self, item):
6+
return item in self._data
7+
8+
def has(self, a, b):
9+
return (a, b) in self._data
10+
11+
def add(self, a, b):
12+
self._data.add((a, b))
13+
self._data.add((b, a))
14+
return self
15+
16+
def remove(self, a, b):
17+
self._data.discard((a, b))
18+
self._data.discard((b, a))

graphql/core/validation/rules/overlapping_fields_can_be_merged.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
from ...error import GraphQLError
44
from ...language import ast
55
from ...language.printer import print_ast
6+
from ...pyutils.default_ordered_dict import DefaultOrderedDict
7+
from ...pyutils.pair_set import PairSet
68
from ...type.definition import (
79
GraphQLInterfaceType,
810
GraphQLObjectType,
911
get_named_type,
1012
)
1113
from ...utils.type_from_ast import type_from_ast
1214
from .base import ValidationRule
13-
from .utils import DefaultOrderedDict, PairSet
1415

1516

1617
class OverlappingFieldsCanBeMerged(ValidationRule):

setup.py

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,4 @@
1-
import sys
2-
31
from setuptools import setup, find_packages
4-
from setuptools.command.test import test as TestCommand
5-
6-
7-
class PyTest(TestCommand):
8-
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
9-
10-
def initialize_options(self):
11-
TestCommand.initialize_options(self)
12-
self.pytest_args = []
13-
14-
def finalize_options(self):
15-
TestCommand.finalize_options(self)
16-
self.test_args = []
17-
self.test_suite = True
18-
19-
def run_tests(self):
20-
# import here, cause outside the eggs aren't loaded
21-
import pytest
22-
errno = pytest.main(self.pytest_args)
23-
sys.exit(errno)
24-
252

263
setup(
274
name='graphql-core',
@@ -50,14 +27,13 @@ def run_tests(self):
5027

5128
keywords='api graphql protocol rest',
5229

53-
packages=find_packages(exclude=['tests']),
30+
packages=find_packages(exclude=['tests', 'tests_py35']),
5431

5532
install_requires=['six>=1.10.0'],
5633
tests_require=['pytest>=2.7.3', 'gevent==1.1b5', 'six>=1.10.0'],
5734
extras_require={
5835
'gevent': [
5936
'gevent==1.1b5'
6037
]
61-
},
62-
cmdclass={'test': PyTest},
38+
}
6339
)

tests/core_pyutils/test_pair_set.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from graphql.core.pyutils.pair_set import PairSet
2+
3+
4+
def test_pair_set():
5+
ps = PairSet()
6+
7+
ps.add(1, 2)
8+
ps.add(2, 4)
9+
10+
assert ps.has(1, 2)
11+
assert ps.has(2, 1)
12+
assert (1, 2) in ps
13+
assert (2, 1) in ps
14+
assert ps.has(4, 2)
15+
assert ps.has(2, 4)
16+
17+
assert not ps.has(2, 3)
18+
assert not ps.has(1, 3)
19+
20+
ps.remove(1, 2)
21+
assert not ps.has(1, 2)
22+
assert not ps.has(2, 1)
23+
assert (1, 2) not in ps
24+
assert (2, 1) not in ps
25+
26+
assert ps.has(4, 2)
27+
assert ps.has(2, 4)

0 commit comments

Comments
 (0)