nodes: 0, module; 1, function_definition; 2, function_name:do_json_dupes_for_keys; 3, parameters; 4, block; 5, identifier:self; 6, identifier:params; 7, expression_statement; 8, try_statement; 9, expression_statement; 10, expression_statement; 11, for_statement; 12, expression_statement; 13, for_statement; 14, for_statement; 15, comment:# if no dupes were found we call it a failure (i.e.: exit(1) from --run-once); 16, if_statement; 17, comment:"""
  \x1b[1mNAME\x1b[0m
          json_duples_for_keys - Gets the duplicate znodes for the given keys
  
  \x1b[1mSYNOPSIS\x1b[0m
          json_dupes_for_keys <path> <keys> [prefix] [report_errors] [first]
  
  \x1b[1mDESCRIPTION\x1b[0m
          Znodes with duplicated keys are sorted and all but the first (original) one
          are printed.
  
  \x1b[1mOPTIONS\x1b[0m
          * prefix: only include matching znodes
          * report_errors: turn on error reporting (i.e.: bad JSON in a znode)
          * first: print the first, non duplicated, znode too.
  
  \x1b[1mEXAMPLES\x1b[0m
          > json_cat /configs/primary_service true
          member_0000000186
          {
            "status": "ALIVE",
            "serviceEndpoint": {
              "http": {
                "host": "10.0.0.2",
                "port": 31994
              }
            },
            "shard": 0
          }
          member_0000000187
          {
            "status": "ALIVE",
            "serviceEndpoint": {
              "http": {
                "host": "10.0.0.2",
                "port": 31994
              }
            },
            "shard": 0
          }
          > json_dupes_for_keys /configs/primary_service shard
          member_0000000187
  
          """; 18, block; 19, except_clause; 20, assignment; 21, assignment; 22, pattern_list; 23, call; 24, block; 25, assignment; 26, pattern_list; 27, call; 28, block; 29, identifier:dup; 30, identifier:dupes; 31, block; 32, comparison_operator:len(dupes) == 0; 33, block; 34, expression_statement; 35, as_pattern; 36, block; 37, identifier:path_map; 38, call; 39, identifier:dupes_by_path; 40, call; 41, identifier:path; 42, identifier:data; 43, attribute; 44, argument_list; 45, expression_statement; 46, if_statement; 47, try_statement; 48, identifier:dupes; 49, list; 50, identifier:_; 51, identifier:paths_by_value; 52, attribute; 53, argument_list; 54, for_statement; 55, expression_statement; 56, call; 57, integer:0; 58, return_statement; 59, call; 60, attribute; 61, as_pattern_target; 62, expression_statement; 63, return_statement; 64, identifier:PathMap; 65, argument_list; 66, identifier:defaultdict; 67, argument_list; 68, identifier:path_map; 69, identifier:get; 70, assignment; 71, not_operator; 72, block; 73, block; 74, except_clause; 75, except_clause; 76, identifier:dupes_by_path; 77, identifier:items; 78, pattern_list; 79, call; 80, block; 81, call; 82, identifier:len; 83, argument_list; 84, False; 85, attribute; 86, argument_list; 87, identifier:Keys; 88, identifier:Bad; 89, identifier:ex; 90, call; 91, attribute; 92, attribute; 93, lambda; 94, pattern_list; 95, call; 96, call; 97, continue_statement; 98, expression_statement; 99, expression_statement; 100, as_pattern; 101, block; 102, as_pattern; 103, block; 104, identifier:_; 105, identifier:paths; 106, attribute; 107, argument_list; 108, if_statement; 109, attribute; 110, argument_list; 111, identifier:dupes; 112, identifier:Keys; 113, identifier:validate; 114, attribute; 115, attribute; 116, argument_list; 117, identifier:self; 118, identifier:_zk; 119, identifier:params; 120, identifier:path; 121, call; 122, identifier:parent; 123, identifier:child; 124, identifier:split; 125, argument_list; 126, attribute; 127, argument_list; 128, assignment; 129, call; 130, identifier:BadJSON; 131, as_pattern_target; 132, if_statement; 133, attribute; 134, as_pattern_target; 135, if_statement; 136, identifier:paths_by_value; 137, identifier:items; 138, comparison_operator:len(paths) > 1; 139, block; 140, identifier:self; 141, identifier:show_output; 142, identifier:dup; 143, identifier:params; 144, identifier:keys; 145, identifier:self; 146, identifier:show_output; 147, call; 148, identifier:defaultdict; 149, argument_list; 150, identifier:path; 151, identifier:child; 152, identifier:startswith; 153, attribute; 154, identifier:value; 155, call; 156, attribute; 157, argument_list; 158, identifier:ex; 159, attribute; 160, block; 161, identifier:Keys; 162, identifier:Missing; 163, identifier:ex; 164, attribute; 165, block; 166, call; 167, integer:1; 168, expression_statement; 169, expression_statement; 170, for_statement; 171, identifier:str; 172, argument_list; 173, identifier:list; 174, identifier:params; 175, identifier:prefix; 176, attribute; 177, argument_list; 178, subscript; 179, identifier:append; 180, identifier:path; 181, identifier:params; 182, identifier:report_errors; 183, expression_statement; 184, identifier:params; 185, identifier:report_errors; 186, expression_statement; 187, identifier:len; 188, argument_list; 189, call; 190, assignment; 191, identifier:path; 192, identifier:paths; 193, block; 194, identifier:ex; 195, identifier:Keys; 196, identifier:value; 197, call; 198, attribute; 199, subscript; 200, identifier:value; 201, call; 202, call; 203, identifier:paths; 204, attribute; 205, argument_list; 206, identifier:paths; 207, conditional_expression:paths if params.first else paths[1:]; 208, expression_statement; 209, expression_statement; 210, identifier:json_deserialize; 211, argument_list; 212, identifier:params; 213, identifier:keys; 214, identifier:dupes_by_path; 215, identifier:parent; 216, attribute; 217, argument_list; 218, attribute; 219, argument_list; 220, identifier:paths; 221, identifier:sort; 222, identifier:paths; 223, attribute; 224, subscript; 225, assignment; 226, call; 227, identifier:data; 228, identifier:self; 229, identifier:show_output; 230, string:"Path %s has bad JSON."; 231, identifier:path; 232, identifier:self; 233, identifier:show_output; 234, string:"Path %s is missing key %s."; 235, identifier:path; 236, identifier:ex; 237, identifier:params; 238, identifier:first; 239, identifier:paths; 240, slice; 241, identifier:idx; 242, call; 243, attribute; 244, argument_list; 245, integer:1; 246, attribute; 247, argument_list; 248, identifier:dupes; 249, identifier:insert; 250, identifier:idx; 251, identifier:path; 252, identifier:bisect; 253, identifier:bisect; 254, identifier:dupes; 255, identifier:path
code: def do_json_dupes_for_keys(self, params):
          """
  \x1b[1mNAME\x1b[0m
          json_duples_for_keys - Gets the duplicate znodes for the given keys
  
  \x1b[1mSYNOPSIS\x1b[0m
          json_dupes_for_keys <path> <keys> [prefix] [report_errors] [first]
  
  \x1b[1mDESCRIPTION\x1b[0m
          Znodes with duplicated keys are sorted and all but the first (original) one
          are printed.
  
  \x1b[1mOPTIONS\x1b[0m
          * prefix: only include matching znodes
          * report_errors: turn on error reporting (i.e.: bad JSON in a znode)
          * first: print the first, non duplicated, znode too.
  
  \x1b[1mEXAMPLES\x1b[0m
          > json_cat /configs/primary_service true
          member_0000000186
          {
            "status": "ALIVE",
            "serviceEndpoint": {
              "http": {
                "host": "10.0.0.2",
                "port": 31994
              }
            },
            "shard": 0
          }
          member_0000000187
          {
            "status": "ALIVE",
            "serviceEndpoint": {
              "http": {
                "host": "10.0.0.2",
                "port": 31994
              }
            },
            "shard": 0
          }
          > json_dupes_for_keys /configs/primary_service shard
          member_0000000187
  
          """
          try:
              Keys.validate(params.keys)
          except Keys.Bad as ex:
              self.show_output(str(ex))
              return
  
          path_map = PathMap(self._zk, params.path)
  
          dupes_by_path = defaultdict(lambda: defaultdict(list))
          for path, data in path_map.get():
              parent, child = split(path)
  
              if not child.startswith(params.prefix):
                  continue
  
              try:
                  value = Keys.value(json_deserialize(data), params.keys)
                  dupes_by_path[parent][value].append(path)
              except BadJSON as ex:
                  if params.report_errors:
                      self.show_output("Path %s has bad JSON.", path)
              except Keys.Missing as ex:
                  if params.report_errors:
                      self.show_output("Path %s is missing key %s.", path, ex)
  
          dupes = []
          for _, paths_by_value in dupes_by_path.items():
              for _, paths in paths_by_value.items():
                  if len(paths) > 1:
                      paths.sort()
                      paths = paths if params.first else paths[1:]
                      for path in paths:
                          idx = bisect.bisect(dupes, path)
                          dupes.insert(idx, path)
  
          for dup in dupes:
              self.show_output(dup)
  
          # if no dupes were found we call it a failure (i.e.: exit(1) from --run-once)
          if len(dupes) == 0:
              return False