text: #!/usr/bin/env python
  """Provides a parser for Rdb format files.
  
  Data in from the European rRNA database in distribution format.
  """
  
  from cogent3.core.alphabet import AlphabetError
  from cogent3.core.info import Info
  from cogent3.core.sequence import RnaSequence, Sequence
  from cogent3.parse.record import RecordError
  from cogent3.parse.record_finder import DelimitedRecordFinder
  
  
  __author__ = "Sandra Smit"
  __copyright__ = "Copyright 2007-2020, The Cogent Project"
  __credits__ = ["Sandra Smit", "Rob Knight"]
  __license__ = "BSD-3"
  __version__ = "2020.12.21a"
  __maintainer__ = "Sandra Smit"
  __email__ = "sandra.smit@colorado.edu"
  __status__ = "Development"
  
  strip = str.strip
  maketrans = str.maketrans
  
  
  RdbFinder = DelimitedRecordFinder("//")
  
  _field_names = {
      "acc": "rRNA",
      "src": "Source",
      "str": "Strain",
      "ta1": "Taxonomy1",
      "ta2": "Taxonomy2",
      "ta3": "Taxonomy3",
      "ta4": "Taxonomy4",
      "chg": "Changes",
      "rem": "Remarks",
      "aut": "Authors",
      "ttl": "Title",
      "jou": "Journal",
      "dat": "JournalYear",
      "vol": "JournalVolume",
      "pgs": "JournalPages",
      "mty": "Gene",
      "del": "Deletions",
      "seq": "Species",
  }
  
  
  def InfoMaker(header_lines):
      """Returns an Info object constructed from the headerLines."""
      info = Info()
      for line in header_lines:
          all = line.strip().split(":", 1)
          # strip out empty lines, lines without name, lines without colon
          if not all[0] or len(all) != 2:
              continue
          try:
              name = _field_names[all[0]]
          except KeyError:
              name = all[0]
  
          value = all[1].strip()
          info[name] = value
      return info
  
  
  def is_seq_label(x):
      "Check if x looks like a sequence label line." ""
      return x.startswith("seq:")
  
  
  def MinimalRdbParser(infile, strict=True):
      """Yield successive sequences as (headerLines, sequence) tuples.
  
      If strict is True (default) raises RecordError when 'seq' label is missing
      and if the record doesn't contain any sequences.
      """
      for rec in RdbFinder(infile):
          index = None
          for line in rec:
              if is_seq_label(line):
                  index = rec.index(line) + 1  # index of first sequence line
  
          # if there is no line that starts with 'seq:' throw error or skip
          if not index:
              if strict:
                  raise RecordError(
                      "Found Rdb record without seq label " + "line: %s" % rec[0]
                  )
              else:
                  continue
  
          headerLines = rec[:index]
          sequence = "".join(rec[index:-1])  # strip off the delimiter
          if sequence.endswith("*"):
              sequence = sequence[:-1]  # strip off '*'
  
          # if there are no sequences throw error or skip
          if not sequence:
              if strict:
                  raise RecordError("Found Rdb record without sequences: %s" % rec[0])
              else:
                  continue
  
          yield headerLines, sequence
  
  
  def create_acceptable_sequence(sequence):
      """Return clean sequence as string, 'o' -> '?', sec. structure deleted
  
      sequence: string of characters
      SeqConstructor: constructor function for sequence creation
  
      Will replace 'o' by '?'.
      Will strip out secondary structure annotation.
      """
      trans_table = dict([(ord(c), None) for c in "{}[]()^"])
      trans_table[ord("o")] = ord("?")
      # strip out secondary structure annotation {}[]()^
      return sequence.translate(trans_table)  # should be accepted by RnaSequence
  
  
  def RdbParser(
      lines, SeqConstructor=RnaSequence, LabelConstructor=InfoMaker, strict=True
  ):
      """Yield sequences from the Rdb record.
  
      lines: a stream of Rdb records.
      SeqConstructor: constructor function to create the final sequence object
      LabelConstructor: function that creates Info dictionary from label lines
      strict: boolean, when True, an error is raised when one occurs, when False,
          the record is ignored when an error occurs.
  
      This function returns proper RnaSequence objects when possible. It strips
      out the secondary structure information, and it replaces 'o' by '?'. The
      original sequence is stored in the info dictionary under 'OriginalSeq'.
      If the original sequence is the desired end product, use MinimalRdbParser.
      """
      for header, sequence in MinimalRdbParser(lines, strict=strict):
          info = LabelConstructor(header)
          clean_seq = create_acceptable_sequence(sequence)
          # add original raw sequence to info
          info["OriginalSeq"] = sequence
          if strict:
              # need to do error checking while constructing info and sequence
              try:
                  yield SeqConstructor(clean_seq, info=info)
              except AlphabetError:
                  raise RecordError(
                      "Sequence construction failed on record with reference %s."
                      % (info.Refs)
                  )
          else:
              # not strict: just skip any record that raises an exception
              try:
                  yield SeqConstructor(clean_seq, info=info)
              except:
                  continue
  
  
  if __name__ == "__main__":
      from sys import argv
  
      filename = argv[1]
      for sequence in RdbParser(open(filename)):
          print(sequence.info.Species)
          print(sequence)
  