class_docstr: Trainable that runs a user function reporting results.
  
  This mode of execution does not support checkpoint/restore.
docstr: Implements train() for a Function API.
  
  If the RunnerThread finishes without reporting "done",
  Tune will automatically provide a magic keyword __duplicate__
  along with a result with "done=True". The TrialRunner will handle the
  result accordingly (see tune/tune_controller.py).
docstr_tok: ["Implements","train","(",")","for","a","Function","API",".","If","the","RunnerThread","finishes","without","reporting","``","done","''",",","Tune","will","automatically","provide","a","magic","keyword","__duplicate__","along","with","a","result","with","``","done=True","''",".","The","TrialRunner","will","handle","the","result","accordingly","(","see","tune\/tune_controller.py",")","."]
code: def step(self):
      """Implements train() for a Function API.
  
      If the RunnerThread finishes without reporting "done",
      Tune will automatically provide a magic keyword __duplicate__
      along with a result with "done=True". The TrialRunner will handle the
      result accordingly (see tune/tune_controller.py).
      """
      session: _TrainSession = get_session()
      if not session.training_started:
          session.start()
  
      training_result: Optional[_TrainingResult] = session.get_next()
  
      if not training_result:
          # The `RESULT_DUPLICATE` result should have been the last
          # result reported by the session, which triggers cleanup.
          raise RuntimeError(
              "Should not have reached here. The TuneController should not "
              "have scheduled another `train` remote call."
              "It should have scheduled a `stop` instead "
              "after the training function exits."
          )
  
      metrics = training_result.metrics
      # This keyword appears if the train_func using the Function API
      # finishes without "done=True". This duplicates the last result, but
      # the TuneController will not log this result again.
      # TuneController will also inject done=True to the result,
      # and proceed to queue up a STOP decision for the trial.
      if RESULT_DUPLICATE in metrics:
          metrics[SHOULD_CHECKPOINT] = False
  
      self._last_training_result = training_result
      if training_result.checkpoint is not None:
          # TODO(justinvyu): Result/checkpoint reporting can be combined.
          # For now, since result/checkpoint reporting is separate, this
          # special key will tell Tune to pull the checkpoint from
          # the `last_training_result`.
          metrics[SHOULD_CHECKPOINT] = True
      return metrics
  
imports: import inspect
  import logging
  import os
  from functools import partial
  from numbers import Number
  from typing import Any, Callable, Dict, Optional, Type
  from ray.air._internal.util import StartTraceback, RunnerThread
  import queue
  from ray.air.constants import _ERROR_FETCH_TIMEOUT
  import ray.train
  from ray.train._internal.checkpoint_manager import _TrainingResult
  from ray.train._internal.session import init_session, get_session, shutdown_session, _TrainSession, TrialInfo
  from ray.tune.execution.placement_groups import PlacementGroupFactory
  from ray.tune.result import DEFAULT_METRIC, RESULT_DUPLICATE, SHOULD_CHECKPOINT
  from ray.tune.trainable import Trainable
  from ray.tune.utils import _detect_checkpoint_function, _detect_config_single, _detect_reporter
  from ray.util.annotations import DeveloperAPI
label_desc: Use image node_id 4 for calling the FunctionTrainable obj's underlying member method code with example usage: obj.step() and returns: metrics