Skip to content

Commit f57f30e

Browse files
committed
Add cbook function for str formatting.
1 parent d11e53d commit f57f30e

File tree

4 files changed

+60
-19
lines changed

4 files changed

+60
-19
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2623,8 +2623,9 @@ def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge",
26232623
label texts will be the data values formatted with *fmt*.
26242624
26252625
fmt : str or callable, default: '%g'
2626-
A format string for the label or a function to call with the value
2627-
as the first argument.
2626+
An unnamed %-style or {}-style format string for the label or a
2627+
function to call with the value as the first argument.
2628+
When *fmt* is a string, %-style takes precedence over {}-style.
26282629
26292630
label_type : {'edge', 'center'}, default: 'edge'
26302631
The label type. Possible values:
@@ -2747,21 +2748,11 @@ def sign(x):
27472748
lbl = ''
27482749

27492750
if lbl is None:
2750-
if callable(fmt):
2751-
formatted_value = fmt(value)
2752-
elif isinstance(fmt, str):
2753-
if fmt.count('{:') == fmt.count('}') == 1:
2754-
formatted_value = fmt.format(value)
2755-
else:
2756-
formatted_value = fmt % value
2757-
else:
2758-
raise ValueError(
2759-
'fmt expected to be a callable or a format string. '
2760-
f'Got "{fmt}".'
2761-
)
2762-
else:
2763-
formatted_value = lbl
2764-
annotation = self.annotate(formatted_value,
2751+
try:
2752+
lbl = fmt(value)
2753+
except TypeError:
2754+
lbl = cbook._auto_format_str(fmt, value)
2755+
annotation = self.annotate(lbl,
27652756
xy, xytext, textcoords="offset points",
27662757
ha=ha, va=va, **kwargs)
27672758
annotations.append(annotation)

lib/matplotlib/cbook/__init__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,3 +2312,35 @@ def _unpack_to_numpy(x):
23122312
if isinstance(xtmp, np.ndarray):
23132313
return xtmp
23142314
return x
2315+
2316+
2317+
def _auto_format_str(fmt, value):
2318+
"""
2319+
Apply *value* to the format string *fmt*.
2320+
2321+
This works both with unnamed %-style formatting and
2322+
unnamed {}-style formatting. %-style formatting has priority.
2323+
If *fmt* is %-style formattable that will be used. Otherwise,
2324+
{}-formatting is applied. Strings without formatting placeholders
2325+
are passed through as is.
2326+
2327+
Examples
2328+
--------
2329+
>>> _auto_format_str('%.2f m', 0.2)
2330+
'0.20 m'
2331+
>>> _auto_format_str('{} m', 0.2)
2332+
'0.2 m'
2333+
>>> _auto_format_str('const', 0.2)
2334+
'const'
2335+
>>> _auto_format_str('%d or {}', 0.2)
2336+
'0 or {}'
2337+
"""
2338+
try:
2339+
lbl = fmt % value
2340+
# when used in `Axes.bar_label()`` this doesn't always raise an error
2341+
# when the {}-style formatting should be used instead of %-style
2342+
if lbl == fmt:
2343+
raise TypeError
2344+
return lbl
2345+
except (TypeError, ValueError):
2346+
return fmt.format(value)

lib/matplotlib/tests/test_axes.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7762,10 +7762,13 @@ def test_bar_label_location_errorbars():
77627762
assert labels[1].get_va() == 'top'
77637763

77647764

7765-
def test_bar_label_fmt():
7765+
@pytest.mark.parametrize('fmt', [
7766+
'%.2f', '{:.2f}', '{:.2f}'.format
7767+
])
7768+
def test_bar_label_fmt(fmt):
77667769
ax = plt.gca()
77677770
rects = ax.bar([1, 2], [3, -4])
7768-
labels = ax.bar_label(rects, fmt='%.2f')
7771+
labels = ax.bar_label(rects, fmt=fmt)
77697772
assert labels[0].get_text() == '3.00'
77707773
assert labels[1].get_text() == '-4.00'
77717774

lib/matplotlib/tests/test_cbook.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,3 +895,18 @@ def test_safe_first_element_with_none():
895895
datetime_lst[0] = None
896896
actual = cbook._safe_first_non_none(datetime_lst)
897897
assert actual is not None and actual == datetime_lst[1]
898+
899+
900+
@pytest.mark.parametrize('fmt, value, result', [
901+
('%.2f m', 0.2, '0.20 m'),
902+
('{:.2f} m', 0.2, '0.20 m'),
903+
('{} m', 0.2, '0.2 m'),
904+
('const', 0.2, 'const'),
905+
('%d or {}', 0.2, '0 or {}'),
906+
('{{{:,.0f}}}', 2e5, '{200,000}'),
907+
('{:.2%}', 2/3, '66.67%'),
908+
('$%g', 2.54, '$2.54'),
909+
])
910+
def test_auto_format_str(fmt, value, result):
911+
"""Apply *value* to the format string *fmt*."""
912+
assert cbook._auto_format_str(fmt, value) == result

0 commit comments

Comments
 (0)