docstr: Add lookup entry in `lookup_dir` for a complex-typed parameter, linking
  the parameter `key`, its `value`, and its `hashed_value`
  
  Parameters
  ----------
  path: Tuple
      The path of keys that leads to `key`
  key: Str
      The parameter name
  value: *
      The value of the parameter `key`
  hashed_value: Str
      The hash produced for `value`
docstr_tok: ["Add","lookup","entry","in","`","lookup_dir","`","for","a","complex-typed","parameter",",","linking","the","parameter","`","key","`",",","its","`","value","`",",","and","its","`","hashed_value","`","Parameters","--","--","--","--","--","path",":","Tuple","The","path","of","keys","that","leads","to","`","key","`","key",":","Str","The","parameter","name","value",":","*","The","value","of","the","parameter","`","key","`","hashed_value",":","Str","The","hash","produced","for","`","value","`"]
code: def add_complex_type_lookup_entry(
      self, path, key, value, hashed_value
  ):
      """Add lookup entry in `lookup_dir` for a complex-typed parameter, linking
      the parameter `key`, its `value`, and its `hashed_value`
  
      Parameters
      ----------
      path: Tuple
          The path of keys that leads to `key`
      key: Str
          The parameter name
      value: *
          The value of the parameter `key`
      hashed_value: Str
          The hash produced for `value`"""
      shelve_params = ["model_initializer", "cv_type"]
      lookup_path = partial(
          os.path.join, self.lookup_dir, *[f"{_}" for _ in path]
      )
  
      if isclass(value) or (key in shelve_params):
          make_dirs(lookup_path(), exist_ok=True)
  
          with shelve.open(lookup_path(f"{key}"), flag="c") as s:
              # NOTE: When reading from shelve file, DO NOT add the ".db" file extension
              s[hashed_value] = value
      elif isinstance(value, pd.DataFrame):
          make_dirs(lookup_path(key), exist_ok=True)
          value.to_csv(
              lookup_path(key, f"{hashed_value}.csv"), index=False
          )
      else:  # Possible types: partial, function, *other
          add_to_json(
              file_path=lookup_path(f"{key}.json"),
              data_to_add=getsource(value),
              key=hashed_value,
              condition=lambda _: hashed_value not in _.keys(),
              default={},
          )
  
imports: from hyperparameter_hunter.feature_engineering import FeatureEngineer, EngineerStep
  from hyperparameter_hunter.i_o.exceptions import EnvironmentInvalidError, EnvironmentInactiveError
  from hyperparameter_hunter.keys.hashing import make_hash_sha256
  from hyperparameter_hunter.compat.keras_helper import keras_callback_to_dict, keras_initializer_to_dict, parameterize_compiled_keras_model
  from hyperparameter_hunter.compat.keras_optimization_helper import initialize_dummy_model
  from hyperparameter_hunter.metrics import Metric
  from hyperparameter_hunter.sentinels import Sentinel
  from hyperparameter_hunter.settings import G
  from hyperparameter_hunter.utils.file_utils import write_json, read_json, add_to_json, make_dirs
  from hyperparameter_hunter.utils.file_utils import RetryMakeDirs
  from hyperparameter_hunter.utils.general_utils import subdict
  from hyperparameter_hunter.utils.boltons_utils import remap, default_enter
  from abc import ABCMeta, abstractmethod
  from copy import deepcopy
  from functools import partial
  from inspect import isclass, getsource
  from os import listdir
  import os.path
  import pandas
  import shelve
label_desc: Use image node_id 8 for calling the KeyMaker obj's underlying member method code with example usage: obj.add_complex_type_lookup_entry(path, key, value, hashed_value) without return types