Skip to content

POC: add validation of formatters to converters #25662

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def __init__(

self.set_figure(axes.figure)
self.axes = axes
self.converter = None

self._loc = loc
self._major = major
Expand Down Expand Up @@ -1821,6 +1822,13 @@ def _set_formatter(self, formatter, level):
_api.warn_external('FixedFormatter should only be used together '
'with FixedLocator')

if hasattr(self, "converter") and self.converter is not None:
if not self.converter.validate_formatter(formatter):
_api.warn_external(
f'Converter {self.converter.__class__.__name__!r} got unexpected '
f'formatter {formatter.__class__.__name__!r}'
)

if level == self.major:
self.isDefault_majfmt = False
else:
Expand Down
4 changes: 4 additions & 0 deletions lib/matplotlib/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ def _validate_unit(unit):
f'Provided unit "{unit}" is not valid for a categorical '
'converter, as it does not have a _mapping attribute.')

@staticmethod
def validate_formatter(formatter):
return isinstance(formatter, (StrCategoryFormatter, ticker.NullFormatter))


class StrCategoryLocator(ticker.Locator):
"""Tick at every integer mapping of the string data."""
Expand Down
10 changes: 10 additions & 0 deletions lib/matplotlib/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1836,6 +1836,13 @@ def default_units(x, axis):
pass
return None

@staticmethod
def validate_formatter(formatter):
return isinstance(formatter, (DateFormatter,
ConciseDateFormatter,
AutoDateFormatter,
ticker.NullFormatter))


class ConciseDateConverter(DateConverter):
# docstring inherited
Expand Down Expand Up @@ -1888,6 +1895,9 @@ def default_units(self, *args, **kwargs):
def convert(self, *args, **kwargs):
return self._get_converter().convert(*args, **kwargs)

def validate_formatter(self, formatter):
return self._get_converter().validate_formatter(formatter)


units.registry[np.datetime64] = \
units.registry[datetime.date] = \
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ def test_autofmt_xdate(which):
warnings.filterwarnings(
'ignore',
'FixedFormatter should only be used together with FixedLocator')
warnings.filterwarnings('ignore', 'Converter .* got unexpected formatter .*')
ax.xaxis.set_minor_formatter(FixedFormatter(minors))

fig.autofmt_xdate(0.2, angle, 'right', which)
Expand Down
23 changes: 23 additions & 0 deletions lib/matplotlib/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ def default_units(x, axis):
"Return the default unit for x or None."
return 'date'

@staticmethod
def validate_formatter(formatter):
"Return whether the formatter works with this converter"
return isinstance(formatter, (dates.DateFormatter,
dates.ConciseDateFormatter,
dates.AutoDateFormatter))

# Finally we register our object type with the Matplotlib units registry.
units.registry[datetime.date] = DateConverter()
"""
Expand Down Expand Up @@ -131,6 +138,22 @@ def convert(obj, unit, axis):
"""
return obj

@staticmethod
def validate_formatter(formatter):
"""Return whether a given formatter is valid for the converter

Parameters
----------
formatter: `~.Formatter`
The formatter instance to validate.

Returns
-------
bool: if the formatter is valid
"""
# For backwards compatibility, the default is to accept any formatter
return True


class DecimalConverter(ConversionInterface):
"""Converter for decimal.Decimal data to float."""
Expand Down