docstr: Returns the requested object depending on obj_type and sub_type
  
  obj_type can be one of 'root', 'flow', 'run', 'step', 'task',
  or 'artifact'
  
  sub_type describes the aggregation required and can be either:
  'metadata', 'self' or any of obj_type provided that it is slotted below
  the object itself. For example, if obj_type is 'flow', you can
  specify 'run' to get all the runs in that flow.
  A few special rules:
      - 'metadata' is only allowed for obj_type 'task'
      - For obj_type 'artifact', only 'self' is allowed
  A few examples:
      - To get a list of all flows:
          - set obj_type to 'root' and sub_type to 'flow'
      - To get a list of all tasks:
          - set obj_type to 'root' and sub_type to 'task'
      - To get a list of all artifacts in a task:
          - set obj_type to 'task' and sub_type to 'artifact'
      - To get information about a specific flow:
          - set obj_type to 'flow' and sub_type to 'self'
  
  Parameters
  ----------
  obj_type : string
      One of 'root', 'flow', 'run', 'step', 'task', 'artifact' or 'metadata'
  sub_type : string
      Same as obj_type with the addition of 'self'
  filters : dict
      Dictionary with keys 'any_tags', 'tags' and 'system_tags'. If specified
      will return only objects that have the specified tags present. Filters
      are ANDed together so all tags must be present for the object to be returned.
  attempt : int or None
      If None, for metadata and artifacts:
        - returns information about the latest attempt for artifacts
        - returns all metadata across all attempts
      Otherwise, returns information about metadata and artifacts for that
      attempt only.
      NOTE: For older versions of Metaflow (pre 2.4.0), the attempt for
      metadata is not known; in that case, all metadata is returned (as
      if None was passed in).
  
  Return
  ------
      object or list :
          Depending on the call, the type of object return varies
code: def get_object(cls, obj_type, sub_type, filters, attempt, *args):
      """Returns the requested object depending on obj_type and sub_type
  
      obj_type can be one of 'root', 'flow', 'run', 'step', 'task',
      or 'artifact'
  
      sub_type describes the aggregation required and can be either:
      'metadata', 'self' or any of obj_type provided that it is slotted below
      the object itself. For example, if obj_type is 'flow', you can
      specify 'run' to get all the runs in that flow.
      A few special rules:
          - 'metadata' is only allowed for obj_type 'task'
          - For obj_type 'artifact', only 'self' is allowed
      A few examples:
          - To get a list of all flows:
              - set obj_type to 'root' and sub_type to 'flow'
          - To get a list of all tasks:
              - set obj_type to 'root' and sub_type to 'task'
          - To get a list of all artifacts in a task:
              - set obj_type to 'task' and sub_type to 'artifact'
          - To get information about a specific flow:
              - set obj_type to 'flow' and sub_type to 'self'
  
      Parameters
      ----------
      obj_type : string
          One of 'root', 'flow', 'run', 'step', 'task', 'artifact' or 'metadata'
      sub_type : string
          Same as obj_type with the addition of 'self'
      filters : dict
          Dictionary with keys 'any_tags', 'tags' and 'system_tags'. If specified
          will return only objects that have the specified tags present. Filters
          are ANDed together so all tags must be present for the object to be returned.
      attempt : int or None
          If None, for metadata and artifacts:
            - returns information about the latest attempt for artifacts
            - returns all metadata across all attempts
          Otherwise, returns information about metadata and artifacts for that
          attempt only.
          NOTE: For older versions of Metaflow (pre 2.4.0), the attempt for
          metadata is not known; in that case, all metadata is returned (as
          if None was passed in).
  
      Return
      ------
          object or list :
              Depending on the call, the type of object return varies
      """
      type_order = ObjectOrder.type_to_order(obj_type)
      sub_order = ObjectOrder.type_to_order(sub_type)
  
      if type_order is None:
          raise MetaflowInternalError(
              msg="Cannot find type %s" % obj_type
          )
      if type_order >= ObjectOrder.type_to_order("metadata"):
          raise MetaflowInternalError(
              msg="Type %s is not allowed" % obj_type
          )
  
      if sub_order is None:
          raise MetaflowInternalError(
              msg="Cannot find subtype %s" % sub_type
          )
  
      if type_order >= sub_order:
          raise MetaflowInternalError(
              msg="Subtype %s not allowed for %s" % (sub_type, obj_type)
          )
  
      # Metadata is always only at the task level
      if sub_type == "metadata" and obj_type != "task":
          raise MetaflowInternalError(
              msg="Metadata can only be retrieved at the task level"
          )
  
      if attempt is not None:
          try:
              attempt_int = int(attempt)
              if attempt_int < 0:
                  raise ValueError("Attempt can only be positive")
          except ValueError:
              raise ValueError("Attempt can only be a positive integer")
      else:
          attempt_int = None
  
      pre_filter = cls._get_object_internal(
          obj_type,
          type_order,
          sub_type,
          sub_order,
          filters,
          attempt_int,
          *args,
      )
      if attempt_int is None or sub_type != "metadata":
          # If no attempt or not for metadata, just return as is
          return pre_filter
      return MetadataProvider._reconstruct_metadata_for_attempt(
          pre_filter, attempt_int
      )
  
imports: import json
  import os
  import re
  import time
  from collections import namedtuple
  from itertools import chain
  from metaflow.exception import MetaflowInternalError, MetaflowTaggingError
  from metaflow.tagging_util import validate_tag
  from metaflow.util import get_username, resolve_identity_as_tuple, is_stringish
label_desc: Use image node_id 16 for calling the MetadataProvider obj's underlying member method code with example usage: obj.get_object(cls, obj_type, sub_type, filters, attempt) and returns: MetadataProvider, pre_filter