code: def main(args=sys.argv[1:]):
      logger = logs.get_logger("studio-runner")
      parser = argparse.ArgumentParser(
          description="Studio runner. \
                       Usage: studio run <runner_arguments> \
                       script <script_arguments>"
      )
      parser.add_argument(
          "--config", help="configuration file", default=None
      )
      parser.add_argument(
          "--project", help="name of the project", default=None
      )
      parser.add_argument(
          "--experiment",
          "-e",
          help="Name of the experiment. If none provided, "
          + "random uuid will be generated",
          default=None,
      )
  
      parser.add_argument(
          "--guest",
          help="Guest mode (does not require db credentials)",
          action="store_true",
      )
  
      parser.add_argument(
          "--force-git",
          help="If run in a git directory, force running the experiment "
          + "even if changes are not commited",
          action="store_true",
      )
  
      parser.add_argument(
          "--gpus",
          help="Number of gpus needed to run the experiment",
          type=int,
          default=None,
      )
  
      parser.add_argument(
          "--cpus",
          help="Number of cpus needed to run the experiment"
          + " (used to configure cloud instance)",
          type=int,
          default=None,
      )
  
      parser.add_argument(
          "--ram",
          help="Amount of RAM needed to run the experiment"
          + " (used to configure cloud instance), ex: 10G, 10GB",
          default=None,
      )
  
      parser.add_argument(
          "--gpuMem",
          help="Amount of GPU RAM needed to run the experiment",
          default=None,
      )
  
      parser.add_argument(
          "--gpuCount",
          help="Number of GPU cards needed to run the experiment",
          default=None,
      )
  
      parser.add_argument(
          "--hdd",
          help="Amount of hard drive space needed to run the experiment"
          + " (used to configure cloud instance), ex: 10G, 10GB",
          default=None,
      )
  
      parser.add_argument(
          "--queue",
          "-q",
          help="Name of the remote execution queue",
          default=None,
      )
  
      parser.add_argument(
          "--cloud",
          help="Cloud execution mode. Could be gcloud, gcspot, ec2 or ec2spot",
          default=None,
      )
  
      parser.add_argument(
          "--bid",
          help="Spot instance price bid, specified in USD or in percentage "
          + "of on-demand instance price. Default is %(default)s",
          default="100%",
      )
  
      parser.add_argument(
          "--capture-once",
          "-co",
          help="Name of the immutable artifact to be captured. "
          + "It will be captured once before the experiment is run",
          default=[],
          action="append",
      )
  
      parser.add_argument(
          "--capture",
          "-c",
          help="Name of the mutable artifact to be captured continuously",
          default=[],
          action="append",
      )
  
      parser.add_argument(
          "--reuse",
          "-r",
          help="Name of the artifact from another experiment to use",
          default=[],
          action="append",
      )
  
      parser.add_argument(
          "--verbose",
          "-v",
          help="Verbosity level. Allowed values: "
          + "debug, info, warn, error, crit "
          + "or numerical value of logger levels.",
          default=None,
      )
  
      parser.add_argument(
          "--metric",
          help="Metric to show in the summary of the experiment, "
          + "and to base hyperparameter search on. "
          + "Refers a scalar value in tensorboard log "
          + "example: --metric=val_loss[:final | :min | :max] to report "
          + "validation loss in the end of the keras experiment "
          + "(or smallest or largest throughout the experiment for :min "
          + "and :max respectively)",
          default=None,
      )
  
      parser.add_argument(
          "--hyperparam",
          "-hp",
          help="Try out multiple values of a certain parameter. "
          + "For example, --hyperparam=learning_rate:0.01:0.1:l10 "
          + "will instantiate 10 versions of the script, replace "
          + "learning_rate with a one of the 10 values for learning "
          + "rate that lies on a log grid from 0.01 to 0.1, create "
          "experiments and place them in the queue.",
          default=[],
          action="append",
      )
  
      parser.add_argument(
          "--num-workers",
          help="Number of local or cloud workers to spin up",
          type=int,
          default=None,
      )
  
      parser.add_argument(
          "--python-pkg",
          help="Python package not present in the current environment "
          + "that is needed for experiment. Only compatible with "
          + "remote and cloud workers for now",
          default=[],
          action="append",
      )
  
      parser.add_argument(
          "--ssh-keypair",
          help="Name of the SSH keypair used to access the EC2 "
          + "instances directly",
          default=None,
      )
  
      parser.add_argument(
          "--optimizer",
          "-opt",
          help="Name of optimizer to use, by default is grid search. "
          + "The name of the optimizer must either be in "
          + "studio/optimizer_plugins "
          + "directory or the path to the optimizer source file "
          + "must be supplied. ",
          default="grid",
      )
  
      parser.add_argument(
          "--cloud-timeout",
          help="Time (in seconds) that cloud workers wait for messages. "
          + "If negative, "
          + "wait for the first message in the queue indefinitely "
          + "and shut down "
          + "as soon as no new messages are available. "
          + "If zero, don't wait at all."
          + "Default value is %(default)d",
          type=int,
          default=300,
      )
  
      parser.add_argument(
          "--user-startup-script",
          help="Path of script to run immediately "
          + "before running the remote worker",
          default=None,
      )
  
      parser.add_argument(
          "--branch",
          help="Branch of studioml to use when running remote worker, useful "
          + "for debugging pull requests. Default is current",
          default=None,
      )
  
      parser.add_argument(
          "--max-duration",
          help="Max experiment runtime (i.e. time after which experiment "
          + "should be killed no matter what.).  Examples of values "
          + "might include 5h, 48h2m10s",
          default=None,
      )
  
      parser.add_argument(
          "--lifetime",
          help="Max experiment lifetime (i.e. wait time after which "
          + "experiment loses relevance and should not be started)"
          + "  Examples include 240h30m10s",
          default=None,
      )
  
      parser.add_argument(
          "--container",
          help="Singularity container in which experiment should be run. "
          + "Assumes that container has all dependencies installed",
          default=None,
      )
  
      parser.add_argument(
          "--port",
          help="Ports to open on a cloud instance",
          default=[],
          action="append",
      )
  
      # detect which argument is the script filename
      # and attribute all arguments past that index as related to the script
      (runner_args, other_args) = parser.parse_known_args(args)
      py_suffix_args = [
          i
          for i, arg in enumerate(args)
          if arg.endswith(".py") or "::" in arg
      ]
  
      rerun = False
      if len(py_suffix_args) < 1:
          print("None of the arugments end with .py")
          if len(other_args) == 0:
              print("Trying to run a container job")
              assert runner_args.container is not None
              exec_filename = None
          elif len(other_args) == 1:
              print("Treating last argument as experiment key to rerun")
              rerun = True
              experiment_key = args[-1]
          else:
              print(
                  "Too many extra arguments - should be either none "
                  + "for container job or one for experiment re-run"
              )
              sys.exit(1)
      else:
          script_index = py_suffix_args[0]
          exec_filename, other_args = (
              args[script_index],
              args[script_index + 1 :],
          )
          runner_args = parser.parse_args(args[:script_index])
  
      # TODO: Queue the job based on arguments and only then execute.
  
      config = model.get_config(runner_args.config)
  
      if runner_args.verbose:
          config["verbose"] = runner_args.verbose
  
      if runner_args.guest:
          config["database"]["guest"] = True
  
      if runner_args.container:
          runner_args.capture_once.append(
              runner_args.container + ":_singularity"
          )
  
      verbose = model.parse_verbosity(config["verbose"])
      logger.setLevel(verbose)
  
      if git_util.is_git() and not git_util.is_clean() and not rerun:
          logger.warn("Running from dirty git repo")
          if not runner_args.force_git:
              logger.error(
                  "Specify --force-git to run experiment from dirty git repo"
              )
              sys.exit(1)
  
      resources_needed = _parse_hardware(
          runner_args, config["resources_needed"]
      )
      logger.debug("resources requested: ")
      logger.debug(str(resources_needed))
  
      # Set up default artifacts:
      # note that their "local" paths will be updated
      # on Experiment creation,
      # but they must have "local" field defined
      # to have storage credentials set up properly.
      artifacts = {
          "workspace": {
              "mutable": False,
              "local": os.getcwd(),
              "unpack": True,
          },
          "modeldir": {"mutable": True, "local": "", "unpack": True},
          "retval": {"mutable": True, "local": "", "unpack": True},
          "output": {"mutable": True, "local": "", "unpack": True},
          "tb": {"mutable": True, "local": "", "unpack": True},
          "_metrics": {"mutable": True, "local": "", "unpack": True},
          "_metadata": {"mutable": True, "local": "", "unpack": True},
      }
  
      artifacts.update(
          _parse_artifacts(runner_args.capture, mutable=True)
      )
      artifacts.update(
          _parse_artifacts(runner_args.capture_once, mutable=False)
      )
      with model.get_db_provider(config) as db:
          artifacts.update(
              _parse_external_artifacts(runner_args.reuse, db)
          )
  
      logger.debug("Task artifacts: %s", repr(artifacts))
      storage_creds = config.get("storage", {}).get(
          KEY_CREDENTIALS, None
      )
      _setup_artifacts_creds(artifacts, storage_creds)
  
      if runner_args.branch:
          config["cloud"]["branch"] = runner_args.branch
  
      if runner_args.user_startup_script:
          config["cloud"][
              "user_startup_script"
          ] = runner_args.user_startup_script
  
      if runner_args.lifetime:
          config["experimentLifetime"] = runner_args.lifetime
  
      queueLifetime = None
  
      if any(runner_args.hyperparam):
          if runner_args.optimizer == "grid":
              experiments = _add_hyperparam_experiments(
                  exec_filename,
                  other_args,
                  runner_args,
                  artifacts,
                  resources_needed,
                  logger,
              )
  
              queue = model.get_queue(
                  queue_name=runner_args.queue,
                  cloud=runner_args.cloud,
                  config=config,
                  close_after=queueLifetime,
                  logger=logger,
                  verbose=verbose,
              )
  
              queue_name = submit_experiments(
                  experiments, config=config, logger=logger, queue=queue
              )
  
              spin_up_workers(
                  runner_args,
                  config,
                  resources_needed,
                  queue_name=queue_name,
                  verbose=verbose,
              )
          else:
              opt_modulepath = os.path.join(
                  os.path.dirname(os.path.abspath(__file__)),
                  "optimizer_plugins",
                  runner_args.optimizer + ".py",
              )
              if not os.path.exists(opt_modulepath):
                  opt_modulepath = os.path.abspath(
                      os.path.expanduser(runner_args.optimizer)
                  )
              logger.info("optimizer path: %s" % opt_modulepath)
  
              assert os.path.exists(opt_modulepath)
              sys.path.append(os.path.dirname(opt_modulepath))
              opt_module = importlib.import_module(
                  os.path.basename(opt_modulepath.replace(".py", ""))
              )
  
              h = HyperparameterParser(runner_args, logger)
              hyperparams = h.parse()
              optimizer = getattr(opt_module, "Optimizer")(
                  hyperparams, config["optimizer"], logger
              )
  
              workers_started = False
              queue_name = runner_args.queue
              while not optimizer.stop():
                  hyperparam_pop = optimizer.ask()
                  hyperparam_tuples = h.convert_to_tuples(
                      hyperparam_pop
                  )
  
                  experiments = _add_hyperparam_experiments(
                      exec_filename,
                      other_args,
                      runner_args,
                      artifacts,
                      resources_needed,
                      logger,
                      optimizer=optimizer,
                      hyperparam_tuples=hyperparam_tuples,
                  )
  
                  queue = model.get_queue(
                      queue_name=queue_name,
                      cloud=runner_args.cloud,
                      config=config,
                      close_after=queueLifetime,
                      logger=logger,
                      verbose=verbose,
                  )
  
                  queue_name = submit_experiments(
                      experiments,
                      config=config,
                      logger=logger,
                      queue=queue,
                  )
  
                  if not workers_started:
                      spin_up_workers(
                          runner_args,
                          config,
                          resources_needed,
                          queue_name=queue_name,
                          verbose=verbose,
                      )
                      workers_started = True
  
                  fitnesses, behaviors = get_experiment_fitnesses(
                      experiments, optimizer, config, logger
                  )
  
                  try:
                      optimizer.tell(
                          hyperparam_pop, fitnesses, behaviors
                      )
                  except BaseException:
                      util.check_for_kb_interrupt()
                      optimizer.tell(hyperparam_pop, fitnesses)
  
                  try:
                      optimizer.disp()
                  except BaseException:
                      util.check_for_kb_interrupt()
                      logger.warn("Optimizer has no disp() method")
      else:
          if rerun:
              with model.get_db_provider(config) as db:
                  experiment = db.get_experiment(experiment_key)
                  new_key = (
                      runner_args.experiment
                      if runner_args.experiment
                      else experiment_key + "_rerun" + str(uuid.uuid4())
                  )
                  experiment.key = new_key
                  for _, art in six.iteritems(experiment.artifacts):
                      art["mutable"] = False
  
                  experiments = [experiment]
  
          else:
              experiments = [
                  create_experiment(
                      filename=exec_filename,
                      args=other_args,
                      experiment_name=runner_args.experiment,
                      project=runner_args.project,
                      artifacts=artifacts,
                      resources_needed=resources_needed,
                      metric=runner_args.metric,
                      max_duration=runner_args.max_duration,
                      dependency_policy=StudioDependencyPolicy(),
                  )
              ]
  
          queue = model.get_queue(
              queue_name=runner_args.queue,
              cloud=runner_args.cloud,
              config=config,
              close_after=queueLifetime,
              logger=logger,
              verbose=verbose,
          )
  
          queue_name = submit_experiments(
              experiments, config=config, logger=logger, queue=queue
          )
  
          spin_up_workers(
              runner_args,
              config,
              resources_needed,
              queue_name=queue_name,
              verbose=verbose,
          )
  
      return
  
imports: import copy
  import sys
  import argparse
  import re
  import os
  import uuid
  import importlib
  import time
  import six
  import numpy
  from studio.credentials.credentials import KEY_CREDENTIALS
  from studio.hyperparameter import HyperparameterParser
  from studio.extra_util import Progbar, rsync_cp
  from studio.experiments.experiment import create_experiment
  from studio.experiment_submitter import submit_experiments
  from studio import model
  from studio import git_util
  from studio import local_worker
  from studio import fs_tracker
  from studio.dependencies_policies.studio_dependencies_policy import StudioDependencyPolicy
  from studio.util import logs, util
label_desc: Use image node_id 1 for calling a global function with example usage: main(args) without return types