Skip to content

Commit 44dc816

Browse files
committed
[plotting] Repair png rendering with matplotlib >= 3.8.0
1 parent 8c2e3b4 commit 44dc816

3 files changed

Lines changed: 62 additions & 1 deletion

File tree

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ in progress
77
===========
88
- Export: Repaired ``ggplot`` graph rendering by switching to ``plotnine``
99
- Export: Repaired ``png`` rendering with modern ``pandas``
10+
- Export: Repaired ``png`` rendering with ``matplotlib >= 3.8.0``
1011
- General: Dropped support for Python 3.7
1112

1213
.. _kotori-0.28.1:

kotori/io/export/util.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def matplotlib_locator_formatter(timedelta, span=1):
5858
Compute appropriate locator and formatter for renderers
5959
based on matplotlib, depending on designated time span.
6060
"""
61-
from matplotlib.dates import date_ticker_factory, DateFormatter
61+
from matplotlib.dates import DateFormatter
6262
locator, formatter = date_ticker_factory(span)
6363

6464
# http://pandas.pydata.org/pandas-docs/stable/timedeltas.html
@@ -93,6 +93,59 @@ def matplotlib_locator_formatter(timedelta, span=1):
9393
return locator, formatter
9494

9595

96+
def date_ticker_factory(span, tz=None, numticks=5):
97+
"""
98+
Create a date locator with *numticks* (approx) and a date formatter
99+
for *span* in days. Return value is (locator, formatter).
100+
101+
`date_ticker_factory` was removed from matplotlib in version 3.8.0.
102+
The recommendation is to use AutoDateLocator and AutoDateFormatter,
103+
or to vendorize the code.
104+
105+
https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.8.0.html#date-ticker-factory-removed
106+
https://github.com/matplotlib/matplotlib/blob/v3.7.5/lib/matplotlib/dates.py#L1784C1-L1823C30
107+
"""
108+
import math
109+
from matplotlib.dates import (
110+
DAYS_PER_MONTH, DAYS_PER_WEEK, DAYS_PER_YEAR, HOURS_PER_DAY, MINUTES_PER_DAY,
111+
DateFormatter, DayLocator, HourLocator, MinuteLocator, MonthLocator, WeekdayLocator, YearLocator)
112+
113+
if span == 0:
114+
span = 1 / HOURS_PER_DAY
115+
116+
mins = span * MINUTES_PER_DAY
117+
hrs = span * HOURS_PER_DAY
118+
days = span
119+
wks = span / DAYS_PER_WEEK
120+
months = span / DAYS_PER_MONTH # Approx
121+
years = span / DAYS_PER_YEAR # Approx
122+
123+
if years > numticks:
124+
locator = YearLocator(int(years / numticks), tz=tz) # define
125+
fmt = '%Y'
126+
elif months > numticks:
127+
locator = MonthLocator(tz=tz)
128+
fmt = '%b %Y'
129+
elif wks > numticks:
130+
locator = WeekdayLocator(tz=tz)
131+
fmt = '%a, %b %d'
132+
elif days > numticks:
133+
locator = DayLocator(interval=math.ceil(days / numticks), tz=tz)
134+
fmt = '%b %d'
135+
elif hrs > numticks:
136+
locator = HourLocator(interval=math.ceil(hrs / numticks), tz=tz)
137+
fmt = '%H:%M\n%b %d'
138+
elif mins > numticks:
139+
locator = MinuteLocator(interval=math.ceil(mins / numticks), tz=tz)
140+
fmt = '%H:%M:%S'
141+
else:
142+
locator = MinuteLocator(tz=tz)
143+
fmt = '%H:%M:%S'
144+
145+
formatter = DateFormatter(fmt, tz=tz)
146+
return locator, formatter
147+
148+
96149
def make_timezone_unaware(df):
97150
# Please ensure that datetimes are timezone unaware before writing to Excel.
98151
# https://github.com/pandas-dev/pandas/pull/27129

test/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
#truncate.DEFAULT_MAX_CHARS = 9999
2525

2626

27+
@pytest.fixture(scope="session", autouse=True)
28+
def mask_logs():
29+
30+
# Tame logging of matplotlib, otherwise it would emit way too many log messages.
31+
logging.getLogger("matplotlib.font_manager").setLevel(logging.INFO)
32+
33+
2734
def create_machinery(config, scope="package"):
2835

2936
@pytest_twisted.inlineCallbacks

0 commit comments

Comments
 (0)