@@ -44,48 +44,92 @@ def visit_using_rules(schema, ast, rules):
44
44
type_info = TypeInfo (schema )
45
45
context = ValidationContext (schema , ast , type_info )
46
46
errors = []
47
- for rule in rules :
48
- instance = rule (context )
49
- visit (ast , ValidationVisitor (instance , type_info , errors ))
47
+ rules = [rule (context ) for rule in rules ]
48
+ visit (ast , ValidationVisitor (rules , context , type_info , errors ))
50
49
return errors
51
50
52
51
53
52
class ValidationVisitor (Visitor ):
54
- def __init__ (self , instance , type_info , errors ):
55
- self .instance = instance
53
+ def __init__ (self , rules , context , type_info , errors ):
54
+ self .context = context
55
+ self .rules = rules
56
+ self .total_rules = len (rules )
56
57
self .type_info = type_info
57
58
self .errors = errors
59
+ self .ignore_children = {}
58
60
59
61
def enter (self , node , key , parent , path , ancestors ):
60
62
self .type_info .enter (node )
63
+ to_ignore = None
64
+ rules_wanting_to_visit_fragment = None
61
65
62
- if isinstance (node , FragmentDefinition ) and key and hasattr (self .instance , 'visit_spread_fragments' ):
63
- return False
66
+ skipped = 0
67
+ for rule in self .rules :
68
+ if rule in self .ignore_children :
69
+ skipped += 1
70
+ continue
71
+
72
+ visit_spread_fragments = getattr (rule , 'visit_spread_fragments' , False )
73
+
74
+ if isinstance (node , FragmentDefinition ) and key and visit_spread_fragments :
75
+ if to_ignore is None :
76
+ to_ignore = []
77
+
78
+ to_ignore .append (rule )
79
+ continue
80
+
81
+ result = rule .enter (node , key , parent , path , ancestors )
82
+
83
+ if result and is_error (result ):
84
+ append (self .errors , result )
85
+ result = False
86
+
87
+ if result is None and visit_spread_fragments and isinstance (node , FragmentSpread ):
88
+ if rules_wanting_to_visit_fragment is None :
89
+ rules_wanting_to_visit_fragment = []
90
+
91
+ rules_wanting_to_visit_fragment .append (rule )
64
92
65
- result = self .instance .enter (node , key , parent , path , ancestors )
66
- if result and is_error (result ):
67
- append (self .errors , result )
68
- result = False
93
+ if result is False :
94
+ if to_ignore is None :
95
+ to_ignore = []
96
+
97
+ to_ignore .append (rule )
98
+
99
+ if rules_wanting_to_visit_fragment :
100
+ fragment = self .context .get_fragment (node .name .value )
69
101
70
- if result is None and getattr (self .instance , 'visit_spread_fragments' , False ) and isinstance (node , FragmentSpread ):
71
- fragment = self .instance .context .get_fragment (node .name .value )
72
102
if fragment :
73
- visit (fragment , self )
103
+ sub_visitor = ValidationVisitor (rules_wanting_to_visit_fragment , self .context , self .type_info ,
104
+ self .errors )
105
+ visit (fragment , sub_visitor )
74
106
75
- if result is False :
107
+ should_skip = (len (to_ignore ) if to_ignore else 0 + skipped ) == self .total_rules
108
+
109
+ if should_skip :
76
110
self .type_info .leave (node )
77
111
78
- return result
112
+ elif to_ignore :
113
+ for rule in to_ignore :
114
+ self .ignore_children [rule ] = node
115
+
116
+ if should_skip :
117
+ return False
79
118
80
119
def leave (self , node , key , parent , path , ancestors ):
81
- result = self .instance .leave (node , key , parent , path , ancestors )
120
+ for rule in self .rules :
121
+ if rule in self .ignore_children :
122
+ if self .ignore_children [rule ] is node :
123
+ del self .ignore_children [rule ]
124
+
125
+ continue
126
+
127
+ result = rule .leave (node , key , parent , path , ancestors )
82
128
83
- if result and is_error (result ):
84
- append (self .errors , result )
85
- result = False
129
+ if result and is_error (result ):
130
+ append (self .errors , result )
86
131
87
132
self .type_info .leave (node )
88
- return result
89
133
90
134
91
135
def is_error (value ):
0 commit comments