code: def dump_obj(
      obj,
      dir_name,
      obj_name="obj",
      dump_design_info=True,
      overwrite_exist_dir=False,
      top_level=True,
  ):
      """Uses DFS to recursively dump an object to pickle files.
      Originally intended for dumping the
      `~greykite.framework.pipeline.pipeline.ForecastResult` instance,
      but could potentially used for other objects.
  
      For each object, if it's picklable, a file with {object_name}.pkl will be
      generated, otherwise, depending on its type, a {object_name}.type file will
      be generated storing it's type, and a folder with {object_name} will be generated
      to store each of its elements/attributes.
  
      For example, if the folder to store results is forecast_result, the items in the
      folders could be:
  
          - timeseries.pkl: a picklable item.
          - model.type: model is not picklable, this file includes the class (Pipeline)
          - model: this folder includes the elements in model.
          - forecast.type: forecast is not picklable, this file includes the class (UnivariateForecast)
          - forecast: this folder includes the elements in forecast.
          - backtest.type: backtest is not picklable, this file includes the class (UnivariateForecast)
          - backtest: this folder includes the elements in backtest.
          - grid_search.type: grid_search is not picklable, this file includes the class (GridSearchCV)
          - grid_search: this folder includes the elements in grid_search.
  
      The items in each subfolder follows the same rule.
  
      The current supported recursion types are:
  
          - list/tuple: type name is "list" or "tuple", each element is attempted to
            be pickled independently if the entire list/tuple is not picklable.
            The order is preserved.
          - OrderedDict: type name is "ordered_dict", each key and value are attempted
            to be pickled independently if the entire dict is not picklable.
            The order is preserved.
          - dict: type name is "dict", each key and value are attempted to be pickled
            independently if the entire dict is not picklable.
            The order is not preserved.
          - class instance: type name is the class object, used to create new instance.
            Each attribute is attempted to be pickled independently if the entire
            instance is not picklable.
  
      Parameters
      ----------
      obj : `object`
          The object to be pickled.
      dir_name : `str`
          The directory to store the pickled results.
      obj_name : `str`, default "obj"
          The name for the pickled items. Applies to the top level object only
          when recursion is used.
      dump_design_info : `bool`, default True
          Whether to dump 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 pickling them takes
          longer to run. If speed is important and you don't need these information,
          you can turn it off.
      overwrite_exist_dir : `bool`, default False
          If True and the directory in ``dir_name`` already exists, the existing
          directory will be removed.
          If False and the directory in ``dir_name`` already exists, an exception
          will be raised.
      top_level : `bool`, default True
          Whether the implementation is an initial call
          (applies to the root object you want to pickle, not a recursive call).
          When you use this function to dump an object, this parameter should always be True.
          Only top level checks if the dir exists,
          because subsequent recursive calls may write files to the same directory,
          and the check for dir exists will not be implemented.
          Setting this parameter to False may cause problems.
  
      Returns
      -------
      The function writes files to local directory and does not return anything.
      """
      # Checks if to dump design info.
      if (not dump_design_info) and (
          isinstance(obj, DesignInfo)
          or (isinstance(obj, str) and obj == "x_design_info")
      ):
          return
  
      # Checks if directory already exists.
      if top_level:
          dir_already_exist = os.path.exists(dir_name)
          if dir_already_exist:
              if not overwrite_exist_dir:
                  raise FileExistsError(
                      "The directory already exists. "
                      "Please either specify a new directory or "
                      "set overwrite_exist_dir to True to overwrite it."
                  )
              else:
                  if os.path.isdir(dir_name):
                      # dir exists as a directory.
                      shutil.rmtree(dir_name)
                  else:
                      # dir exists as a file.
                      os.remove(dir_name)
  
      # Creates the directory.
      # None top-level may write to the same directory,
      # so we allow existing directory in this case.
      try:
          os.mkdir(dir_name)
      except FileExistsError:
          pass
  
      # Start dumping recursively.
      try:
          # Attempts to directly dump the object.
          dill.dump(
              obj, open(os.path.join(dir_name, f"{obj_name}.pkl"), "wb")
          )
      except NotImplementedError:
          # Direct dumping fails.
          # Removed the failed file.
          try:
              os.remove(os.path.join(dir_name, f"{obj_name}.pkl"))
          except FileNotFoundError:
              pass
          # Attempts to do recursive dumping depending on the object type.
          if isinstance(obj, OrderedDict):
              # For OrderedDict (there are a lot in `pasty.design_info.DesignInfo`),
              # recursively dumps the keys and values, because keys can be class instances
              # and unpicklable, too.
              # The keys and values have index number appended to the front,
              # so the order is kept.
              dill.dump(
                  "ordered_dict",
                  open(
                      os.path.join(dir_name, f"{obj_name}.type"), "wb"
                  ),
              )  # type "ordered_dict"
              for i, (key, value) in enumerate(obj.items()):
                  name = f"{i}_{str(key)}"
                  dump_obj(
                      key,
                      os.path.join(dir_name, obj_name),
                      f"{name}__key__",
                      dump_design_info=dump_design_info,
                      top_level=False,
                  )
                  dump_obj(
                      value,
                      os.path.join(dir_name, obj_name),
                      f"{name}__value__",
                      dump_design_info=dump_design_info,
                      top_level=False,
                  )
          elif isinstance(obj, dict):
              # For regular dictionary,
              # recursively dumps the keys and values, because keys can be class instances
              # and unpicklable, too.
              # The order is not important.
              dill.dump(
                  "dict",
                  open(
                      os.path.join(dir_name, f"{obj_name}.type"), "wb"
                  ),
              )  # type "dict"
              for key, value in obj.items():
                  name = str(key)
                  dump_obj(
                      key,
                      os.path.join(dir_name, obj_name),
                      f"{name}__key__",
                      dump_design_info=dump_design_info,
                      top_level=False,
                  )
                  dump_obj(
                      value,
                      os.path.join(dir_name, obj_name),
                      f"{name}__value__",
                      dump_design_info=dump_design_info,
                      top_level=False,
                  )
          elif isinstance(obj, (list, tuple)):
              # For list and tuples,
              # recursively dumps the elements.
              # The names have index number appended to the front,
              # so the order is kept.
              dill.dump(
                  type(obj).__name__,
                  open(
                      os.path.join(dir_name, f"{obj_name}.type"), "wb"
                  ),
              )  # type "list"/"tuple"
              for i, value in enumerate(obj):
                  dump_obj(
                      value,
                      os.path.join(dir_name, obj_name),
                      f"{i}_key",
                      dump_design_info=dump_design_info,
                      top_level=False,
                  )
          elif hasattr(obj, "__class__") and not isinstance(obj, type):
              # For class instance,
              # recursively dumps the attributes.
              dill.dump(
                  obj.__class__,
                  open(
                      os.path.join(dir_name, f"{obj_name}.type"), "wb"
                  ),
              )  # type is class itself
              for key, value in obj.__dict__.items():
                  dump_obj(
                      value,
                      os.path.join(dir_name, obj_name),
                      key,
                      dump_design_info=dump_design_info,
                      top_level=False,
                  )
          else:
              # Other unrecognized unpicklable types, not common.
              print(f"I Don't recognize type {type(obj)}")
  
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 2 for calling a global function with example usage: dump_obj(obj, dir_name, obj_name, dump_design_info, overwrite_exist_dir, top_level) without return types