text: #!/usr/bin/env python
  #
  # Copyright (c) Microsoft Corporation.
  # Licensed under the MIT License.
  
  import logging
  from inspect import Parameter, signature
  from typing import Any, Dict, List, Optional
  
  from onefuzztypes.enums import ContainerType, UserFieldType
  from onefuzztypes.job_templates import JobTemplateConfig, JobTemplateRequestParameters
  from onefuzztypes.models import Job
  from onefuzztypes.primitives import Directory, File
  
  from ..api import PreviewFeature
  from .handlers import TemplateSubmitHandler
  
  LOGGER = logging.getLogger("job-templates")
  
  TYPES = {
      UserFieldType.Str: str,
      UserFieldType.Int: int,
      UserFieldType.ListStr: List[str],
      UserFieldType.DictStr: Dict[str, str],
      UserFieldType.Bool: bool,
  }
  
  NAMES = {
      UserFieldType.Str: "str",
      UserFieldType.Int: "int",
      UserFieldType.ListStr: "list",
      UserFieldType.DictStr: "dict",
      UserFieldType.Bool: "bool",
  }
  
  
  def container_type_name(container_type: ContainerType) -> str:
      return container_type.name + "_dir"
  
  
  def config_to_params(config: JobTemplateConfig) -> List[Parameter]:
      params: List[Parameter] = []
  
      for entry in config.user_fields:
          default = entry.default if entry.default is not None else Parameter.empty
          annotation: Any = None
  
          if entry.name == "target_exe":
              annotation = File
          else:
              annotation = TYPES[entry.type]
  
          is_optional = entry.default is None and entry.required is False
          if is_optional:
              annotation = Optional[annotation]
  
          param = Parameter(
              entry.name,
              Parameter.KEYWORD_ONLY,
              annotation=annotation,
              default=default,
          )
          params.append(param)
  
      for container in config.containers:
          if container not in ContainerType.user_config():
              continue
  
          param = Parameter(
              container_type_name(container),
              Parameter.KEYWORD_ONLY,
              annotation=Optional[Directory],
              default=Parameter.empty,
          )
          params.append(param)
  
      if config.containers:
          param = Parameter(
              "container_names",
              Parameter.KEYWORD_ONLY,
              annotation=Optional[Dict[ContainerType, str]],
              default=None,
          )
          params.append(param)
  
      param = Parameter(
          "parameters",
          Parameter.KEYWORD_ONLY,
          annotation=Optional[JobTemplateRequestParameters],
          default=Parameter.empty,
      )
      params.append(param)
  
      return params
  
  
  def build_template_doc(config: JobTemplateConfig) -> str:
      docs = [
          f"Launch '{config.name}' job",
          "",
      ]
  
      for entry in config.user_fields:
          line = f":param {NAMES[entry.type]} {entry.name}: {entry.help}"
          docs.append(line)
  
      for container in config.containers:
          if container not in ContainerType.user_config():
              continue
          line = f":param Directory {container_type_name(container)}: Local path to the {container.name} directory"
          docs.append(line)
  
      if config.containers:
          line = ":param dict container_names: custom container names (eg: setup=my-setup-container)"
          docs.append(line)
  
      return "\n".join(docs)
  
  
  def build_template_func(config: JobTemplateConfig) -> Any:
      def func(
          self: TemplateSubmitHandler,
          *,
          wait_for_running: bool = False,
          **kwargs: Any,
      ) -> Job:
          self.onefuzz._warn_preview(PreviewFeature.job_templates)
          return self._execute(
              config,
              kwargs,
              wait_for_running=wait_for_running,
          )
  
      sig = signature(func)
      params = [
          sig.parameters["self"],
          sig.parameters["wait_for_running"],
      ] + config_to_params(config)
      sig = sig.replace(parameters=tuple(params))
      func.__signature__ = sig  # type: ignore
  
      func.__doc__ = build_template_doc(config)
  
      return func
  