From 791aaf9d2a4a34c774746e90095675e418428073 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sat, 28 Mar 2026 07:49:03 +0100 Subject: [PATCH] MNT: Privatize Formatter attributes Co-authored-by: buddy0452004 --- .../deprecations/31416-TH.rst | 8 ++ lib/matplotlib/dates.py | 16 ++-- .../testing/jpl_units/UnitDblFormatter.py | 2 +- lib/matplotlib/tests/test_ticker.py | 2 +- lib/matplotlib/ticker.py | 95 ++++++++++--------- lib/matplotlib/ticker.pyi | 3 + 6 files changed, 73 insertions(+), 53 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/31416-TH.rst diff --git a/doc/api/next_api_changes/deprecations/31416-TH.rst b/doc/api/next_api_changes/deprecations/31416-TH.rst new file mode 100644 index 000000000000..6d6d4b7b7809 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/31416-TH.rst @@ -0,0 +1,8 @@ +Formatter attributes +~~~~~~~~~~~~~~~~~~~~ + +These following attributes are considered internal and users should not have a need to access them: + +- `.ScalarFormatter`: ``orderOfMagnitude`` and ``format`` +- `.ConciseDateFormatter`: ``offset_format`` +- `.Formatter`: ``locs`` diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index 369e93f1ac6f..cdd7d3b280a4 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -665,6 +665,10 @@ class ConciseDateFormatter(ticker.Formatter): """ + offset_string = _api.deprecate_privatize_attribute( + "3.11", alternative="get_offset()" + ) + def __init__(self, locator, tz=None, formats=None, offset_formats=None, zero_formats=None, show_offset=True, *, usetex=None): """ @@ -719,7 +723,7 @@ def __init__(self, locator, tz=None, formats=None, offset_formats=None, '%Y-%b-%d', '%Y-%b-%d', '%Y-%b-%d %H:%M'] - self.offset_string = '' + self._offset_string = '' self.show_offset = show_offset self._usetex = mpl._val_or_rc(usetex, 'text.usetex') @@ -799,13 +803,13 @@ def format_ticks(self, values): if (self._locator.axis and self._locator.axis.__name__ in ('xaxis', 'yaxis') and self._locator.axis.get_inverted()): - self.offset_string = tickdatetime[0].strftime(offsetfmts[level]) + self._offset_string = tickdatetime[0].strftime(offsetfmts[level]) else: - self.offset_string = tickdatetime[-1].strftime(offsetfmts[level]) + self._offset_string = tickdatetime[-1].strftime(offsetfmts[level]) if self._usetex: - self.offset_string = _wrap_in_tex(self.offset_string) + self._offset_string = _wrap_in_tex(self._offset_string) else: - self.offset_string = '' + self._offset_string = '' if self._usetex: return [_wrap_in_tex(l) for l in labels] @@ -813,7 +817,7 @@ def format_ticks(self, values): return labels def get_offset(self): - return self.offset_string + return self._offset_string def format_data_short(self, value): return num2date(value, tz=self._tz).strftime('%Y-%m-%d %H:%M:%S') diff --git a/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py b/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py index 30a9914015bc..d5b815d1fc67 100644 --- a/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py +++ b/lib/matplotlib/testing/jpl_units/UnitDblFormatter.py @@ -14,7 +14,7 @@ class UnitDblFormatter(ticker.ScalarFormatter): def __call__(self, x, pos=None): # docstring inherited - if len(self.locs) == 0: + if len(self._locs) == 0: return '' else: return f'{x:.12}' diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 478a54b8a317..0d01677acc8c 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -920,7 +920,7 @@ def test_scilimits(self, sci_type, scilimits, lim, orderOfMag, fewticks): ax.yaxis.set_major_locator(mticker.MaxNLocator(4)) tmp_form.set_locs(ax.yaxis.get_majorticklocs()) - assert orderOfMag == tmp_form.orderOfMagnitude + assert orderOfMag == tmp_form._orderOfMagnitude @pytest.mark.parametrize('value, expected', format_data) def test_format_data(self, value, expected): diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 82b502719267..9fe29e22515c 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -202,7 +202,9 @@ class Formatter(TickHelper): """ # some classes want to see all the locs to help format # individual ones - locs = [] + _locs = [] + + locs = _api.deprecate_privatize_attribute("3.11") def __call__(self, x, pos=None): """ @@ -250,7 +252,7 @@ def set_locs(self, locs): This method is called before computing the tick labels because some formatters need to know all tick locations to do so. """ - self.locs = locs + self._locs = locs @staticmethod def fix_minus(s): @@ -458,6 +460,9 @@ class ScalarFormatter(Formatter): """ + orderOfMagnitude = _api.deprecate_privatize_attribute("3.11") + format = _api.deprecate_privatize_attribute("3.11") + def __init__(self, useOffset=None, useMathText=None, useLocale=None, *, usetex=None): useOffset = mpl._val_or_rc(useOffset, 'axes.formatter.useoffset') @@ -465,8 +470,8 @@ def __init__(self, useOffset=None, useMathText=None, useLocale=None, *, self.set_useOffset(useOffset) self.set_usetex(usetex) self.set_useMathText(useMathText) - self.orderOfMagnitude = 0 - self.format = '' + self._orderOfMagnitude = 0 + self._format = '' self._scientific = True self._powerlimits = mpl.rcParams['axes.formatter.limits'] self.set_useLocale(useLocale) @@ -615,13 +620,13 @@ def __call__(self, x, pos=None): """ Return the format for tick value *x* at position *pos*. """ - if len(self.locs) == 0: + if len(self._locs) == 0: return '' else: - xp = (x - self.offset) / (10. ** self.orderOfMagnitude) + xp = (x - self.offset) / (10. ** self._orderOfMagnitude) if abs(xp) < 1e-8: xp = 0 - return self._format_maybe_minus_and_locale(self.format, xp) + return self._format_maybe_minus_and_locale(self._format, xp) def set_scientific(self, b): """ @@ -715,20 +720,20 @@ def get_offset(self): """ Return scientific notation, plus offset. """ - if len(self.locs) == 0: + if len(self._locs) == 0: return '' - if self.orderOfMagnitude or self.offset: + if self._orderOfMagnitude or self.offset: offsetStr = '' sciNotStr = '' if self.offset: offsetStr = self.format_data(self.offset) if self.offset > 0: offsetStr = '+' + offsetStr - if self.orderOfMagnitude: + if self._orderOfMagnitude: if self._usetex or self._useMathText: - sciNotStr = self.format_data(10 ** self.orderOfMagnitude) + sciNotStr = self.format_data(10 ** self._orderOfMagnitude) else: - sciNotStr = '1e%d' % self.orderOfMagnitude + sciNotStr = '1e%d' % self._orderOfMagnitude if self._useMathText or self._usetex: if sciNotStr != '': sciNotStr = r'\times\mathdefault{%s}' % sciNotStr @@ -740,15 +745,15 @@ def get_offset(self): def set_locs(self, locs): # docstring inherited - self.locs = locs - if len(self.locs) > 0: + self._locs = locs + if len(self._locs) > 0: if self._useOffset: self._compute_offset() self._set_order_of_magnitude() self._set_format() def _compute_offset(self): - locs = self.locs + locs = self._locs # Restrict to visible ticks. vmin, vmax = sorted(self.axis.get_view_interval()) locs = np.asarray(locs) @@ -791,19 +796,19 @@ def _set_order_of_magnitude(self): # if using a numerical offset, find the exponent after applying the # offset. When lower power limit = upper <> 0, use provided exponent. if not self._scientific: - self.orderOfMagnitude = 0 + self._orderOfMagnitude = 0 return if self._powerlimits[0] == self._powerlimits[1] != 0: # fixed scaling when lower power limit = upper <> 0. - self.orderOfMagnitude = self._powerlimits[0] + self._orderOfMagnitude = self._powerlimits[0] return # restrict to visible ticks vmin, vmax = sorted(self.axis.get_view_interval()) - locs = np.asarray(self.locs) + locs = np.asarray(self._locs) locs = locs[(vmin <= locs) & (locs <= vmax)] locs = np.abs(locs) if not len(locs): - self.orderOfMagnitude = 0 + self._orderOfMagnitude = 0 return if self.offset: oom = math.floor(math.log10(vmax - vmin)) @@ -814,20 +819,20 @@ def _set_order_of_magnitude(self): else: oom = math.floor(math.log10(val)) if oom <= self._powerlimits[0]: - self.orderOfMagnitude = oom + self._orderOfMagnitude = oom elif oom >= self._powerlimits[1]: - self.orderOfMagnitude = oom + self._orderOfMagnitude = oom else: - self.orderOfMagnitude = 0 + self._orderOfMagnitude = 0 def _set_format(self): # set the format string to format all the ticklabels - if len(self.locs) < 2: + if len(self._locs) < 2: # Temporarily augment the locations with the axis end points. - _locs = [*self.locs, *self.axis.get_view_interval()] + _locs = [*self._locs, *self.axis.get_view_interval()] else: - _locs = self.locs - locs = (np.asarray(_locs) - self.offset) / 10. ** self.orderOfMagnitude + _locs = self._locs + locs = (np.asarray(_locs) - self.offset) / 10. ** self._orderOfMagnitude loc_range = np.ptp(locs) # Curvilinear coordinates can yield two identical points. if loc_range == 0: @@ -835,7 +840,7 @@ def _set_format(self): # Both points might be zero. if loc_range == 0: loc_range = 1 - if len(self.locs) < 2: + if len(self._locs) < 2: # We needed the end points only for the loc_range calculation. locs = locs[:-2] loc_range_oom = int(math.floor(math.log10(loc_range))) @@ -849,9 +854,9 @@ def _set_format(self): else: break sigfigs += 1 - self.format = f'%1.{sigfigs}f' + self._format = f'%1.{sigfigs}f' if self._usetex or self._useMathText: - self.format = r'$\mathdefault{%s}$' % self.format + self._format = r'$\mathdefault{%s}$' % self._format class LogFormatter(Formatter): @@ -1243,7 +1248,7 @@ def set_minor_number(self, minor_number): self._minor_number = minor_number def set_locs(self, locs): - self.locs = np.array(locs) + self._locs = np.array(locs) self._labelled.clear() if not self._minor: @@ -1269,7 +1274,7 @@ def set_locs(self, locs): # the previous, and between the ticks and the next one. Ticks # with smallest minimum are chosen. As tiebreak, the ticks # with smallest sum is chosen. - diff = np.diff(-np.log(1 / self.locs - 1)) + diff = np.diff(-np.log(1 / self._locs - 1)) space_pessimistic = np.minimum( np.concatenate(((np.inf,), diff)), np.concatenate((diff, (np.inf,))), @@ -1279,7 +1284,7 @@ def set_locs(self, locs): + np.concatenate((diff, (0,))) ) good_minor = sorted( - range(len(self.locs)), + range(len(self._locs)), key=lambda i: (space_pessimistic[i], space_sum[i]), )[-self._minor_number:] self._labelled.update(locs[i] for i in good_minor) @@ -1330,11 +1335,11 @@ def __call__(self, x, pos=None): exponent = round(math.log10(1 - x)) s = self._one_minus("10^{%d}" % exponent) elif x < 0.1: - s = self._format_value(x, self.locs) + s = self._format_value(x, self._locs) elif x > 0.9: - s = self._one_minus(self._format_value(1-x, 1-self.locs)) + s = self._one_minus(self._format_value(1-x, 1-self._locs)) else: - s = self._format_value(x, self.locs, sci_notation=False) + s = self._format_value(x, self._locs, sci_notation=False) return r"$\mathdefault{%s}$" % s def format_data_short(self, value): @@ -1440,18 +1445,18 @@ def __call__(self, x, pos=None): If there is no currently offset in the data, it returns the best engineering formatting that fits the given argument, independently. """ - if len(self.locs) == 0 or self.offset == 0: + if len(self._locs) == 0 or self.offset == 0: return self.fix_minus(self.format_data(x)) else: - xp = (x - self.offset) / (10. ** self.orderOfMagnitude) + xp = (x - self.offset) / (10. ** self._orderOfMagnitude) if abs(xp) < 1e-8: xp = 0 - return self._format_maybe_minus_and_locale(self.format, xp) + return self._format_maybe_minus_and_locale(self._format, xp) def set_locs(self, locs): # docstring inherited - self.locs = locs - if len(self.locs) > 0: + self._locs = locs + if len(self._locs) > 0: vmin, vmax = sorted(self.axis.get_view_interval()) if self._useOffset: self._compute_offset() @@ -1465,17 +1470,17 @@ def set_locs(self, locs): # value: self.offset = round((vmin + vmax)/2, 3) # Use log1000 to use engineers' oom standards - self.orderOfMagnitude = math.floor(math.log(vmax - vmin, 1000))*3 + self._orderOfMagnitude = math.floor(math.log(vmax - vmin, 1000))*3 self._set_format() # Simplify a bit ScalarFormatter.get_offset: We always want to use # self.format_data. Also we want to return a non-empty string only if there - # is an offset, no matter what is self.orderOfMagnitude. If there _is_ an - # offset, self.orderOfMagnitude is consulted. This behavior is verified + # is an offset, no matter what is self._orderOfMagnitude. If there _is_ an + # offset, self._orderOfMagnitude is consulted. This behavior is verified # in `test_ticker.py`. def get_offset(self): # docstring inherited - if len(self.locs) == 0: + if len(self._locs) == 0: return '' if self.offset: offsetStr = '' @@ -1483,7 +1488,7 @@ def get_offset(self): offsetStr = self.format_data(self.offset) if self.offset > 0: offsetStr = '+' + offsetStr - sciNotStr = self.format_data(10 ** self.orderOfMagnitude) + sciNotStr = self.format_data(10 ** self._orderOfMagnitude) if self._useMathText or self._usetex: if sciNotStr != '': sciNotStr = r'\times%s' % sciNotStr diff --git a/lib/matplotlib/ticker.pyi b/lib/matplotlib/ticker.pyi index bed288658909..6590faee7b10 100644 --- a/lib/matplotlib/ticker.pyi +++ b/lib/matplotlib/ticker.pyi @@ -24,6 +24,7 @@ class TickHelper: class Formatter(TickHelper): locs: list[float] + _locs: list[float] def __call__(self, x: float, pos: int | None = ...) -> str: ... def format_ticks(self, values: list[float]) -> list[str]: ... def format_data(self, value: float) -> str: ... @@ -58,7 +59,9 @@ class StrMethodFormatter(Formatter): class ScalarFormatter(Formatter): orderOfMagnitude: int + _orderOfMagnitude: int format: str + _format: str def __init__( self, useOffset: bool | float | None = ...,