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_user_hash_error(self):
      class MyObj(object):
          pass
  
      def bad_hash_func(x):
          x += 10  # Throws a TypeError since x has type MyObj.
          return x
  
      @st.cache(hash_funcs={MyObj: bad_hash_func})
      def user_hash_error_func(x):
          pass
  
      with self.assertRaises(hashing.UserHashError) as cm:
          my_obj = MyObj()
          user_hash_error_func(my_obj)
  
      ep = ExceptionProto()
      exception.marshall(ep, cm.exception)
  
      self.assertEqual(ep.type, "TypeError")
      self.assertTrue(
          normalize_md(ep.message).startswith(
              normalize_md(
                  """
  unsupported operand type(s) for +=: 'MyObj' and 'int'
  
  This error is likely due to a bug in `bad_hash_func()`, which is a user-defined
  hash function that was passed into the `@st.cache` decorator of `user_hash_error_func()`.
  
  `bad_hash_func()` failed when hashing an object of type
  `tests.streamlit.runtime.legacy_caching.caching_test.CacheErrorsTest.test_user_hash_error.<locals>.MyObj`.  If you
  don't know where that object is coming from, try looking at the hash chain below
  for an object that you do recognize, then pass that to `hash_funcs` instead:
  
  ```
  Object of type tests.streamlit.runtime.legacy_caching.caching_test.CacheErrorsTest.test_user_hash_error.<locals>.MyObj:
  <tests.streamlit.runtime.legacy_caching.caching_test.CacheErrorsTest.test_user_hash_error.<locals>.MyObj object at
                      """
              )
          )
      )
  
      # Stack trace doesn't show in test :(
      # self.assertNotEqual(len(ep.stack_trace), 0)
      self.assertEqual(ep.message_is_markdown, True)
      self.assertEqual(ep.is_warning, False)
  
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 5 for calling the CacheErrorsTest obj's underlying member method code with example usage: obj.test_user_hash_error() and returns: x