<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def setup_modules(self, args):
 """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. """


    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)