class_docstr: Make sure user-visible error messages look correct.
  
  These errors are a little annoying to test, but they're important! So we
  are testing them word-for-word as much as possible. Even though this
  *feels* like an antipattern, it isn't: we're making sure the codepaths
  that pull useful debug info from the code are working.
code: def test_hash_funcs_acceptable_keys(self):
      @st.cache
      def unhashable_type_func():
          return (x for x in range(1))
  
      @st.cache(hash_funcs={types.GeneratorType: id})
      def hf_key_as_type():
          return (x for x in range(1))
  
      @st.cache(hash_funcs={"builtins.generator": id})
      def hf_key_as_str():
          return (x for x in range(1))
  
      with self.assertRaises(hashing.UnhashableTypeError) as cm:
          unhashable_type_func()
  
      self.assertEqual(list(hf_key_as_type()), list(hf_key_as_str()))
  
imports: import threading
  import types
  from unittest.mock import Mock, patch
  from parameterized import parameterized
  import streamlit
  from streamlit.elements import exception
  from streamlit.proto.Exception_pb2 import Exception
  from streamlit.runtime.legacy_caching import caching, hashing
  from tests import testutil
  from tests.delta_generator_test_case import DeltaGeneratorTestCase
label_desc: Use image node_id 4 for calling the CacheErrorsTest obj's underlying member method code with example usage: obj.test_hash_funcs_acceptable_keys() and returns: unknown, unknown, unknown