docs: def get_insertions(aln_df):
      """Get a list of tuples indicating the first and last residues of a insertion region, as well as the length of the insertion.
  
      If the first tuple is:
          (-1, 1) that means the insertion is at the beginning of the original protein
          (X, Inf) where X is the length of the original protein, that means the insertion is at the end of the protein
  
      Examples:
          # Insertion at beginning, length 3
          >>> test = {'id_a': {0: 'a', 1: 'a', 2: 'a', 3: 'a'}, 'id_a_aa': {0: np.nan, 1: np.nan, 2: np.nan, 3: 'M'}, 'id_a_pos': {0: np.nan, 1: np.nan, 2: np.nan, 3: 1.0}, 'id_b': {0: 'b', 1: 'b', 2: 'b', 3: 'b'}, 'id_b_aa': {0: 'M', 1: 'M', 2: 'L', 3: 'M'}, 'id_b_pos': {0: 1, 1: 2, 2: 3, 3: 4}, 'type': {0: 'insertion', 1: 'insertion', 2: 'insertion', 3: 'match'}}
          >>> my_alignment = pd.DataFrame.from_dict(test)
          >>> get_insertions(my_alignment)
          [((-1, 1.0), 3)]
  
      Args:
          aln_df (DataFrame): Alignment DataFrame
  
      Returns:
          list: A list of tuples with the format ((insertion_start_resnum, insertion_end_resnum), insertion_length)
  
      """
  
      insertion_df = aln_df[aln_df['type'] == 'insertion']
      # if not insertion_df.empty: # don't need to do this for insertions
      #     insertion_df['id_a_pos'] = insertion_df['id_a_pos'].astype(int)
  
      insertions = []
  
      for k, g in groupby(insertion_df.index, key=lambda n, c=count(): n - next(c)):
          tmp = list(g)
          insertion_indices = (min(tmp), max(tmp))
  
          insertion_start = insertion_indices[0] - 1
          insertion_end = insertion_indices[1] + 1
  
          # Checking if insertion is at the beginning or end
          if insertion_start < 0:
              insertion_start = insertion_indices[0]
              insertion_length = insertion_end - insertion_start
          elif insertion_end >= len(aln_df):
              insertion_end = insertion_indices[1]
              insertion_length = insertion_end - insertion_start
          else:
              insertion_length = insertion_end - insertion_start - 1
  
          id_a_pos_insertion_start = aln_df.ix[insertion_start].id_a_pos
          id_a_pos_insertion_end = aln_df.ix[insertion_end].id_a_pos
  
          # Checking if insertion is at the beginning or end
          if np.isnan(id_a_pos_insertion_start) and id_a_pos_insertion_end == 1:
              insertion_region = (-1, id_a_pos_insertion_end)
          elif np.isnan(id_a_pos_insertion_end):
              insertion_region = (id_a_pos_insertion_start, float('Inf'))
          else:
              insertion_region = (id_a_pos_insertion_start, id_a_pos_insertion_end)
  
          # Logging where the insertion is
          if insertion_region[0] == -1:
              log.debug('Insertion of length {} at beginning'.format(insertion_length))
          elif insertion_region[1] == float('Inf'):
              log.debug('Insertion of length {} at end'.format(insertion_length))
          else:
              log.debug('Insertion of length {} at residues {}'.format(insertion_length, insertion_region))
  
          to_append = (insertion_region, insertion_length)
          insertions.append(to_append)
  
      return insertions