proplot
proplot copied to clipboard
wrong internal state with title setting and unable to make new figures
Description
When I was adjusting the axes titles of a figure, something seemed to break the internal state of proplot. The package does not work anymore, see the error message below. I have no choice but restart the whole python session.
Steps to reproduce
I tried the following code
import proplot as pplt
fig, axs = pplt.subplots(None, 1, 4)
axs[0].format(
title='title',
titleloc='c', titlesize=10, titleweight='normal', titlecolor='gray10'
)
It complains "gray10" is not a good color as follows
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_125973/2407293224.py in <module>
2
3 fig, axs = pplt.subplots(None, 1, 4)
----> 4 axs[0].format(
5 title='title',
6 titleloc='c', titlesize=10, titleweight='normal', titlecolor='gray10'
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/cartesian.py in format(self, aspect, xloc, yloc, xspineloc, yspineloc, xoffsetloc, yoffsetloc, xwraprange, ywraprange, xreverse, yreverse, xlim, ylim, xmin, ymin, xmax, ymax, xscale, yscale, xbounds, ybounds, xmargin, ymargin, xrotation, yrotation, xformatter, yformatter, xticklabels, yticklabels, xticks, yticks, xlocator, ylocator, xminorticks, yminorticks, xminorlocator, yminorlocator, xcolor, ycolor, xlinewidth, ylinewidth, xtickloc, ytickloc, fixticks, xtickdir, ytickdir, xtickminor, ytickminor, xtickrange, ytickrange, xtickcolor, ytickcolor, xticklen, yticklen, xticklenratio, yticklenratio, xtickwidth, ytickwidth, xtickwidthratio, ytickwidthratio, xticklabelloc, yticklabelloc, xticklabeldir, yticklabeldir, xticklabelpad, yticklabelpad, xticklabelcolor, yticklabelcolor, xticklabelsize, yticklabelsize, xticklabelweight, yticklabelweight, xlabel, ylabel, xlabelloc, ylabelloc, xlabelpad, ylabelpad, xlabelcolor, ylabelcolor, xlabelsize, ylabelsize, xlabelweight, ylabelweight, xgrid, ygrid, xgridminor, ygridminor, xgridcolor, ygridcolor, xlabel_kw, ylabel_kw, xscale_kw, yscale_kw, xlocator_kw, ylocator_kw, xformatter_kw, yformatter_kw, xminorlocator_kw, yminorlocator_kw, **kwargs)
987 """
988 rc_kw, rc_mode = _pop_rc(kwargs)
--> 989 with rc.context(rc_kw, mode=rc_mode):
990 # No mutable default args
991 xlabel_kw = xlabel_kw or {}
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/config.py in __enter__(self)
791 rc_old = context.rc_old # used to re-apply settings without copying whole dict
792 for key, value in kwargs.items():
--> 793 kw_proplot, kw_matplotlib = self._get_item_dicts(key, value)
794 for rc_dict, kw_new in zip(
795 (rc_proplot, rc_matplotlib),
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/config.py in _get_item_dicts(self, key, value, skip_cycle)
922 # Get validated key, value, and child keys
923 key, value = self._validate_key(key, value)
--> 924 value = self._validate_value(key, value)
925 keys = (key,) + rcsetup._rc_children.get(key, ()) # settings to change
926 contains = lambda *args: any(arg in keys for arg in args) # noqa: E731
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/config.py in _validate_value(key, value)
884 value = validate_matplotlib[key](value)
885 elif key in validate_proplot:
--> 886 value = validate_proplot[key](value)
887 return value
888
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/internals/rcsetup.py in _validate_color(value, alternative)
264 or not REGEX_NAMED_COLOR.match(value)
265 ):
--> 266 raise ValueError(f'{value!r} is not a valid color arg.') from None
267 return value
268 except Exception as error:
ValueError: 'gray10' is not a valid color arg.
The problem is that I can not make any new figures since then. When I start a new figure in a jupyter cell
fig, axs = pplt.subplots(None, 1, 4)
I get the following error
Error in callback <function install_repl_displayhook.<locals>.post_execute at 0x7f705b74adc0> (for post_execute):
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/pyplot.py in post_execute()
137 def post_execute():
138 if matplotlib.is_interactive():
--> 139 draw_all()
140
141 # IPython >= 2
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/_pylab_helpers.py in draw_all(cls, force)
135 for manager in cls.get_all_fig_managers():
136 if force or manager.canvas.figure.stale:
--> 137 manager.canvas.draw_idle()
138
139
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/backend_bases.py in draw_idle(self, *args, **kwargs)
2053 if not self._is_idle_drawing:
2054 with self._idle_draw_cntx():
-> 2055 self.draw(*args, **kwargs)
2056
2057 def get_width_height(self):
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/figure.py in _canvas_preprocess(self, *args, **kwargs)
461 ctx3 = rc.context(fig._render_context) # draw with figure-specific setting
462 with ctx1, ctx2, ctx3:
--> 463 fig.auto_layout()
464 return func(self, *args, **kwargs)
465
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/figure.py in auto_layout(self, renderer, aspect, tight, resize)
1474 _align_content()
1475 if tight:
-> 1476 gs._auto_layout_tight(renderer)
1477 _align_content()
1478
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/gridspec.py in _auto_layout_tight(self, renderer)
818 pad = self._outerpad
819 obox = fig.bbox_inches # original bbox
--> 820 bbox = fig.get_tightbbox(renderer)
821
822 # Calculate new figure margins
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/figure.py in get_tightbbox(self, renderer, bbox_extra_artists)
1637
1638 for a in artists:
-> 1639 bbox = a.get_tightbbox(renderer)
1640 if bbox is not None and (bbox.width != 0 or bbox.height != 0):
1641 bb.append(bbox)
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/cartesian.py in get_tightbbox(self, renderer, *args, **kwargs)
1310 self._apply_axis_sharing()
1311 self._update_rotation('x')
-> 1312 return super().get_tightbbox(renderer, *args, **kwargs)
1313
1314
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/base.py in get_tightbbox(self, renderer, *args, **kwargs)
2610 if self._inset_parent is not None and self._inset_zoom:
2611 self.indicate_inset_zoom()
-> 2612 self._tight_bbox = super().get_tightbbox(renderer, *args, **kwargs)
2613 return self._tight_bbox
2614
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/axes/_base.py in get_tightbbox(self, renderer, call_axes_locator, bbox_extra_artists, for_layout_only)
4444 if bb_yaxis:
4445 bb.append(bb_yaxis)
-> 4446 self._update_title_position(renderer)
4447 axbbox = self.get_window_extent(renderer)
4448 bb.append(axbbox)
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/base.py in _update_title_position(self, renderer)
2388 # Sync the title position with the a-b-c label position
2389 aobj = self._title_dict['abc']
-> 2390 tobj = self._title_dict[self._abc_loc]
2391 aobj.set_transform(tobj.get_transform())
2392 aobj.set_position(tobj.get_position())
KeyError: None
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
~/local/conda/envs/nemo/lib/python3.8/site-packages/IPython/core/formatters.py in __call__(self, obj)
339 pass
340 else:
--> 341 return printer(obj)
342 # Finally look for special method names
343 method = get_real_method(obj, self.print_method)
~/local/conda/envs/nemo/lib/python3.8/site-packages/IPython/core/pylabtools.py in retina_figure(fig, base64, **kwargs)
166 base64 argument
167 """
--> 168 pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
169 # Make sure that retina_figure acts just like print_figure and returns
170 # None when the figure is empty.
~/local/conda/envs/nemo/lib/python3.8/site-packages/IPython/core/pylabtools.py in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
149 FigureCanvasBase(fig)
150
--> 151 fig.canvas.print_figure(bytes_io, **kw)
152 data = bytes_io.getvalue()
153 if fmt == 'svg':
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/figure.py in _canvas_preprocess(self, *args, **kwargs)
461 ctx3 = rc.context(fig._render_context) # draw with figure-specific setting
462 with ctx1, ctx2, ctx3:
--> 463 fig.auto_layout()
464 return func(self, *args, **kwargs)
465
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/figure.py in auto_layout(self, renderer, aspect, tight, resize)
1474 _align_content()
1475 if tight:
-> 1476 gs._auto_layout_tight(renderer)
1477 _align_content()
1478
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/gridspec.py in _auto_layout_tight(self, renderer)
818 pad = self._outerpad
819 obox = fig.bbox_inches # original bbox
--> 820 bbox = fig.get_tightbbox(renderer)
821
822 # Calculate new figure margins
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/figure.py in get_tightbbox(self, renderer, bbox_extra_artists)
1637
1638 for a in artists:
-> 1639 bbox = a.get_tightbbox(renderer)
1640 if bbox is not None and (bbox.width != 0 or bbox.height != 0):
1641 bb.append(bbox)
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/cartesian.py in get_tightbbox(self, renderer, *args, **kwargs)
1310 self._apply_axis_sharing()
1311 self._update_rotation('x')
-> 1312 return super().get_tightbbox(renderer, *args, **kwargs)
1313
1314
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/base.py in get_tightbbox(self, renderer, *args, **kwargs)
2610 if self._inset_parent is not None and self._inset_zoom:
2611 self.indicate_inset_zoom()
-> 2612 self._tight_bbox = super().get_tightbbox(renderer, *args, **kwargs)
2613 return self._tight_bbox
2614
~/local/conda/envs/nemo/lib/python3.8/site-packages/matplotlib/axes/_base.py in get_tightbbox(self, renderer, call_axes_locator, bbox_extra_artists, for_layout_only)
4444 if bb_yaxis:
4445 bb.append(bb_yaxis)
-> 4446 self._update_title_position(renderer)
4447 axbbox = self.get_window_extent(renderer)
4448 bb.append(axbbox)
~/local/conda/envs/nemo/lib/python3.8/site-packages/proplot/axes/base.py in _update_title_position(self, renderer)
2388 # Sync the title position with the a-b-c label position
2389 aobj = self._title_dict['abc']
-> 2390 tobj = self._title_dict[self._abc_loc]
2391 aobj.set_transform(tobj.get_transform())
2392 aobj.set_position(tobj.get_position())
KeyError: None
Figure(nrows=1, ncols=4, refwidth=2.5)
Proplot version
Paste the results of import matplotlib; print(matplotlib.__version__); import proplot; print(proplot.version)
here.
3.4.3
0.9.5.post284
I think I see the problem -- the rc
state is not reset by Configurator.__exit__
during the error because the error is triggered inside Configurator.__enter__
rather than inside the with...as
block. This leaves rc
in a "user-modification" context mode rather than an "initialization" mode, so the initial values for ax._title_loc
and ax._abc_loc
are unset.
I think the solution is to validate rc values inside of pplt.rc.context
rather than inside of pplt.Configurator.__enter__
and simplify the content in __enter__
, and/or use a try...except
block in __enter__
that calls __exit__
if necessary.
This was a very simple fix -- should have done it much sooner. Fixed by c567329 with a simple try-except clause.