st.cache doesn't work with matplotlib.pyplot (InternalHashError: 'Spines' object does not contain a 'name' spine)
See original GitHub issueSummary
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:
- Created 2 years ago
- Comments:10 (2 by maintainers)
Top GitHub Comments
I have found the answer. The reason is the Strimlit tracks output mutations by default (
allow_output_mutation=False
).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: