Skip to content

Change Locator MAXTICKS checking to emitting a log at WARNING level. #13510

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

Merged
merged 1 commit into from
Sep 5, 2019
Merged
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
6 changes: 6 additions & 0 deletions doc/api/next_api_changes/2019-02-25-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
API changes
```````````

When more than `.Locator.MAXTICKS` ticks are generated, the behavior of
`.Locator.raise_if_exceeds` changed from raising a RuntimeError to emitting a
log at WARNING level.
14 changes: 10 additions & 4 deletions lib/matplotlib/tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,13 @@ def test_date_axvline():
fig.autofmt_xdate()


def test_too_many_date_ticks():
def test_too_many_date_ticks(caplog):
# Attempt to test SF 2715172, see
# https://sourceforge.net/tracker/?func=detail&aid=2715172&group_id=80706&atid=560720
# setting equal datetimes triggers and expander call in
# transforms.nonsingular which results in too many ticks in the
# DayLocator. This should trigger a Locator.MAXTICKS RuntimeError
# DayLocator. This should emit a log at WARNING level.
caplog.set_level("WARNING")
t0 = datetime.datetime(2000, 1, 20)
tf = datetime.datetime(2000, 1, 20)
fig = plt.figure()
Expand All @@ -152,8 +153,13 @@ def test_too_many_date_ticks():
'Attempting to set identical left == right' in str(rec[0].message)
ax.plot([], [])
ax.xaxis.set_major_locator(mdates.DayLocator())
with pytest.raises(RuntimeError):
fig.savefig('junk.png')
fig.canvas.draw()
# The warning is emitted multiple times because the major locator is also
# called both when placing the minor ticks (for overstriking detection) and
# during tick label positioning.
assert caplog.records and all(
record.name == "matplotlib.ticker" and record.levelname == "WARNING"
for record in caplog.records)


@image_comparison(['RRuleLocator_bounds.png'])
Expand Down
18 changes: 14 additions & 4 deletions lib/matplotlib/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1690,11 +1690,21 @@ def __call__(self):
raise NotImplementedError('Derived must override')

def raise_if_exceeds(self, locs):
"""Raise a RuntimeError if ``len(locs) > self.MAXTICKS``."""
"""
Log at WARNING level if *locs* is longer than `Locator.MAXTICKS`.

This is intended to be called immediately before returning *locs* from
``__call__`` to inform users in case their Locator returns a huge
number of ticks, causing Matplotlib to run out of memory.

The "strange" name of this method dates back to when it would raise an
exception instead of emitting a log.
"""
if len(locs) >= self.MAXTICKS:
raise RuntimeError("Locator attempting to generate {} ticks from "
"{} to {}: exceeds Locator.MAXTICKS".format(
len(locs), locs[0], locs[-1]))
_log.warning(
"Locator attempting to generate %s ticks ([%s, ..., %s]), "
"which exceeds Locator.MAXTICKS (%s).",
len(locs), locs[0], locs[-1], self.MAXTICKS)
return locs

def nonsingular(self, v0, v1):
Expand Down