class_docstr: The scansion symbols used can be configured by passing a suitable constants class to
  the constructor.
class_docstr_tok: ["The","scansion","symbols","used","can","be","configured","by","passing","a","suitable","constants","class","to","the","constructor","."]
docstr: Scan a line of Latin pentameter and produce a scansion pattern, and other data.
  
  :param original_line: the original line of Latin verse
  :param optional_transform: whether or not to perform i to j transform for syllabification
  :return: a Verse object
  
  >>> scanner = PentameterScanner()
  >>> print(scanner.scan('ex hoc ingrato gaudia amore tibi.'))
  Verse(original='ex hoc ingrato gaudia amore tibi.', scansion='-   -  -   - -   - U  U - U  U U ', meter='pentameter', valid=True, syllable_count=12, accented='ēx hōc īngrātō gaudia amōre tibi.', scansion_notes=['Spondaic pentameter'], syllables = ['ēx', 'hoc', 'īn', 'gra', 'to', 'gau', 'di', 'a', 'mo', 're', 'ti', 'bi'])
  >>> print(scanner.scan(
  ... "in vento et rapida scribere oportet aqua.").scansion) # doctest: +NORMALIZE_WHITESPACE
  -   -    -   U U -    - U   U -  U  U  U
docstr_tok: ["Scan","a","line","of","Latin","pentameter","and","produce","a","scansion","pattern",",","and","other","data",".",":","param","original_line",":","the","original","line","of","Latin","verse",":","param","optional_transform",":","whether","or","not","to","perform","i","to","j","transform","for","syllabification",":","return",":","a","Verse","object",">",">",">","scanner","=","PentameterScanner","(",")",">",">",">","print","(","scanner.scan","(","'ex","hoc","ingrato","gaudia","amore","tibi",".","'",")",")","Verse","(","original='ex","hoc","ingrato","gaudia","amore","tibi",".","'",",","scansion='-","-","-","-","-","-","U","U","-","U","U","U","'",",","meter='pentameter","'",",","valid=True",",","syllable_count=12",",","accented='\u0113x","h\u014dc","\u012bngr\u0101t\u014d","gaudia","am\u014dre","tibi",".","'",",","scansion_notes=","[","'Spondaic","pentameter","'","]",",","syllables","=","[","'\u0113x","'",",","'hoc","'",",","'\u012bn","'",",","'gra","'",",","'to","'",",","'gau","'",",","'di","'",",","'","a","'",",","'mo","'",",","'re","'",",","'ti","'",",","'bi","'","]",")",">",">",">","print","(","scanner.scan","(","...","``","in","vento","et","rapida","scribere","oportet","aqua",".","``",")",".scansion",")","#","doctest",":","+NORMALIZE_WHITESPACE","-","-","-","U","U","-","-","U","U","-","U","U","U"]
code: def scan(
      self, original_line: str, optional_transform: bool = False
  ) -> Verse:
      """
      Scan a line of Latin pentameter and produce a scansion pattern, and other data.
  
      :param original_line: the original line of Latin verse
      :param optional_transform: whether or not to perform i to j transform for syllabification
      :return: a Verse object
  
      >>> scanner = PentameterScanner()
      >>> print(scanner.scan('ex hoc ingrato gaudia amore tibi.'))
      Verse(original='ex hoc ingrato gaudia amore tibi.', scansion='-   -  -   - -   - U  U - U  U U ', meter='pentameter', valid=True, syllable_count=12, accented='ēx hōc īngrātō gaudia amōre tibi.', scansion_notes=['Spondaic pentameter'], syllables = ['ēx', 'hoc', 'īn', 'gra', 'to', 'gau', 'di', 'a', 'mo', 're', 'ti', 'bi'])
      >>> print(scanner.scan(
      ... "in vento et rapida scribere oportet aqua.").scansion) # doctest: +NORMALIZE_WHITESPACE
      -   -    -   U U -    - U   U -  U  U  U
      """
      verse = Verse(original_line, meter="pentameter")
      # replace punctuation with spaces
      line = original_line.translate(self.punctuation_substitutions)
      # conservative i to j
      line = self.transform_i_to_j(line)
      working_line = self.elide_all(line)
      working_line = self.accent_by_position(working_line)
      syllables = self.syllabifier.syllabify(working_line)
      if optional_transform:
          working_line = self.transform_i_to_j_optional(line)
          working_line = self.elide_all(working_line)
          working_line = self.accent_by_position(working_line)
          syllables = self.syllabifier.syllabify(working_line)
          verse.scansion_notes += [
              self.constants.NOTE_MAP["optional i to j"]
          ]
      verse.working_line = working_line
      verse.syllable_count = self.syllabifier.get_syllable_count(
          syllables
      )
      verse.syllables = syllables
      if verse.syllable_count < 12:
          verse.valid = False
          verse.scansion_notes += [self.constants.NOTE_MAP["< 12p"]]
          return verse
      stresses = self.flag_dipthongs(syllables)
      syllables_wspaces = (
          string_utils.to_syllables_with_trailing_spaces(
              working_line, syllables
          )
      )
      offset_map = self.calc_offset(syllables_wspaces)
      for idx, syl in enumerate(syllables):
          for accented in self.constants.ACCENTED_VOWELS:
              if accented in syl:
                  stresses.append(idx)
      # first syllable is always long in Pentameter
      stresses.append(0)
      # second to last syllable is always long
      stresses.append(verse.syllable_count - 2)
  
      verse.scansion = self.produce_scansion(
          stresses, syllables_wspaces, offset_map
      )
      if len(
          string_utils.stress_positions(
              self.constants.STRESSED, verse.scansion
          )
      ) != len(set(stresses)):
          verse.valid = False
          verse.scansion_notes += [
              self.constants.NOTE_MAP["invalid syllables"]
          ]
          return verse
  
      if self.metrical_validator.is_valid_pentameter(verse.scansion):
          verse.scansion_notes += [
              self.constants.NOTE_MAP["positionally"]
          ]
          return self.assign_candidate(verse, verse.scansion)
  
      # identify some obvious and probably choices based on number of syllables
      if verse.syllable_count == 12:  # produce spondees where possible
          candidate = self.make_spondaic(verse.scansion)
          verse.scansion_notes += [self.constants.NOTE_MAP["12p"]]
          return self.assign_candidate(verse, candidate)
      if verse.syllable_count == 14:  # produce spondees where possible
          candidate = self.make_dactyls(verse.scansion)
          verse.scansion_notes += [self.constants.NOTE_MAP["14p"]]
          return self.assign_candidate(verse, candidate)
      if verse.syllable_count > 14:
          verse.valid = False
          verse.scansion_notes += [self.constants.NOTE_MAP["> 14"]]
          return verse
  
      smoothed = self.correct_first_two_dactyls(verse.scansion)
  
      if verse.scansion != smoothed:
          verse.scansion_notes += [
              self.constants.NOTE_MAP["invalid start"]
          ]
          verse.scansion = smoothed
          stresses += string_utils.differences(verse.scansion, smoothed)
  
      if self.metrical_validator.is_valid_pentameter(verse.scansion):
          return self.assign_candidate(verse, verse.scansion)
  
      smoothed = self.correct_penultimate_dactyl_chain(verse.scansion)
  
      if verse.scansion != smoothed:
          verse.scansion_notes += [
              self.constants.NOTE_MAP["penultimate dactyl chain"]
          ]
          verse.scansion = smoothed
          stresses += string_utils.differences(verse.scansion, smoothed)
  
      if self.metrical_validator.is_valid_pentameter(verse.scansion):
          return self.assign_candidate(verse, verse.scansion)
  
      candidates = self.metrical_validator.closest_pentameter_patterns(
          verse.scansion
      )
      if candidates is not None:
          if (
              len(candidates) == 1
              and len(verse.scansion.replace(" ", ""))
              == len(candidates[0])
              and len(
                  string_utils.differences(
                      verse.scansion, candidates[0]
                  )
              )
              == 1
          ):
              tmp_scansion = self.produce_scansion(
                  string_utils.differences(
                      verse.scansion, candidates[0]
                  ),
                  syllables_wspaces,
                  offset_map,
              )
  
              if self.metrical_validator.is_valid_pentameter(
                  tmp_scansion
              ):
                  verse.scansion_notes += [
                      self.constants.NOTE_MAP["closest match"]
                  ]
                  return self.assign_candidate(verse, tmp_scansion)
  
      # if the line doesn't scan "as is", it may scan if the optional i to j transformations
      # are made, so here we set them and try again.
      if self.optional_transform and not verse.valid:
          return self.scan(original_line, optional_transform=True)
  
      verse.accented = self.formatter.merge_line_scansion(
          verse.original, verse.scansion
      )
      return verse
  
file_path: cltk/src/cltk/prosody/lat/pentameter_scanner.py
imports: import re
  import cltk.prosody.lat.string_utils
  from cltk.prosody.lat.metrical_validator import MetricalValidator
  from cltk.prosody.lat.scansion_constants import ScansionConstants
  from cltk.prosody.lat.scansion_formatter import ScansionFormatter
  from cltk.prosody.lat.syllabifier import Syllabifier
  from cltk.prosody.lat.verse import Verse
  from cltk.prosody.lat.verse_scanner import VerseScanner
label_desc: Use image node_id 2 for calling the PentameterScanner obj's underlying member method code with example usage: obj.scan(original_line, optional_transform) and returns: verse, verse, verse, self, self, self, verse, self, self, self, self