@@ -44,48 +44,92 @@ def visit_using_rules(schema, ast, rules):
4444 type_info = TypeInfo (schema )
4545 context = ValidationContext (schema , ast , type_info )
4646 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 ))
5049 return errors
5150
5251
5352class 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 )
5657 self .type_info = type_info
5758 self .errors = errors
59+ self .ignore_children = {}
5860
5961 def enter (self , node , key , parent , path , ancestors ):
6062 self .type_info .enter (node )
63+ to_ignore = None
64+ rules_wanting_to_visit_fragment = None
6165
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 )
6492
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 )
69101
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 )
72102 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 )
74106
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 :
76110 self .type_info .leave (node )
77111
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
79118
80119 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 )
82128
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 )
86131
87132 self .type_info .leave (node )
88- return result
89133
90134
91135def is_error (value ):
0 commit comments