question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

st.cache doesn't work with matplotlib.pyplot (InternalHashError: 'Spines' object does not contain a 'name' spine)

See original GitHub issue

Summary

If function returns matplotlib.pyplot.Figure object, it cannot be cached with st.cache.

Steps to reproduce

Code snippet:

import streamlit as st
import matplotlib.pyplot as plt

@st.cache
def mk_figure():
    fig, ax = plt.subplots()
    ax.plot([0, 1], [0, 1])
    return fig

fig = mk_figure()
st.pyplot(fig)

Expected behavior:

Figure is shown.

Actual behavior:

When application is started, during a long time, I see only message Running mk_figure(). After a while, I get the following message:

InternalHashError: ‘Spines’ object does not contain a ‘name’ spine While caching the return value of mk_figure(), Streamlit encountered an object of type matplotlib.spines.Spines, which it does not know how to hash.

In this specific case, it’s very likely you found a Streamlit bug so please file a bug report here.

In the meantime, you can try bypassing this error by registering a custom hash function via the hash_funcs keyword in @st.cache(). For example:

@st.cache(hash_funcs={matplotlib.spines.Spines: my_hash_func})
def my_func(...):
    ...

If you don’t know where the object of type matplotlib.spines.Spines 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 matplotlib.spines.Spines: <matplotlib.spines.Spines object at 0x7fd19a717640>
Object of type builtins.tuple: ('spines', <matplotlib.spines.Spines object at 0x7fd19a717640>)
Object of type builtins.dict: {'_stale': True, 'stale_callback': None, '_axes': <AxesSubplot:>, 'figure': <Figure size 640x480 with 1 Axes>, '_transform': None, '_transformSet': False, '_visible': True, '_animated': False, '_alpha': None, 'clipbox': None, '_clippath': None, '_clipon': True, '_label': '', '_picker': None, '_contains': None, '_rasterized': False, '_agg_filter': None, '_mouseover': False, '_callbacks': <matplotlib.cbook.CallbackRegistry object at 0x7fd19a7173d0>, '_remove_method': <bound method FigureBase.delaxes of <Figure size 640x480 with 1 Axes>>, '_url': None, '_gid': None, '_snap': None, '_sketch': None, '_path_effects': [], '_sticky_edges': _XYPair(x=[], y=[]), '_in_layout': True, '_position': Bbox([[0.125, 0.10999999999999999], [0.9, 0.88]]), '_originalPosition': Bbox([[0.125, 0.10999999999999999], [0.9, 0.88]]), '_aspect': 'auto', '_adjustable': 'box', '_anchor': 'C', '_stale_viewlim_x': True, '_stale_viewlim_y': True, '_sharex': None, '_sharey': None, 'bbox': <matplotlib.transforms.TransformedBbox object at 0x7fd19a7170a0>, 'dataLim': Bbox([[0.0, 0.0], [1.0, 1.0]]), '_viewLim': Bbox([[0.0, 0.0], [1.0, 1.0]]), 'transScale': <matplotlib.transforms.TransformWrapper object at 0x7fd19a717280>, 'transAxes': <matplotlib.transforms.BboxTransformTo object at 0x7fd19a717400>, 'transLimits': <matplotlib.transforms.BboxTransformFrom object at 0x7fd19a717730>, 'transData': <matplotlib.transforms.CompositeGenericTransform object at 0x7fd19a717670>, '_xaxis_transform': <matplotlib.transforms.BlendedGenericTransform object at 0x7fd19a7175e0>, '_yaxis_transform': <matplotlib.transforms.BlendedGenericTransform object at 0x7fd19a717610>, '_box_aspect': None, '_axes_locator': None, '_colorbars': [], 'spines': <matplotlib.spines.Spines object at 0x7fd19a717640>, 'xaxis': <matplotlib.axis.XAxis object at 0x7fd19a7177f0>, 'yaxis': <matplotlib.axis.YAxis object at 0x7fd19a72b100>, '_facecolor': 'white', '_frameon': True, '_axisbelow': 'line', '_rasterization_zorder': None, 'ignore_existing_data_limits': False, 'callbacks': <matplotlib.cbook.CallbackRegistry object at 0x7fd19a72b670>, '_autoscaleXon': True, '_autoscaleYon': True, '_xmargin': 0.05, '_ymargin': 0.05, '_tight': None, '_use_sticky_edges': True, '_get_lines': <matplotlib.axes._base._process_plot_var_args object at 0x7fd19a72b8e0>, '_get_patches_for_fill': <matplotlib.axes._base._process_plot_var_args object at 0x7fd19a72b6a0>, '_gridOn': False, 'lines': [<matplotlib.lines.Line2D object at 0x7fd19a765d00>], 'patches': [], 'texts': [], 'tables': [], 'artists': [], 'images': [], '_mouseover_set': <matplotlib.cbook._OrderedSet object at 0x7fd19a72ba30>, 'child_axes': [], '_current_image': None, '_projection_init': (<class 'matplotlib.axes._axes.Axes'>, {'sharex': None, 'sharey': None}), 'legend_': None, 'collections': [], 'containers': [], '_autotitlepos': True, 'title': Text(0.5, 1.0, ''), '_left_title': Text(0.0, 1.0, ''), '_right_title': Text(1.0, 1.0, ''), 'titleOffsetTrans': <matplotlib.transforms.ScaledTranslation object at 0x7fd19a74ba00>, 'patch': <matplotlib.patches.Rectangle object at 0x7fd19a74bbb0>, 'axison': True, 'fmt_xdata': None, 'fmt_ydata': None, '_navigate': True, '_navigate_mode': None, '_subplotspec': GridSpec(1, 1)[0:1, 0:1], '_shared_x_axes': None, '_shared_y_axes': None, '_twinned_axes': None}
Object of type matplotlib.axes._subplots.AxesSubplot: AxesSubplot(0.125,0.11;0.775x0.77)
Object of type builtins.tuple: (1, <AxesSubplot:>)
Object of type builtins.list: [(1, <AxesSubplot:>)]
Object of type builtins.tuple: ('_elements', [(1, <AxesSubplot:>)])
Object of type builtins.dict: {'_pos': 0, '_elements': [(1, <AxesSubplot:>)], '_default': None, '_ind': 1}
Object of type matplotlib.figure._AxesStack: <matplotlib.figure._AxesStack object at 0x7fd19a66c610>
Object of type builtins.tuple: ('_localaxes', <matplotlib.figure._AxesStack object at 0x7fd19a66c610>)
Object of type builtins.dict: {'_stale': True, 'stale_callback': None, 'figure': <Figure size 640x480 with 1 Axes>, '_transform': None, '_transformSet': False, '_visible': True, '_animated': False, '_alpha': None, 'clipbox': None, '_clippath': None, '_clipon': True, '_label': '', '_picker': None, '_contains': None, '_rasterized': False, '_agg_filter': None, '_mouseover': False, '_callbacks': <matplotlib.cbook.CallbackRegistry object at 0x7fd19a66c6a0>, '_remove_method': None, '_url': None, '_gid': None, '_snap': None, '_sketch': None, '_path_effects': [], '_sticky_edges': _XYPair(x=[], y=[]), '_in_layout': True, '_suptitle': None, '_supxlabel': None, '_supylabel': None, '_align_label_groups': {'x': <matplotlib.cbook.Grouper object at 0x7fd19a66c9a0>, 'y': <matplotlib.cbook.Grouper object at 0x7fd19a66c970>}, '_gridspecs': [GridSpec(1, 1)], '_localaxes': <matplotlib.figure._AxesStack object at 0x7fd19a66c610>, 'artists': [], 'lines': [], 'patches': [], 'texts': [], 'images': [], 'legends': [], 'subfigs': [], 'suppressComposite': None, 'callbacks': <matplotlib.cbook.CallbackRegistry object at 0x7fd19a66cca0>, '_canvas_callbacks': <matplotlib.cbook.CallbackRegistry object at 0x7fd19a66c5e0>, '_button_pick_id': 0, '_scroll_pick_id': 1, 'bbox_inches': Bbox([[0.0, 0.0], [6.4, 4.8]]), 'dpi_scale_trans': <matplotlib.transforms.Affine2D object at 0x7fd19a66c940>, '_dpi': 100.0, 'bbox': <matplotlib.transforms.TransformedBbox object at 0x7fd19a66ca90>, 'figbbox': <matplotlib.transforms.TransformedBbox object at 0x7fd19a66ca90>, 'transFigure': <matplotlib.transforms.BboxTransformTo object at 0x7fd19a66cac0>, 'transSubfigure': <matplotlib.transforms.BboxTransformTo object at 0x7fd19a66cac0>, 'patch': <matplotlib.patches.Rectangle object at 0x7fd19a66caf0>, 'subplotpars': <matplotlib.figure.SubplotParams object at 0x7fd19a66cbb0>, '_constrained': False, '_tight': False, '_tight_parameters': {}, '_axstack': <matplotlib.figure._AxesStack object at 0x7fd19a66cc70>, '_axobservers': <matplotlib.cbook.CallbackRegistry object at 0x7fd19a66ca60>, '_cachedRenderer': None, '_constrained_layout_pads': {'w_pad': 0.04167, 'h_pad': 0.04167, 'wspace': 0.02, 'hspace': 0.02}, 'number': 1, '__mpl_version__': '3.4.0', '_restore_to_pylab': True}
Object of type matplotlib.figure.Figure: Figure(640x480)

