Skip to content

Commit 68633ff

Browse files
committed
[Validation] Parallelize validation rules.
Related GraphQL-js commit: graphql/graphql-js@9577041
1 parent 6cbf0b2 commit 68633ff

File tree

3 files changed

+40
-26
lines changed

3 files changed

+40
-26
lines changed

graphql/core/validation/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from ..utils.type_info import TypeInfo
44
from .context import ValidationContext
55
from .rules import specified_rules
6-
from .visitor import ValidationVisitor
6+
from .visitor import ParallelVisitor, TypeInfoVisitor
77

88

99
def validate(schema, ast, rules=specified_rules):
@@ -16,7 +16,6 @@ def validate(schema, ast, rules=specified_rules):
1616

1717
def visit_using_rules(schema, type_info, ast, rules):
1818
context = ValidationContext(schema, ast, type_info)
19-
rules = [rule(context) for rule in rules]
20-
for instance in rules:
21-
visit(ast, ValidationVisitor(instance, context, type_info))
19+
visitors = [rule(context) for rule in rules]
20+
visit(ast, TypeInfoVisitor(type_info, ParallelVisitor(visitors)))
2221
return context.get_errors()

graphql/core/validation/context.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from ..language.ast import FragmentDefinition, FragmentSpread, VariableDefinition, Variable, OperationDefinition
22
from ..utils.type_info import TypeInfo
33
from ..language.visitor import Visitor, visit
4+
from .visitor import TypeInfoVisitor
45

56

67
class VariableUsage(object):
@@ -12,22 +13,18 @@ def __init__(self, node, type):
1213

1314

1415
class UsageVisitor(Visitor):
15-
__slots__ = 'context', 'usages', 'type_info'
16+
__slots__ = 'usages', 'type_info'
1617

1718
def __init__(self, usages, type_info):
1819
self.usages = usages
1920
self.type_info = type_info
2021

21-
def enter(self, node, key, parent, path, ancestors):
22-
self.type_info.enter(node)
23-
if isinstance(node, VariableDefinition):
24-
return False
25-
elif isinstance(node, Variable):
26-
usage = VariableUsage(node, type=self.type_info.get_input_type())
27-
self.usages.append(usage)
22+
def enter_VariableDefinition(self, node, key, parent, path, ancestors):
23+
return False
2824

29-
def leave(self, node, key, parent, path, ancestors):
30-
self.type_info.leave(node)
25+
def enter_Variable(self, node, key, parent, path, ancestors):
26+
usage = VariableUsage(node, type=self.type_info.get_input_type())
27+
self.usages.append(usage)
3128

3229

3330
class ValidationContext(object):
@@ -58,7 +55,7 @@ def get_variable_usages(self, node):
5855
if usages is None:
5956
usages = []
6057
sub_visitor = UsageVisitor(usages, self._type_info)
61-
visit(node, sub_visitor)
58+
visit(node, TypeInfoVisitor(self._type_info, sub_visitor))
6259
self._variable_usages[node] = usages
6360

6461
return usages

graphql/core/validation/visitor.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,42 @@
11
from ..language.visitor import Visitor
22

33

4-
class ValidationVisitor(Visitor):
5-
__slots__ = 'context', 'instance', 'type_info'
4+
class TypeInfoVisitor(Visitor):
5+
__slots__ = 'visitor', 'type_info'
66

7-
def __init__(self, instance, context, type_info):
8-
self.context = context
9-
self.instance = instance
7+
def __init__(self, type_info, visitor):
108
self.type_info = type_info
9+
self.visitor = visitor
1110

1211
def enter(self, node, key, parent, path, ancestors):
1312
self.type_info.enter(node)
14-
result = self.instance.enter(node, key, parent, path, ancestors)
13+
result = self.visitor.enter(node, key, parent, path, ancestors)
1514
if result is False:
1615
self.type_info.leave(node)
17-
18-
return result
16+
return False
1917

2018
def leave(self, node, key, parent, path, ancestors):
21-
result = self.instance.leave(node, key, parent, path, ancestors)
22-
19+
self.visitor.leave(node, key, parent, path, ancestors)
2320
self.type_info.leave(node)
24-
return result
21+
22+
23+
class ParallelVisitor(Visitor):
24+
__slots__ = 'skipping', 'visitors'
25+
26+
def __init__(self, visitors):
27+
self.visitors = visitors
28+
self.skipping = [None]*len(visitors)
29+
30+
def enter(self, node, key, parent, path, ancestors):
31+
for i, visitor in enumerate(self.visitors):
32+
if not self.skipping[i]:
33+
result = visitor.enter(node, key, parent, path, ancestors)
34+
if result is False:
35+
self.skipping[i] = node
36+
37+
def leave(self, node, key, parent, path, ancestors):
38+
for i, visitor in enumerate(self.visitors):
39+
if not self.skipping[i]:
40+
visitor.leave(node, key, parent, path, ancestors)
41+
else:
42+
self.skipping[i] = None

0 commit comments

Comments
 (0)