@@ -45,48 +45,92 @@ def visit_using_rules(schema, ast, rules):
4545 type_info = TypeInfo (schema )
4646 context = ValidationContext (schema , ast , type_info )
4747 errors = []
48- for rule in rules :
49- instance = rule (context )
50- visit (ast , ValidationVisitor (instance , type_info , errors ))
48+ rules = [rule (context ) for rule in rules ]
49+ visit (ast , ValidationVisitor (rules , context , type_info , errors ))
5150 return errors
5251
5352
5453class ValidationVisitor (Visitor ):
55- def __init__ (self , instance , type_info , errors ):
56- self .instance = instance
54+ def __init__ (self , rules , context , type_info , errors ):
55+ self .context = context
56+ self .rules = rules
57+ self .total_rules = len (rules )
5758 self .type_info = type_info
5859 self .errors = errors
60+ self .ignore_children = {}
5961
6062 def enter (self , node , key , parent , path , ancestors ):
6163 self .type_info .enter (node )
64+ to_ignore = None
65+ rules_wanting_to_visit_fragment = None
6266
63- if isinstance (node , FragmentDefinition ) and key and hasattr (self .instance , 'visit_spread_fragments' ):
64- return False
67+ skipped = 0
68+ for rule in self .rules :
69+ if rule in self .ignore_children :
70+ skipped += 1
71+ continue
72+
73+ visit_spread_fragments = getattr (rule , 'visit_spread_fragments' , False )
74+
75+ if isinstance (node , FragmentDefinition ) and key and visit_spread_fragments :
76+ if to_ignore is None :
77+ to_ignore = []
78+
79+ to_ignore .append (rule )
80+ continue
81+
82+ result = rule .enter (node , key , parent , path , ancestors )
83+
84+ if result and is_error (result ):
85+ append (self .errors , result )
86+ result = False
87+
88+ if result is None and visit_spread_fragments and isinstance (node , FragmentSpread ):
89+ if rules_wanting_to_visit_fragment is None :
90+ rules_wanting_to_visit_fragment = []
91+
92+ rules_wanting_to_visit_fragment .append (rule )
6593
66- result = self .instance .enter (node , key , parent , path , ancestors )
67- if result and is_error (result ):
68- append (self .errors , result )
69- result = False
94+ if result is False :
95+ if to_ignore is None :
96+ to_ignore = []
97+
98+ to_ignore .append (rule )
99+
100+ if rules_wanting_to_visit_fragment :
101+ fragment = self .context .get_fragment (node .name .value )
70102
71- if result is None and getattr (self .instance , 'visit_spread_fragments' , False ) and isinstance (node , FragmentSpread ):
72- fragment = self .instance .context .get_fragment (node .name .value )
73103 if fragment :
74- visit (fragment , self )
104+ sub_visitor = ValidationVisitor (rules_wanting_to_visit_fragment , self .context , self .type_info ,
105+ self .errors )
106+ visit (fragment , sub_visitor )
75107
76- if result is False :
108+ should_skip = (len (to_ignore ) if to_ignore else 0 + skipped ) == self .total_rules
109+
110+ if should_skip :
77111 self .type_info .leave (node )
78112
79- return result
113+ elif to_ignore :
114+ for rule in to_ignore :
115+ self .ignore_children [rule ] = node
116+
117+ if should_skip :
118+ return False
80119
81120 def leave (self , node , key , parent , path , ancestors ):
82- result = self .instance .leave (node , key , parent , path , ancestors )
121+ for rule in self .rules :
122+ if rule in self .ignore_children :
123+ if self .ignore_children [rule ] is node :
124+ del self .ignore_children [rule ]
125+
126+ continue
127+
128+ result = rule .leave (node , key , parent , path , ancestors )
83129
84- if result and is_error (result ):
85- append (self .errors , result )
86- result = False
130+ if result and is_error (result ):
131+ append (self .errors , result )
87132
88133 self .type_info .leave (node )
89- return result
90134
91135
92136def is_error (value ):
0 commit comments