nodes: 0, module; 1, function_definition; 2, function_name:pair_SAM_alignments; 3, parameters; 4, block; 5, identifier:alignments; 6, default_parameter; 7, default_parameter; 8, expression_statement; 9, expression_statement; 10, function_definition; 11, expression_statement; 12, expression_statement; 13, for_statement; 14, if_statement; 15, if_statement; 16, identifier:bundle; 17, False; 18, identifier:primary_only; 19, False; 20, string; 21, assignment; 22, function_name:process_list; 23, parameters; 24, block; 25, assignment; 26, assignment; 27, identifier:almnt; 28, identifier:alignments; 29, block; 30, identifier:bundle; 31, block; 32, else_clause; 33, comparison_operator:mate_missing_count[0] > 1; 34, block; 35, string_content:Iterate over SAM aligments, name-sorted paired-end
  
      Args:
          alignments (iterator of SAM/BAM alignments): the alignments to wrap
          bundle (bool): if True, bundle all alignments from one read pair into a
              single yield. If False (default), each pair of alignments is
              yielded separately.
          primary_only (bool): for each read, consider only the primary line
              (SAM flag 0x900 = 0). The SAM specification requires one and only
              one of those for each read.
  
      Yields:
          2-tuples with each pair of alignments or, if bundle==True, each bundled
          list of alignments.; 36, identifier:mate_missing_count; 37, list; 38, identifier:almnt_list; 39, expression_statement; 40, while_statement; 41, identifier:almnt_list; 42, list; 43, identifier:current_name; 44, None; 45, if_statement; 46, if_statement; 47, comment:# FIXME: almnt.not_primary_alignment currently means secondary; 48, if_statement; 49, if_statement; 50, expression_statement; 51, block; 52, subscript; 53, integer:1; 54, expression_statement; 55, integer:0; 56, string; 57, comparison_operator:len(almnt_list) > 0; 58, block; 59, not_operator; 60, block; 61, comparison_operator:almnt.pe_which == "unknown"; 62, block; 63, boolean_operator; 64, block; 65, comparison_operator:almnt.read.name == current_name; 66, block; 67, else_clause; 68, yield; 69, for_statement; 70, identifier:mate_missing_count; 71, integer:0; 72, call; 73, string_content:Transform a list of alignment with the same read name into pairs
  
          Args:
              almnt_list (list): alignments to process
  
          Yields:
              each pair of alignments.
  
          This function is needed because each line of a BAM file is not a read
          but an alignment. For uniquely mapped and unmapped reads, those two are
          the same. For multimapped reads, however, there can be more than one
          alignment for each read. Also, it is normal for a mapper to uniquely
          map one read and multimap its mate.
  
          This function goes down the list of alignments for a given read name
          and tries to find the first mate. So if read 1 is uniquely mapped but
          read 2 is mapped 4 times, only (read 1, read 2 - first occurrence) will
          yield; the other 3 alignments of read 2 are ignored.; 74, call; 75, integer:0; 76, expression_statement; 77, comment:# Find its mate; 78, for_statement; 79, if_statement; 80, if_statement; 81, attribute; 82, raise_statement; 83, attribute; 84, string:"unknown"; 85, raise_statement; 86, identifier:primary_only; 87, parenthesized_expression; 88, continue_statement; 89, attribute; 90, identifier:current_name; 91, expression_statement; 92, block; 93, call; 94, identifier:p; 95, call; 96, block; 97, attribute; 98, argument_list; 99, identifier:len; 100, argument_list; 101, assignment; 102, identifier:a2; 103, identifier:almnt_list; 104, block; 105, else_clause; 106, comparison_operator:a2 is not None; 107, block; 108, comparison_operator:a1.pe_which == "first"; 109, block; 110, else_clause; 111, identifier:almnt; 112, identifier:paired_end; 113, call; 114, identifier:almnt; 115, identifier:pe_which; 116, call; 117, boolean_operator; 118, attribute; 119, identifier:name; 120, call; 121, if_statement; 122, expression_statement; 123, expression_statement; 124, identifier:list; 125, argument_list; 126, identifier:process_list; 127, argument_list; 128, expression_statement; 129, identifier:warnings; 130, identifier:warn; 131, binary_operator:"%d reads with missing mate encountered." %
                        mate_missing_count[0]; 132, identifier:almnt_list; 133, identifier:a1; 134, call; 135, if_statement; 136, if_statement; 137, if_statement; 138, if_statement; 139, block; 140, identifier:a2; 141, None; 142, expression_statement; 143, attribute; 144, string:"first"; 145, expression_statement; 146, block; 147, identifier:ValueError; 148, argument_list; 149, identifier:ValueError; 150, argument_list; 151, attribute; 152, attribute; 153, identifier:almnt; 154, identifier:read; 155, attribute; 156, argument_list; 157, identifier:bundle; 158, block; 159, else_clause; 160, assignment; 161, assignment; 162, call; 163, identifier:almnt_list; 164, yield; 165, string:"%d reads with missing mate encountered."; 166, subscript; 167, attribute; 168, argument_list; 169, comparison_operator:a1.pe_which == a2.pe_which; 170, block; 171, boolean_operator; 172, block; 173, not_operator; 174, block; 175, boolean_operator; 176, block; 177, if_statement; 178, expression_statement; 179, call; 180, identifier:a1; 181, identifier:pe_which; 182, yield; 183, assert_statement; 184, expression_statement; 185, string:"'pair_alignments' needs a sequence of paired-end alignments"; 186, string:"Paired-end read found with 'unknown' 'pe_which' status."; 187, identifier:almnt; 188, identifier:not_primary_alignment; 189, identifier:almnt; 190, identifier:supplementary; 191, identifier:almnt_list; 192, identifier:append; 193, identifier:almnt; 194, expression_statement; 195, block; 196, identifier:current_name; 197, attribute; 198, identifier:almnt_list; 199, list; 200, identifier:process_list; 201, argument_list; 202, identifier:p; 203, identifier:mate_missing_count; 204, integer:0; 205, identifier:almnt_list; 206, identifier:pop; 207, integer:0; 208, attribute; 209, attribute; 210, continue_statement; 211, comparison_operator:a1.aligned != a2.mate_aligned; 212, comparison_operator:a1.mate_aligned != a2.aligned; 213, continue_statement; 214, parenthesized_expression; 215, break_statement; 216, boolean_operator; 217, comparison_operator:a2.iv.start == a1.mate_start.pos; 218, break_statement; 219, attribute; 220, block; 221, assignment; 222, attribute; 223, argument_list; 224, tuple; 225, comparison_operator:a1.pe_which == "second"; 226, yield; 227, yield; 228, for_statement; 229, attribute; 230, identifier:name; 231, identifier:almnt; 232, identifier:almnt_list; 233, identifier:a1; 234, identifier:pe_which; 235, identifier:a2; 236, identifier:pe_which; 237, attribute; 238, attribute; 239, attribute; 240, attribute; 241, boolean_operator; 242, boolean_operator; 243, line_continuation:\; 244, comparison_operator:a2.iv.chrom == a1.mate_start.chrom; 245, attribute; 246, attribute; 247, identifier:a1; 248, identifier:mate_aligned; 249, expression_statement; 250, if_statement; 251, identifier:a2; 252, None; 253, identifier:almnt_list; 254, identifier:remove; 255, identifier:a2; 256, identifier:a1; 257, identifier:a2; 258, attribute; 259, string:"second"; 260, tuple; 261, call; 262, identifier:p; 263, call; 264, block; 265, identifier:almnt; 266, identifier:read; 267, identifier:a1; 268, identifier:aligned; 269, identifier:a2; 270, identifier:mate_aligned; 271, identifier:a1; 272, identifier:mate_aligned; 273, identifier:a2; 274, identifier:aligned; 275, attribute; 276, attribute; 277, comparison_operator:a1.iv.chrom == a2.mate_start.chrom; 278, comparison_operator:a1.iv.start == a2.mate_start.pos; 279, attribute; 280, attribute; 281, attribute; 282, identifier:start; 283, attribute; 284, identifier:pos; 285, augmented_assignment; 286, comparison_operator:mate_missing_count[0] == 1; 287, block; 288, identifier:a1; 289, identifier:pe_which; 290, identifier:a2; 291, identifier:a1; 292, identifier:list; 293, argument_list; 294, identifier:process_list; 295, argument_list; 296, expression_statement; 297, identifier:a1; 298, identifier:aligned; 299, identifier:a2; 300, identifier:aligned; 301, attribute; 302, attribute; 303, attribute; 304, attribute; 305, attribute; 306, identifier:chrom; 307, attribute; 308, identifier:chrom; 309, identifier:a2; 310, identifier:iv; 311, identifier:a1; 312, identifier:mate_start; 313, subscript; 314, integer:1; 315, subscript; 316, integer:1; 317, expression_statement; 318, call; 319, identifier:almnt_list; 320, yield; 321, attribute; 322, identifier:chrom; 323, attribute; 324, identifier:chrom; 325, attribute; 326, identifier:start; 327, attribute; 328, identifier:pos; 329, identifier:a2; 330, identifier:iv; 331, identifier:a1; 332, identifier:mate_start; 333, identifier:mate_missing_count; 334, integer:0; 335, identifier:mate_missing_count; 336, integer:0; 337, call; 338, identifier:process_list; 339, argument_list; 340, identifier:p; 341, identifier:a1; 342, identifier:iv; 343, identifier:a2; 344, identifier:mate_start; 345, identifier:a1; 346, identifier:iv; 347, identifier:a2; 348, identifier:mate_start; 349, attribute; 350, argument_list; 351, identifier:almnt_list; 352, identifier:warnings; 353, identifier:warn; 354, binary_operator:"Read " + a1.read.name + " claims to have an aligned mate " +
                              "which could not be found in an adjacent line."; 355, binary_operator:"Read " + a1.read.name + " claims to have an aligned mate "; 356, string:"which could not be found in an adjacent line."; 357, binary_operator:"Read " + a1.read.name; 358, string:" claims to have an aligned mate "; 359, string:"Read "; 360, attribute; 361, attribute; 362, identifier:name; 363, identifier:a1; 364, identifier:read
