nodes: 0, module; 1, function_definition; 2, function_name:_diff_lists; 3, parameters; 4, block; 5, identifier:old; 6, identifier:new; 7, identifier:comparator; 8, expression_statement; 9, function_definition; 10, expression_statement; 11, comment:# We don't want to alter old since it may be used later by caller; 12, expression_statement; 13, for_statement; 14, expression_statement; 15, return_statement; 16, string; 17, function_name:_remove_indent; 18, parameters; 19, block; 20, assignment; 21, assignment; 22, identifier:new_item; 23, identifier:new; 24, block; 25, assignment; 26, identifier:diff; 27, string_content:Compare lists to extract the changes
  
      :param old: old list
      :param new: new list
      :return: a dictionary with ``unchanged``, ``new``, ``deleted`` and ``sorted`` keys
  
      The sorted list is the union of unchanged and new lists, but keeping the original
      order from the new list.; 28, identifier:node; 29, expression_statement; 30, expression_statement; 31, expression_statement; 32, for_statement; 33, return_statement; 34, identifier:diff; 35, dictionary; 36, identifier:old_devices; 37, call; 38, expression_statement; 39, if_statement; 40, subscript; 41, identifier:old_devices; 42, string; 43, assignment; 44, assignment; 45, identifier:item; 46, call; 47, block; 48, identifier:node_copy; 49, pair; 50, pair; 51, pair; 52, pair; 53, attribute; 54, argument_list; 55, assignment; 56, identifier:found; 57, block; 58, else_clause; 59, identifier:diff; 60, string; 61, string_content:Remove the XML indentation to compare XML trees more easily; 62, identifier:node_copy; 63, call; 64, attribute; 65, None; 66, attribute; 67, argument_list; 68, expression_statement; 69, string; 70, list; 71, string; 72, list; 73, string; 74, list; 75, string; 76, list; 77, identifier:copy; 78, identifier:deepcopy; 79, identifier:old; 80, identifier:found; 81, list_comprehension; 82, expression_statement; 83, expression_statement; 84, expression_statement; 85, block; 86, string_content:deleted; 87, attribute; 88, argument_list; 89, identifier:node_copy; 90, identifier:text; 91, identifier:node_copy; 92, identifier:iter; 93, assignment; 94, string_content:unchanged; 95, string_content:new; 96, string_content:deleted; 97, string_content:sorted; 98, identifier:item; 99, for_in_clause; 100, if_clause; 101, call; 102, call; 103, call; 104, expression_statement; 105, expression_statement; 106, identifier:copy; 107, identifier:deepcopy; 108, identifier:node; 109, attribute; 110, None; 111, identifier:item; 112, identifier:old_devices; 113, call; 114, attribute; 115, argument_list; 116, attribute; 117, argument_list; 118, attribute; 119, argument_list; 120, call; 121, call; 122, identifier:item; 123, identifier:tail; 124, identifier:comparator; 125, argument_list; 126, identifier:old_devices; 127, identifier:remove; 128, subscript; 129, subscript; 130, identifier:append; 131, subscript; 132, subscript; 133, identifier:append; 134, subscript; 135, attribute; 136, argument_list; 137, attribute; 138, argument_list; 139, call; 140, call; 141, identifier:found; 142, integer:0; 143, identifier:diff; 144, string; 145, identifier:found; 146, integer:0; 147, identifier:diff; 148, string; 149, identifier:found; 150, integer:0; 151, subscript; 152, identifier:append; 153, identifier:new_item; 154, subscript; 155, identifier:append; 156, identifier:new_item; 157, identifier:_remove_indent; 158, argument_list; 159, identifier:_remove_indent; 160, argument_list; 161, string_content:unchanged; 162, string_content:sorted; 163, identifier:diff; 164, string; 165, identifier:diff; 166, string; 167, identifier:item; 168, identifier:new_item; 169, string_content:new; 170, string_content:sorted
code: def _diff_lists(old, new, comparator):
      '''
      Compare lists to extract the changes
  
      :param old: old list
      :param new: new list
      :return: a dictionary with ``unchanged``, ``new``, ``deleted`` and ``sorted`` keys
  
      The sorted list is the union of unchanged and new lists, but keeping the original
      order from the new list.
      '''
      def _remove_indent(node):
          '''
          Remove the XML indentation to compare XML trees more easily
          '''
          node_copy = copy.deepcopy(node)
          node_copy.text = None
          for item in node_copy.iter():
              item.tail = None
          return node_copy
  
      diff = {'unchanged': [], 'new': [], 'deleted': [], 'sorted': []}
      # We don't want to alter old since it may be used later by caller
      old_devices = copy.deepcopy(old)
      for new_item in new:
          found = [item for item in old_devices if comparator(_remove_indent(item), _remove_indent(new_item))]
          if found:
              old_devices.remove(found[0])
              diff['unchanged'].append(found[0])
              diff['sorted'].append(found[0])
          else:
              diff['new'].append(new_item)
              diff['sorted'].append(new_item)
      diff['deleted'] = old_devices
      return diff