<s> import itertools <EOL> from ... error import GraphQLError <EOL> from ... language import ast <EOL> from ... language . printer import print_ast <EOL> from ... pyutils . default_ordered_dict import DefaultOrderedDict <EOL> from ... pyutils . pair_set import PairSet <EOL> from ... type . definition import ( GraphQLInterfaceType , GraphQLObjectType , <EOL> get_named_type ) <EOL> from ... utils . type_comparators import is_equal_type <EOL> from ... utils . type_from_ast import type_from_ast <EOL> from . base import ValidationRule <EOL> class OverlappingFieldsCanBeMerged ( ValidationRule ) : <EOL> __slots__ = '<STR_LIT>' , <EOL> def __init__ ( self , context ) : <EOL> super ( OverlappingFieldsCanBeMerged , self ) . __init__ ( context ) <EOL> self . compared_set = PairSet ( ) <EOL> def find_conflicts ( self , field_map ) : <EOL> conflicts = [ ] <EOL> for response_name , fields in field_map . items ( ) : <EOL> field_len = len ( fields ) <EOL> if field_len <= <NUM_LIT:1> : <EOL> continue <EOL> for field_a in fields : <EOL> for field_b in fields : <EOL> conflict = self . find_conflict ( response_name , field_a , field_b ) <EOL> if conflict : <EOL> conflicts . append ( conflict ) <EOL> return conflicts <EOL> def find_conflict ( self , response_name , field1 , field2 ) : <EOL> parent_type1 , ast1 , def1 = field1 <EOL> parent_type2 , ast2 , def2 = field2 <EOL> if ast1 is ast2 : <EOL> return <EOL> if parent_type1 != parent_type2 and isinstance ( parent_type1 , GraphQLObjectType ) and isinstance ( parent_type2 , GraphQLObjectType ) : <EOL> return <EOL> if self . compared_set . has ( ast1 , ast2 ) : <EOL> return <EOL> self . compared_set . add ( ast1 , ast2 ) <EOL> name1 = ast1 . name . value <EOL> name2 = ast2 . name . value <EOL> if name1 != name2 : <EOL> return ( <EOL> ( response_name , '<STR_LIT>' . format ( name1 , name2 ) ) , <EOL> [ ast1 ] , <EOL> [ ast2 ] <EOL> ) <EOL> type1 = def1 and def1 . type <EOL> type2 = def2 and def2 . type <EOL> if type1 and type2 and not self . same_type ( type1 , type2 ) : <EOL> return ( <EOL> ( response_name , '<STR_LIT>' . format ( type1 , type2 ) ) , <EOL> [ ast1 ] , <EOL> [ ast2 ] <EOL> ) <EOL> if not self . same_arguments ( ast1 . arguments , ast2 . arguments ) : <EOL> return ( <EOL> ( response_name , '<STR_LIT>' ) , <EOL> [ ast1 ] , <EOL> [ ast2 ] <EOL> ) <EOL> selection_set1 = ast1 . selection_set <EOL> selection_set2 = ast2 . selection_set <EOL> if selection_set1 and selection_set2 : <EOL> visited_fragment_names = set ( ) <EOL> subfield_map = self . collect_field_asts_and_defs ( <EOL> get_named_type ( type1 ) , <EOL> selection_set1 , <EOL> visited_fragment_names <EOL> ) <EOL> subfield_map = self . collect_field_asts_and_defs ( <EOL> get_named_type ( type2 ) , <EOL> selection_set2 , <EOL> visited_fragment_names , <EOL> subfield_map <EOL> ) <EOL> conflicts = self . find_conflicts ( subfield_map ) <EOL> if conflicts : <EOL> return ( <EOL> ( response_name , [ conflict [ <NUM_LIT:0> ] for conflict in conflicts ] ) , <EOL> tuple ( itertools . chain ( [ ast1 ] , * [ conflict [ <NUM_LIT:1> ] for conflict in conflicts ] ) ) , <EOL> tuple ( itertools . chain ( [ ast2 ] , * [ conflict [ <NUM_LIT:2> ] for conflict in conflicts ] ) ) <EOL> ) <EOL> def leave_SelectionSet ( self , node , key , parent , path , ancestors ) : <EOL> field_map = self . collect_field_asts_and_defs ( <EOL> self . context . get_parent_type ( ) , <EOL> node <EOL> ) <EOL> conflicts = self . find_conflicts ( field_map ) <EOL> if conflicts : <EOL> for ( reason_name , reason ) , fields1 , fields2 in conflicts : <EOL> self . context . report_error ( <EOL> GraphQLError ( <EOL> self . fields_conflict_message ( <EOL> reason_name , <EOL> reason ) , <EOL> list ( fields1 ) + <EOL> list ( fields2 ) ) ) <EOL> @ staticmethod <EOL> def same_type ( type1 , type2 ) : <EOL> return is_equal_type ( type1 , type2 ) <EOL> @ staticmethod <EOL> def same_value ( value1 , value2 ) : <EOL> return ( not value1 and not value2 ) or print_ast ( value1 ) == print_ast ( value2 ) <EOL> @ classmethod <EOL> def same_arguments ( cls , arguments1 , arguments2 ) : <EOL> if not ( arguments1 or arguments2 ) : <EOL> return True <EOL> if len ( arguments1 ) != len ( arguments2 ) : <EOL> return False <EOL> arguments2_values_to_arg = { a . name . value : a for a in arguments2 } <EOL> for argument1 in arguments1 : <EOL> argument2 = arguments2_values_to_arg . get ( argument1 . name . value ) <EOL> if not argument2 : <EOL> return False <EOL> if not cls . same_value ( argument1 . value , argument2 . value ) : <EOL> return False <EOL> return True <EOL> def collect_field_asts_and_defs ( self , parent_type , selection_set , visited_fragment_names = None , ast_and_defs = None ) : <EOL> if visited_fragment_names is None : <EOL> visited_fragment_names = set ( ) <EOL> if ast_and_defs is None : <EOL> ast_and_defs = DefaultOrderedDict ( list ) <EOL> for selection in selection_set . selections : <EOL> if isinstance ( selection , ast . Field ) : <EOL> field_name = selection . name . value <EOL> field_def = None <EOL> if isinstance ( parent_type , ( GraphQLObjectType , GraphQLInterfaceType ) ) : <EOL> field_def = parent_type . get_fields ( ) . get ( field_name ) <EOL> response_name = selection . alias . value if selection . alias else field_name <EOL> ast_and_defs [ response_name ] . append ( ( parent_type , selection , field_def ) ) <EOL> elif isinstance ( selection , ast . InlineFragment ) : <EOL> type_condition = selection . type_condition <EOL> inline_fragment_type = type_from_ast ( self . context . get_schema ( ) , type_condition ) if type_condition else parent_type <EOL> self . collect_field_asts_and_defs ( <EOL> inline_fragment_type , <EOL> selection . selection_set , <EOL> visited_fragment_names , <EOL> ast_and_defs <EOL> ) <EOL> elif isinstance ( selection , ast . FragmentSpread ) : <EOL> fragment_name = selection . name . value <EOL> if fragment_name in visited_fragment_names : <EOL> continue <EOL> visited_fragment_names . add ( fragment_name ) <EOL> fragment = self . context . get_fragment ( fragment_name ) <EOL> if not fragment : <EOL> continue <EOL> self . collect_field_asts_and_defs ( <EOL> type_from_ast ( self . context . get_schema ( ) , fragment . type_condition ) , <EOL> fragment . selection_set , <EOL> visited_fragment_names , <EOL> ast_and_defs <EOL> ) <EOL> return ast_and_defs <EOL> @ classmethod <EOL> def fields_conflict_message ( cls , reason_name , reason ) : <EOL> return '<STR_LIT>' . format ( reason_name , cls . reason_message ( reason ) ) <EOL> @ classmethod <EOL> def reason_message ( cls , reason ) : <EOL> if isinstance ( reason , list ) : <EOL> return '<STR_LIT>' . join ( '<STR_LIT>' . format ( reason_name , cls . reason_message ( sub_reason ) ) <EOL> for reason_name , sub_reason in reason ) <EOL> return reason </s>