Please see the hash_funcs documentation for more details.

Traceback:
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/script_runner.py", line 333, in _run_script
    exec(code, module.__dict__)
File "/Users/user/prj/svn.math-hse.info/repo/2020-21/nes-datascience/streamlit-demo-2021-url/test_cache_fig.py", line 10, in <module>
    fig = mk_figure()
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/caching.py", line 564, in wrapped_func
    return get_or_create_cached_value()
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/caching.py", line 550, in get_or_create_cached_value
    _write_to_cache(
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/caching.py", line 341, in _write_to_cache
    _write_to_mem_cache(
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/caching.py", line 260, in _write_to_mem_cache
    hash = _get_output_hash(value, func_or_code, hash_funcs)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/caching.py", line 267, in _get_output_hash
    update_hash(
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 91, in update_hash
    ch.update(hasher, val, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 647, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 420, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 414, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 647, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 420, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 414, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 414, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 414, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 647, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 420, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 414, in _to_bytes
    self.update(h, item, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 364, in update
    b = self.to_bytes(obj, context)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 353, in to_bytes
    raise InternalHashError(e, obj)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 339, in to_bytes
    b = b"%s:%s" % (tname, self._to_bytes(obj, context))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/streamlit/hashing.py", line 483, in _to_bytes
    elif hasattr(obj, "name") and (
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/matplotlib/spines.py", line 553, in __getattr__
    raise ValueError(

Is this a regression?

No, doesn’t look so.

Debug info

  • Streamlit version: Streamlit, version 0.79.0
  • Python version: Python 3.9.0
  • Don’t use environment manager.
  • OS version: MacOS X 10.15.7 (19H2)
  • Browser version: Safari Version 14.0.3 (15610.4.3.1.6, 15610)

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:10 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
dbalabkacommented, May 13, 2021

I have found the answer. The reason is the Strimlit tracks output mutations by default (allow_output_mutation=False).

1reaction
StefanBrandcommented, Apr 20, 2021

I actually have a much more complicated figure with many text labels (a big annotated heatmap/confusion matrix). Therefore the bottleneck actually is rendering. This technique allows to cache the rendered image:

import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
import io

@st.cache(suppress_st_warning=True, allow_output_mutation=True)
def plot():
    st.warning("CACHE MISS")
    buf = io.BytesIO()

    arr = np.random.normal(1, 1, size=(100,horizontal_size))
    fig, ax = plt.subplots()
    ax.imshow(arr)
    fig.savefig(buf, format="png")
    return buf

horizontal_size = st.slider("horizontal size", 50,150,step=50)
st.image(plot())
Read more comments on GitHub >

github_iconTop Results From Across the Web

Cache matplotlib figure - Using Streamlit
Problem Streamlit hangs (no output, can only be killed) when I apply @st.cache decorator to function that returns matplotlib figure.
Read more >
How to cache a plot in streamlit? - Stack Overflow
I have built a dashboard in streamlit where you can select a client_ID and have SHAP plots displayed (Waterfall and Force plot) to...
Read more >
Fanilo Andrianasolo on Twitter: "Trouble with using st.cache ...
With the hash_funcs argument, you can go deeply into Streamlit internals and how it manages caching and returning objects between reruns.
Read more >
How to use the streamlit.cache function in streamlit - Snyk
cache function in streamlit. To help you get started, we've selected a few streamlit examples, based on popular ways it is used in...
Read more >
Like Streamlit, but fast. Enabling low-latency data apps.
Python frameworks such as Streamlit and Plotly Dash gave rise to “data ... This problem is alleviated by a caching mechanism that must...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found