text: import shutil
  import os
  import time
  import multiprocessing as mp
  import yaml
  
  from montreal_forced_aligner import __version__
  from montreal_forced_aligner.corpus.align_corpus import AlignableCorpus
  from montreal_forced_aligner.dictionary import Dictionary
  from montreal_forced_aligner.aligner import PretrainedAligner
  from montreal_forced_aligner.models import AcousticModel
  from montreal_forced_aligner.config import TEMP_DIR, align_yaml_to_config, load_basic_align
  from montreal_forced_aligner.utils import get_available_acoustic_languages, get_pretrained_acoustic_path, \
      get_available_dict_languages, get_dictionary_path
  from montreal_forced_aligner.helper import setup_logger
  from montreal_forced_aligner.exceptions import ArgumentError
  
  
  def train_dictionary(args):
      command = 'train_dictionary'
      all_begin = time.time()
      if not args.temp_directory:
          temp_dir = TEMP_DIR
      else:
          temp_dir = os.path.expanduser(args.temp_directory)
      corpus_name = os.path.basename(args.corpus_directory)
      if corpus_name == '':
          args.corpus_directory = os.path.dirname(args.corpus_directory)
          corpus_name = os.path.basename(args.corpus_directory)
      data_directory = os.path.join(temp_dir, corpus_name)
      conf_path = os.path.join(data_directory, 'config.yml')
      if args.config_path:
          align_config = align_yaml_to_config(args.config_path)
      else:
          align_config = load_basic_align()
      if getattr(args, 'clean', False) and os.path.exists(data_directory):
          print('Cleaning old directory!')
          shutil.rmtree(data_directory, ignore_errors=True)
      logger = setup_logger(command, data_directory)
      if os.path.exists(conf_path):
          with open(conf_path, 'r') as f:
              conf = yaml.load(f, Loader=yaml.SafeLoader)
      else:
          conf = {'dirty': False,
                  'begin': time.time(),
                  'version': __version__,
                  'type': command,
                  'corpus_directory': args.corpus_directory,
                  'dictionary_path': args.dictionary_path,
                  'acoustic_model_path': args.acoustic_model_path
                  }
      if conf['dirty'] or conf['type'] != command \
              or conf['corpus_directory'] != args.corpus_directory \
              or conf['version'] != __version__ \
              or conf['dictionary_path'] != args.dictionary_path:
          logger.warning(
              'WARNING: Using old temp directory, this might not be ideal for you, use the --clean flag to ensure no '
              'weird behavior for previous versions of the temporary directory.')
          if conf['dirty']:
              logger.debug('Previous run ended in an error (maybe ctrl-c?)')
          if conf['type'] != command:
              logger.debug('Previous run was a different subcommand than {} (was {})'.format(command, conf['type']))
          if conf['corpus_directory'] != args.corpus_directory:
              logger.debug('Previous run used source directory '
                           'path {} (new run: {})'.format(conf['corpus_directory'], args.corpus_directory))
          if conf['version'] != __version__:
              logger.debug('Previous run was on {} version (new run: {})'.format(conf['version'], __version__))
          if conf['dictionary_path'] != args.dictionary_path:
              logger.debug('Previous run used dictionary path {} '
                           '(new run: {})'.format(conf['dictionary_path'], args.dictionary_path))
          if conf['acoustic_model_path'] != args.acoustic_model_path:
              logger.debug('Previous run used acoustic model path {} '
                           '(new run: {})'.format(conf['acoustic_model_path'], args.acoustic_model_path))
  
      os.makedirs(data_directory, exist_ok=True)
      try:
          corpus = AlignableCorpus(args.corpus_directory, data_directory,
                          speaker_characters=args.speaker_characters,
                          num_jobs=args.num_jobs, use_mp=align_config.use_mp, logger=logger)
          if corpus.issues_check:
              logger.warning('WARNING: Some issues parsing the corpus were detected. '
                    'Please run the validator to get more information.')
          logger.info(corpus.speaker_utterance_info())
          acoustic_model = AcousticModel(args.acoustic_model_path)
          dictionary = Dictionary(args.dictionary_path, data_directory, word_set=corpus.word_set, logger=logger)
          acoustic_model.validate(dictionary)
  
          begin = time.time()
          a = PretrainedAligner(corpus, dictionary, acoustic_model, align_config,
                                temp_directory=data_directory,
                                debug=getattr(args, 'debug', False), logger=logger)
          logger.debug('Setup pretrained aligner in {} seconds'.format(time.time() - begin))
          a.verbose = args.verbose
  
          begin = time.time()
          a.align()
          logger.debug('Performed alignment in {} seconds'.format(time.time() - begin))
  
          a.generate_pronunciations(args.output_directory)
          print('Done! Everything took {} seconds'.format(time.time() - all_begin))
      except Exception as _:
          conf['dirty'] = True
          raise
      finally:
          with open(conf_path, 'w') as f:
              yaml.dump(conf, f)
  
  
  def validate_args(args, downloaded_acoustic_models, download_dictionaries):
      if not os.path.exists(args.corpus_directory):
          raise ArgumentError('Could not find the corpus directory {}.'.format(args.corpus_directory))
      if not os.path.isdir(args.corpus_directory):
          raise ArgumentError('The specified corpus directory ({}) is not a directory.'.format(args.corpus_directory))
  
      if args.dictionary_path.lower() in download_dictionaries:
          args.dictionary_path = get_dictionary_path(args.dictionary_path.lower())
  
      if not os.path.exists(args.dictionary_path):
          raise ArgumentError('Could not find the dictionary file {}'.format(args.dictionary_path))
      if not os.path.isfile(args.dictionary_path):
          raise ArgumentError('The specified dictionary path ({}) is not a text file.'.format(args.dictionary_path))
  
      if args.acoustic_model_path.lower() in downloaded_acoustic_models:
          args.acoustic_model_path = get_pretrained_acoustic_path(args.acoustic_model_path.lower())
      elif args.acoustic_model_path.lower().endswith(AcousticModel.extension):
          if not os.path.exists(args.acoustic_model_path):
              raise ArgumentError('The specified model path does not exist: ' + args.acoustic_model_path)
      else:
          raise ArgumentError(
              'The language \'{}\' is not currently included in the distribution, '
              'please align via training or specify one of the following language names: {}.'.format(
                  args.acoustic_model_path.lower(), ', '.join(downloaded_acoustic_models)))
  
  
  def run_train_dictionary(args, downloaded_acoustic_models=None, download_dictionaries=None):
      if downloaded_acoustic_models is None:
          downloaded_acoustic_models = get_available_acoustic_languages()
      if download_dictionaries is None:
          download_dictionaries = get_available_dict_languages()
      try:
          args.speaker_characters = int(args.speaker_characters)
      except ValueError:
          pass
      args.output_directory = args.output_directory.rstrip('/').rstrip('\\')
      args.corpus_directory = args.corpus_directory.rstrip('/').rstrip('\\')
  
      validate_args(args, downloaded_acoustic_models, download_dictionaries)
      train_dictionary(args)
  
  
  if __name__ == '__main__':  # pragma: no cover
      mp.freeze_support()
      from montreal_forced_aligner.command_line.mfa import train_dictionary_parser, fix_path, unfix_path, \
          acoustic_languages, dict_languages
  
      align_args = train_dictionary_parser.parse_args()
      fix_path()
      run_train_dictionary(align_args, acoustic_languages, dict_languages)
      unfix_path()
  