code: def setup_modules(self, args):
      
  
      def _setup_module_thread(module_description):
        """Calls the module's setup() function and sets an Event object for it.
  
        Args:
          module_description (dict): Corresponding recipe module description.
        """
        new_args = utils.import_args_from_dict(
            module_description['args'], vars(args), self.config)
        module = self._module_pool[module_description['name']]
        try:
          module.setup(**new_args)
        except Exception as error:  # pylint: disable=broad-except
          self.add_error(
              'An unknown error occurred: {0!s}\nFull traceback:\n{1:s}'.format(
                  error, traceback.format_exc()),
              critical=True)
  
        self.events[module_description['name']] = threading.Event()
        self.cleanup()
  
      threads = []
      for module_description in self.recipe['modules']:
        t = threading.Thread(
            target=_setup_module_thread,
            args=(module_description, )
        )
        threads.append(t)
        t.start()
      for t in threads:
        t.join()
  
      self.check_errors(is_global=True)
docs: Performs setup tasks for each module in the module pool.
  
      Threads declared modules' setup() functions. Takes CLI arguments into
      account when replacing recipe parameters for each module.
  
      Args:
        args: Command line arguments that will be used to replace the parameters
            declared in the recipe.