Skip to content

Commit fe79211

Browse files
committed
Improve abc/title seq internals, validation, docs
1 parent 930e3c6 commit fe79211

File tree

2 files changed

+59
-28
lines changed

2 files changed

+59
-28
lines changed

proplot/axes/base.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -296,14 +296,17 @@
296296

297297
# Format docstrings
298298
_axes_format_docstring = """
299-
title : str, optional
300-
The axes title.
301-
abc : bool or str or tuple, default: :rc:`abc`
299+
title : str or sequence, optional
300+
The axes title. Can optionally be a sequence strings, in which case
301+
the title will be selected from the sequence according to `~Axes.number`.
302+
abc : bool or str or sequence, default: :rc:`abc`
302303
The "a-b-c" subplot label style. Must contain the character ``a`` or ``A``,
303304
for example ``'a.'``, or ``'A'``. If ``True`` then the default style of
304305
``'a'`` is used. The ``a`` or ``A`` is replaced with the alphabetic character
305306
matching the `~Axes.number`. If `~Axes.number` is greater than 26, the
306307
characters loop around to a, ..., z, aa, ..., zz, aaa, ..., zzz, etc.
308+
Can also be a sequence of strings, in which case the "a-b-c" label
309+
will simply be selected from the sequence according to `~Axes.number`.
307310
abcloc, titleloc : str, default: :rc:`abc.loc`, :rc:`title.loc`
308311
Strings indicating the location for the a-b-c label and main title.
309312
The following locations are valid:
@@ -345,17 +348,17 @@
345348
The horizontal padding between a-b-c labels and titles in the same location.
346349
%(units.pt)s
347350
ltitle, ctitle, rtitle, ultitle, uctitle, urtitle, lltitle, lctitle, lrtitle \
348-
: str, tuple, optional
351+
: str or sequence, optional
349352
Shorthands for the below keywords.
350353
lefttitle, centertitle, righttitle, upperlefttitle, uppercentertitle, upperrighttitle, \
351-
lowerlefttitle, lowercentertitle, lowerrighttitle : str, tuple, optional
352-
Additional titles in specific positions. This works as an alternative
353-
to the ``ax.format(title='Title', titleloc=loc)`` workflow and permits
354-
adding more than one title-like label for a single axes.
354+
lowerlefttitle, lowercentertitle, lowerrighttitle : str or sequence, optional
355+
Additional titles in specific positions (see `title` for details). This works as
356+
an alternative to the ``ax.format(title='Title', titleloc=loc)`` workflow and
357+
permits adding more than one title-like label for a single axes.
355358
a, alpha, fc, facecolor, ec, edgecolor, lw, linewidth, ls, linestyle : default: \
356359
:rc:`axes.alpha`, :rc:`axes.facecolor`, :rc:`axes.edgecolor`, :rc:`axes.linewidth`, '-'
357360
Additional settings applied to the background patch, and their
358-
shorthands. Defaults are the ``'axes'`` properties.
361+
shorthands. Their defaults values are the ``'axes'`` properties.
359362
"""
360363
_figure_format_docstring = """
361364
rowlabels, collabels, llabels, tlabels, rlabels, blabels
@@ -2285,20 +2288,30 @@ def _update_abc(self, **kwargs):
22852288
self._abc_border_kwargs.update(kwb)
22862289

22872290
# A-b-c labels. Build as a...z...aa...zz...aaa...zzz
2291+
# NOTE: The abc string should already be validated here
22882292
abc = rc.find('abc', context=True) # 1st run, or changed
22892293
if abc is True:
22902294
abc = 'a'
2291-
if isinstance(abc, tuple):
2292-
kw['text'] = abc[self.number - 1]
2293-
elif abc and (not isinstance(abc, str) or 'a' not in abc and 'A' not in abc):
2294-
raise ValueError(f'Invalid style {abc!r}. Must include letter "a" or "A"')
2295-
if isinstance(abc, str) and self.number is not None:
2295+
if abc is False:
2296+
abc = ''
2297+
if abc is None or self.number is None:
2298+
pass
2299+
elif isinstance(abc, str):
22962300
nabc, iabc = divmod(self.number - 1, 26)
2297-
old = re.search('[aA]', abc).group() # return the *first* 'a' or 'A'
2298-
new = (nabc + 1) * ABC_STRING[iabc]
2299-
new = new.upper() if old == 'A' else new
2300-
abc = abc.replace(old, new, 1)
2301-
kw['text'] = abc or ''
2301+
if abc: # should have been validated to contain 'a' or 'A'
2302+
old = re.search('[aA]', abc).group() # return first occurrence
2303+
new = (nabc + 1) * ABC_STRING[iabc]
2304+
new = new.upper() if old == 'A' else new
2305+
abc = abc.replace(old, new, 1) # replace first occurrence
2306+
kw['text'] = abc
2307+
else:
2308+
if self.number > len(abc):
2309+
raise ValueError(
2310+
f'Invalid abc list length {len(abc)} '
2311+
f'for axes with number {self.number}.'
2312+
)
2313+
else:
2314+
kw['text'] = abc[self._number - 1]
23022315

23032316
# Update a-b-c label
23042317
loc = rc.find('abc.loc', context=True)
@@ -2371,10 +2384,22 @@ def _update_title(self, loc, title=None, **kwargs):
23712384
# necesssary. For inner panels, use the border and bbox settings.
23722385
if loc not in ('left', 'right', 'center'):
23732386
kw.update(self._title_border_kwargs)
2374-
if isinstance(title, tuple):
2375-
kw['text'] = title[self.number - 1]
2376-
elif title is not None:
2387+
if title is None:
2388+
pass
2389+
elif isinstance(title, str):
23772390
kw['text'] = title
2391+
elif np.iterable(title) and all(isinstance(_, str) for _ in title):
2392+
if self.number is None:
2393+
pass
2394+
elif self.number > len(title):
2395+
raise ValueError(
2396+
f'Invalid title list length {len(title)} '
2397+
f'for axes with number {self.number}.'
2398+
)
2399+
else:
2400+
kw['text'] = title[self.number - 1]
2401+
else:
2402+
raise ValueError(f'Invalid title {title!r}. Must be string(s).')
23782403
kw.update(kwargs)
23792404
self._title_dict[loc].update(kw)
23802405

proplot/internals/rcsetup.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,21 @@ def _validate_abc(value):
181181
Validate a-b-c setting.
182182
"""
183183
try:
184-
if type(value) is not tuple:
184+
if np.iterable(value):
185+
return all(map(_validate_bool, value))
186+
else:
185187
return _validate_bool(value)
186188
except ValueError:
187189
pass
188-
if isinstance(value, str) and 'a' in value.lower():
189-
return value
190-
if isinstance(value, tuple):
191-
return value
192-
raise ValueError("A-b-c specification must be string containing 'a' or 'A'")
190+
if isinstance(value, str):
191+
if 'a' in value.lower():
192+
return value
193+
else:
194+
if all(isinstance(_, str) for _ in value):
195+
return tuple(value)
196+
raise ValueError(
197+
"A-b-c setting must be string containing 'a' or 'A' or sequence of strings."
198+
)
193199

194200

195201
def _validate_belongs(*options):

0 commit comments

Comments
 (0)