class_docstr: Sweep controller class.
  
  Internal datastructures on the sweep object to coordinate local controller with
  cloud controller.
  
  Data structures:
      controller: {
          schedule: [
              { id: SCHEDULE_ID
                data: {param1: val1, param2: val2}},
          ]
          earlystop: [RUN_ID, ...]
      scheduler:
          scheduled: [
              { id: SCHEDULE_ID
                runid: RUN_ID},
          ]
  
  `controller` is only updated by the client
  `scheduler` is only updated by the cloud backend
  
  Protocols:
      Scheduling a run:
      - client controller adds a schedule entry on the controller.schedule list
      - cloud backend notices the new entry and creates a run with the parameters
      - cloud backend adds a scheduled entry on the scheduler.scheduled list
      - client controller notices that the run has been scheduled and removes it from
        controller.schedule list
  
  Current implementation details:
      - Runs are only schedule if there are no other runs scheduled.
class_docstr_tok: ["Sweep","controller","class",".","Internal","datastructures","on","the","sweep","object","to","coordinate","local","controller","with","cloud","controller",".","Data","structures",":","controller",":","{","schedule",":","[","{","id",":","SCHEDULE_ID","data",":","{","param1",":","val1",",","param2",":","val2","}","}",",","]","earlystop",":","[","RUN_ID",",","...","]","scheduler",":","scheduled",":","[","{","id",":","SCHEDULE_ID","runid",":","RUN_ID","}",",","]","`","controller","`","is","only","updated","by","the","client","`","scheduler","`","is","only","updated","by","the","cloud","backend","Protocols",":","Scheduling","a","run",":","-","client","controller","adds","a","schedule","entry","on","the","controller.schedule","list","-","cloud","backend","notices","the","new","entry","and","creates","a","run","with","the","parameters","-","cloud","backend","adds","a","scheduled","entry","on","the","scheduler.scheduled","list","-","client","controller","notices","that","the","run","has","been","scheduled","and","removes","it","from","controller.schedule","list","Current","implementation","details",":","-","Runs","are","only","schedule","if","there","are","no","other","runs","scheduled","."]
code: def schedule(self, run: Optional[sweeps.SweepRun]) -> None:
      self._start_if_not_started()
  
      # only schedule one run at a time (for now)
      if self._controller and self._controller.get("schedule"):
          return
  
      schedule_id = _id_generator()
  
      if run is None:
          schedule_list = [{"id": schedule_id, "data": {"args": None}}]
      else:
          param_list = [
              "{}={}".format(k, v.get("value"))
              for k, v in sorted(run.config.items())
          ]
          self._log_actions.append(("schedule", ",".join(param_list)))
  
          # schedule one run
          schedule_list = [
              {"id": schedule_id, "data": {"args": run.config}}
          ]
  
      self._controller["schedule"] = schedule_list
      self._sweep_object_sync_to_backend()
  
imports: import json
  import os
  import random
  import string
  import time
  from typing import Callable, Dict, List, Optional, Tuple, Union
  import yaml
  from wandb import env
  from wandb.apis import InternalApi
  from wandb.sdk import wandb_sweep
  from wandb.sdk.launch.sweeps.utils import handle_sweep_config_violations, sweep_config_err_text_from_jsonschema_violations
  from wandb.util import get_module
label_desc: Use image node_id 33 for calling the _WandbController obj's underlying member method code with example usage: obj.schedule(run) without return types