code: def load_obj(dir_name, obj=None, load_design_info=True):
      """Loads the pickled files which are pickled by
      `~greykite.framework.templates.pickle_utils.dump_obj`.
      Originally intended for loading the
      `~greykite.framework.pipeline.pipeline.ForecastResult` instance,
      but could potentially used for other objects.
  
      Parameters
      ----------
      dir_name : `str`
          The directory that stores the pickled files.
          Must be the top level dir when having nested pickling results.
      obj : `object`, default None
          The object type for the next-level files.
          Can be one of "list", "tuple", "dict", "ordered_dict" or a class.
      load_design_info : `bool`, default True
          Whether to load the design info in `ForecastResult`.
          The design info is specifically for Silverkite and can be accessed from
  
              - ForecastResult.model[-1].model_dict["x_design_info"]
              - ForecastResult.forecast.estimator.model_dict["x_design_info"]
              - ForecastResult.backtest.estimator.model_dict["x_design_info"]
  
          The design info is a class from `patsy` and contains a significant amount of
          instances that can not be pickled directly. Recursively loading them takes
          longer to run. If speed is important and you don't need these information,
          you can turn it off.
  
      Returns
      -------
      result : `object`
          The loaded object from the pickled files.
      """
      # Checks if to load design info.
      if (not load_design_info) and (
          isinstance(obj, type) and obj == DesignInfo
      ):
          return None
  
      # Gets file names in the level.
      files = os.listdir(dir_name)
      if not files:
          raise ValueError("dir is empty!")
  
      # Gets the type files if any.
      # Stores in a dictionary with key being the name and value being the loaded value.
      obj_types = {
          file.split(".")[0]: dill.load(
              open(os.path.join(dir_name, file), "rb")
          )
          for file in files
          if ".type" in file
      }
  
      # Gets directories and pickled files.
      # Every type must have a directory with the same name.
      directories = [
          file
          for file in files
          if os.path.isdir(os.path.join(dir_name, file))
      ]
      if not all([directory in obj_types for directory in directories]):
          raise ValueError("type and directories do not match.")
      pickles = [file for file in files if ".pkl" in file]
  
      # Starts loading objects
      if obj is None:
          # obj is None indicates this is the top level directory.
          # This directory can either have 1 .pkl file, or 1 .type file associated with the directory of same name.
          if not obj_types:
              # The only 1 .pkl file case.
              if len(files) > 1:
                  raise ValueError(
                      "Multiple elements found in top level."
                  )
              return dill.load(
                  open(os.path.join(dir_name, files[0]), "rb")
              )
          else:
              # The .type + dir case.
              if len(obj_types) > 1:
                  raise ValueError(
                      "Multiple elements found in top level"
                  )
              obj_name = list(obj_types.keys())[0]
              obj_type = obj_types[obj_name]
              return load_obj(
                  os.path.join(dir_name, obj_name),
                  obj_type,
                  load_design_info=load_design_info,
              )
      else:
          # If obj is not None, does recursive loading depending on the obj type.
          if obj in ("list", "tuple"):
              # Object is list or tuple.
              # Fetches each element according to the number index to preserve orders.
              result = []
              # Order index is a number appended to the front.
              elements = sorted(
                  pickles + directories,
                  key=lambda x: int(x.split("_")[0]),
              )
              # Recursively loads elements.
              for element in elements:
                  if ".pkl" in element:
                      result.append(
                          dill.load(
                              open(
                                  os.path.join(dir_name, element), "rb"
                              )
                          )
                      )
                  else:
                      result.append(
                          load_obj(
                              os.path.join(dir_name, element),
                              obj_types[element],
                              load_design_info=load_design_info,
                          )
                      )
              if obj == "tuple":
                  result = tuple(result)
              return result
          elif obj == "dict":
              # Object is a dictionary.
              # Fetches keys and values recursively.
              result = {}
              elements = pickles + directories
              keys = [
                  element
                  for element in elements
                  if "__key__" in element
              ]
              values = [
                  element
                  for element in elements
                  if "__value__" in element
              ]
              # Iterates through keys and finds the corresponding values.
              for element in keys:
                  if ".pkl" in element:
                      key = dill.load(
                          open(os.path.join(dir_name, element), "rb")
                      )
                  else:
                      key = load_obj(
                          os.path.join(dir_name, element),
                          obj_types[element],
                          load_design_info=load_design_info,
                      )
                  # Value name could be either with .pkl or a directory.
                  value_name = element.replace("__key__", "__value__")
                  if ".pkl" in value_name:
                      value_name_alt = value_name.replace(".pkl", "")
                  else:
                      value_name_alt = value_name + ".pkl"
                  # Checks if value name is in the dir.
                  if (value_name not in values) and (
                      value_name_alt not in values
                  ):
                      raise FileNotFoundError(
                          f"Value not found for key {key}."
                      )
                  value_name = (
                      value_name
                      if value_name in values
                      else value_name_alt
                  )
                  # Gets the value.
                  if ".pkl" in value_name:
                      value = dill.load(
                          open(os.path.join(dir_name, value_name), "rb")
                      )
                  else:
                      value = load_obj(
                          os.path.join(dir_name, value_name),
                          obj_types[value_name],
                          load_design_info=load_design_info,
                      )
                  # Sets the key, value pair.
                  result[key] = value
              return result
          elif obj == "ordered_dict":
              # Object is OrderedDict.
              # Fetches keys and values according to the number index to preserve orders.
              result = OrderedDict()
              # Order index is a number appended to the front.
              elements = sorted(
                  pickles + directories,
                  key=lambda x: int(x.split("_")[0]),
              )
              keys = [
                  element
                  for element in elements
                  if "__key__" in element
              ]
              values = [
                  element
                  for element in elements
                  if "__value__" in element
              ]
              # Iterates through keys and finds the corresponding values.
              for element in keys:
                  if ".pkl" in element:
                      key = dill.load(
                          open(os.path.join(dir_name, element), "rb")
                      )
                  else:
                      key = load_obj(
                          os.path.join(dir_name, element),
                          obj_types[element],
                          load_design_info=load_design_info,
                      )
                  value_name = element.replace("__key__", "__value__")
                  # Value name could be either with .pkl or a directory.
                  if ".pkl" in value_name:
                      value_name_alt = value_name.replace(".pkl", "")
                  else:
                      value_name_alt = value_name + ".pkl"
                  # Checks if value name is in the dir.
                  if (value_name not in values) and (
                      value_name_alt not in values
                  ):
                      raise FileNotFoundError(
                          f"Value not found for key {key}."
                      )
                  value_name = (
                      value_name
                      if value_name in values
                      else value_name_alt
                  )
                  # Gets the value.
                  if ".pkl" in value_name:
                      value = dill.load(
                          open(os.path.join(dir_name, value_name), "rb")
                      )
                  else:
                      value = load_obj(
                          os.path.join(dir_name, value_name),
                          obj_types[value_name],
                          load_design_info=load_design_info,
                      )
                  # Sets the key, value pair.
                  result[key] = value
              return result
          elif inspect.isclass(obj):
              # Object is a class instance.
              # Creates the class instance and sets the attributes.
              # Some class has required args during initialization,
              # these args are pulled from attributes.
              init_params = list(
                  inspect.signature(obj.__init__).parameters
              )  # init args
              elements = pickles + directories
              # Gets the attribute names and their values in a dictionary.
              values = {}
              for element in elements:
                  if ".pkl" in element:
                      values[element.split(".")[0]] = dill.load(
                          open(os.path.join(dir_name, element), "rb")
                      )
                  else:
                      values[element] = load_obj(
                          os.path.join(dir_name, element),
                          obj_types[element],
                          load_design_info=load_design_info,
                      )
              # Gets the init args from values.
              init_dict = {
                  key: value
                  for key, value in values.items()
                  if key in init_params
              }
              # Some attributes has a "_" at the beginning.
              init_dict.update(
                  {
                      key[1:]: value
                      for key, value in values.items()
                      if (key[1:] in init_params and key[0] == "_")
                  }
              )
              # ``design_info`` does not have column_names attribute,
              # which is required during init.
              # The column_names param is pulled from the column_name_indexes attribute.
              # This can be omitted once we allow dumping @property attributes.
              if "column_names" in init_params:
                  init_dict["column_names"] = values[
                      "column_name_indexes"
                  ].keys()
              # Creates the instance.
              result = obj(**init_dict)
              # Sets the attributes.
              for key, value in values.items():
                  setattr(result, key, value)
              return result
          else:
              # Raises an error if the object is not recognized.
              # This typically does not happen when the source file is dumped
              # with the `dump_obj` function.
              raise ValueError(f"Object {obj} is not recognized.")
  
file_path: greykite/greykite/framework/templates/pickle_utils.py
imports: import inspect
  import os
  import shutil
  from collections import OrderedDict
  import dill
  from patsy.design_info import DesignInfo
label_desc: Use image node_id 3 for calling a global function with example usage: load_obj(dir_name, obj, load_design_info) and returns: None, dill, load_obj, result, result, result, result