docs: def get_cached_data(
          step: 'projects.ProjectStep'
  ) -> typing.Union[None, STEP_DATA]:
      """
      Attempts to load and return the cached step data for the specified step. If
      not cached data exists, or the cached data is corrupt, a None value is
      returned instead.
  
      :param step:
          The step for which the cached data should be loaded
  
      :return:
          Either a step data structure containing the cached step data or None
          if no cached data exists for the step
      """
  
      cache_path = step.report.results_cache_path
      if not os.path.exists(cache_path):
          return None
  
      out = create_data(step)
  
      try:
          with open(cache_path, 'r') as f:
              cached_data = json.load(f)
      except Exception:
          return None
  
      file_writes = [
          file_io.entry_from_dict(fw)
          for fw in cached_data['file_writes']
      ]
  
      return out \
          ._replace(**cached_data) \
          ._replace(file_writes=file_writes)