code: def logs(
      obj,
      input_path,
      stdout=None,
      stderr=None,
      both=None,
      timestamps=False,
  ):
      types = set()
      if stdout:
          types.add("stdout")
          both = False
      if stderr:
          types.add("stderr")
          both = False
      if both:
          types.update(("stdout", "stderr"))
  
      streams = list(sorted(types, reverse=True))
  
      # Pathspec can either be run_id/step_name or run_id/step_name/task_id.
      parts = input_path.split("/")
      if len(parts) == 2:
          run_id, step_name = parts
          task_id = None
      elif len(parts) == 3:
          run_id, step_name, task_id = parts
      else:
          raise CommandException(
              "input_path should either be run_id/step_name "
              "or run_id/step_name/task_id"
          )
  
      datastore_set = TaskDataStoreSet(
          obj.flow_datastore,
          run_id,
          steps=[step_name],
          allow_not_done=True,
      )
      if task_id:
          ds_list = [
              TaskDataStore(
                  obj.flow_datastore,
                  run_id=run_id,
                  step_name=step_name,
                  task_id=task_id,
                  mode="r",
                  allow_not_done=True,
              )
          ]
      else:
          ds_list = list(datastore_set)  # get all tasks
  
      if ds_list:
  
          def echo_unicode(line, **kwargs):
              click.secho(
                  line.decode("UTF-8", errors="replace"), **kwargs
              )
  
          # old style logs are non mflog-style logs
          maybe_old_style = True
          for ds in ds_list:
              echo(
                  "Dumping logs of run_id=*{run_id}* "
                  "step=*{step}* task_id=*{task_id}*".format(
                      run_id=ds.run_id,
                      step=ds.step_name,
                      task_id=ds.task_id,
                  ),
                  fg="magenta",
              )
  
              for stream in streams:
                  echo(stream, bold=True)
                  logs = ds.load_logs(LOG_SOURCES, stream)
                  if any(data for _, data in logs):
                      # attempt to read new, mflog-style logs
                      for line in mflog.merge_logs(
                          [blob for _, blob in logs]
                      ):
                          if timestamps:
                              ts = mflog.utc_to_local(line.utc_tstamp)
                              tstamp = ts.strftime(
                                  "%Y-%m-%d %H:%M:%S.%f"
                              )[:-3]
                              click.secho(
                                  tstamp + " ",
                                  fg=LOGGER_TIMESTAMP,
                                  nl=False,
                              )
                          echo_unicode(line.msg)
                      maybe_old_style = False
                  elif maybe_old_style:
                      # if they are not available, we may be looking at
                      # a legacy run (unless we have seen new-style data already
                      # for another stream). This return an empty string if
                      # nothing is found
                      log = ds.load_log_legacy(stream)
                      if log and timestamps:
                          raise CommandException(
                              "We can't show --timestamps for old runs. Sorry!"
                          )
                      echo_unicode(log, nl=False)
      else:
          raise CommandException(
              "No Tasks found at the given path -- "
              "either none exist or none have started yet"
          )
  
imports: import inspect
  import sys
  import traceback
  from datetime import datetime
  from functools import wraps
  import metaflow.tracing
  from metaflow._vendor import click
  from .None import lint
  from .None import plugins
  from .None import parameters
  from .None import decorators
  from .None import metaflow_version
  from .None import namespace
  from .None import current
  from .cli_args import cli_args
  from .tagging_util import validate_tags
  from .util import resolve_identity, decompress_list, write_latest_run_id, get_latest_run_id
  from .task import MetaflowTask
  from .exception import CommandException, MetaflowException
  from .graph import FlowGraph
  from .datastore import FlowDataStore, TaskDataStoreSet, TaskDataStore
  from .runtime import NativeRuntime
  from .package import MetaflowPackage
  from .plugins import DATASTORES, ENVIRONMENTS, LOGGING_SIDECARS, METADATA_PROVIDERS, MONITOR_SIDECARS
  from .metaflow_config import DEFAULT_DATASTORE, DEFAULT_ENVIRONMENT, DEFAULT_EVENT_LOGGER, DEFAULT_METADATA, DEFAULT_MONITOR, DEFAULT_PACKAGE_SUFFIXES
  from .metaflow_environment import MetaflowEnvironment
  from .pylint_wrapper import PyLint
  from .R import use_r, metaflow_r_version
  from .mflog import mflog, LOG_SOURCES
  from .unbounded_foreach import UBF_CONTROL, UBF_TASK
label_desc: Use image node_id 11 for calling a global function with example usage: logs(obj, input_path, stdout, stderr, both, timestamps) without return types