class_docstr: Task Scheduling controller.
  Notice time-zone is ALWAYS UTC
class_docstr_tok: ["Task","Scheduling","controller",".","Notice","time-zone","is","ALWAYS","UTC"]
docstr: scheduling processing step
docstr_tok: ["scheduling","processing","step"]
code: def _step(self):
      # type: () -> bool
      """
      scheduling processing step
      """
      # update next execution datetime
      for j in self._schedule_jobs:
          j.next()
  
      # get idle timeout (aka sleeping)
      scheduled_jobs = sorted(
          [j for j in self._schedule_jobs if j.next_run() is not None],
          key=lambda x: x.next_run(),
      )
      # sort by key
      timeout_job_datetime = (
          min(self._timeout_jobs, key=self._timeout_jobs.get)
          if self._timeout_jobs
          else None
      )
      if not scheduled_jobs and timeout_job_datetime is None:
          # sleep and retry
          seconds = 60.0 * self._sync_frequency_minutes
          self._log(
              "Nothing to do, sleeping for {:.2f} minutes.".format(
                  seconds / 60.0
              )
          )
          sleep(seconds)
          return False
  
      next_time_stamp = (
          scheduled_jobs[0].next_run() if scheduled_jobs else None
      )
      if timeout_job_datetime is not None:
          next_time_stamp = (
              min(next_time_stamp, timeout_job_datetime)
              if next_time_stamp
              else timeout_job_datetime
          )
  
      sleep_time = (next_time_stamp - datetime.utcnow()).total_seconds()
      if sleep_time > 0:
          # sleep until we need to run a job or maximum sleep time
          seconds = min(sleep_time, 60.0 * self._sync_frequency_minutes)
          self._log(
              "Waiting for next run [UTC {}], sleeping for {:.2f} minutes, until next sync.".format(
                  next_time_stamp, seconds / 60.0
              )
          )
          sleep(seconds)
          return False
  
      # check if this is a Task timeout check
      if (
          timeout_job_datetime is not None
          and next_time_stamp == timeout_job_datetime
      ):
          task_id = self._timeout_jobs[timeout_job_datetime]
          self._log("Aborting job due to timeout: {}".format(task_id))
          self._cancel_task(task_id=task_id)
          self._timeout_jobs.pop(timeout_job_datetime, None)
      else:
          self._log("Launching job: {}".format(scheduled_jobs[0]))
          self._launch_job(scheduled_jobs[0])
  
      return True
  
imports: import json
  import logging
  from datetime import datetime
  from threading import Thread, enumerate
  from time import sleep, time
  from typing import List, Union, Optional, Callable, Sequence, Dict
  from attr import attrs, attrib
  from dateutil.relativedelta import relativedelta
  from .job import ClearmlJob
  from .controller import PipelineController
  from ..backend_interface.util import datetime_from_isoformat, datetime_to_isoformat, mutually_exclusive
  from ..task import Task
label_desc: Use image node_id 6 for calling the TaskScheduler obj's underlying member method code with example usage: obj._step() and returns: True, False, False