code: def pair_SAM_alignments(
          alignments,
          bundle=False,
          primary_only=False):
      '''Iterate over SAM aligments, name-sorted paired-end
  
      Args:
          alignments (iterator of SAM/BAM alignments): the alignments to wrap
          bundle (bool): if True, bundle all alignments from one read pair into a
              single yield. If False (default), each pair of alignments is
              yielded separately.
          primary_only (bool): for each read, consider only the primary line
              (SAM flag 0x900 = 0). The SAM specification requires one and only
              one of those for each read.
  
      Yields:
          2-tuples with each pair of alignments or, if bundle==True, each bundled
          list of alignments.
      '''
  
      mate_missing_count = [0]
  
      def process_list(almnt_list):
          '''Transform a list of alignment with the same read name into pairs
  
          Args:
              almnt_list (list): alignments to process
  
          Yields:
              each pair of alignments.
  
          This function is needed because each line of a BAM file is not a read
          but an alignment. For uniquely mapped and unmapped reads, those two are
          the same. For multimapped reads, however, there can be more than one
          alignment for each read. Also, it is normal for a mapper to uniquely
          map one read and multimap its mate.
  
          This function goes down the list of alignments for a given read name
          and tries to find the first mate. So if read 1 is uniquely mapped but
          read 2 is mapped 4 times, only (read 1, read 2 - first occurrence) will
          yield; the other 3 alignments of read 2 are ignored.
          '''
  
  
          while len(almnt_list) > 0:
              a1 = almnt_list.pop(0)
              # Find its mate
              for a2 in almnt_list:
                  if a1.pe_which == a2.pe_which:
                      continue
                  if a1.aligned != a2.mate_aligned or a1.mate_aligned != a2.aligned:
                      continue
                  if not (a1.aligned and a2.aligned):
                      break
                  if a1.iv.chrom == a2.mate_start.chrom and a1.iv.start == a2.mate_start.pos and \
                     a2.iv.chrom == a1.mate_start.chrom and a2.iv.start == a1.mate_start.pos:
                      break
              else:
                  if a1.mate_aligned:
                      mate_missing_count[0] += 1
                      if mate_missing_count[0] == 1:
                          warnings.warn(
                              "Read " + a1.read.name + " claims to have an aligned mate " +
                              "which could not be found in an adjacent line.")
                  a2 = None
              if a2 is not None:
                  almnt_list.remove(a2)
              if a1.pe_which == "first":
                  yield (a1, a2)
              else:
                  assert a1.pe_which == "second"
                  yield (a2, a1)
  
      almnt_list = []
      current_name = None
      for almnt in alignments:
          if not almnt.paired_end:
              raise ValueError(
                  "'pair_alignments' needs a sequence of paired-end alignments")
          if almnt.pe_which == "unknown":
              raise ValueError(
                  "Paired-end read found with 'unknown' 'pe_which' status.")
          # FIXME: almnt.not_primary_alignment currently means secondary
          if primary_only and (almnt.not_primary_alignment or almnt.supplementary):
              continue
          if almnt.read.name == current_name:
              almnt_list.append(almnt)
          else:
              if bundle:
                  yield list(process_list(almnt_list))
              else:
                  for p in process_list(almnt_list):
                      yield p
              current_name = almnt.read.name
              almnt_list = [almnt]
      if bundle:
          yield list(process_list(almnt_list))
      else:
          for p in process_list(almnt_list):
              yield p
      if mate_missing_count[0] > 1:
          warnings.warn("%d reads with missing mate encountered." %
                        mate_missing_count[0])