Skip to content
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
17 changes: 17 additions & 0 deletions doc/api/api_changes/2015-04-12_microsecondlocator.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Removed `args` and `kwargs` from `MicrosecondLocator.__call__`
``````````````````````````````````````````````````````````````

The call signature of :meth:`~matplotlib.dates.MicrosecondLocator.__call__`
has changed from `__call__(self, *args, **kwargs)` to `__call__(self)`.
This is consistent with the super class :class:`~matplotlib.ticker.Locator`
and also all the other Locators derived from this super class.


No `ValueError` for the MicrosecondLocator and YearLocator
``````````````````````````````````````````````````````````

The :class:`~matplotlib.dates.MicrosecondLocator` and
:class:`~matplotlib.dates.YearLocator` objects when called will return
an empty list if the axes have no data or the view has no interval.
Previously, they raised a `ValueError`. This is consistent with all
the Date Locators.
15 changes: 15 additions & 0 deletions doc/users/whats_new/datelocators.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Date Locators
-------------

Date Locators (derived from :class:`~matplotlib.dates.DateLocator`) now
implement the :meth:`~matplotlib.tickers.Locator.tick_values` method.
This is expected of all Locators derived from :class:`~matplotlib.tickers.Locator`.

The Date Locators can now be used easily without creating axes

from datetime import datetime
from matplotlib.dates import YearLocator
t0 = datetime(2002, 10, 9, 12, 10)
tf = datetime(2005, 10, 9, 12, 15)
loc = YearLocator()
values = loc.tick_values(t0, tf)
59 changes: 41 additions & 18 deletions lib/matplotlib/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,18 +667,21 @@ def __call__(self):
except ValueError:
return []

if dmin > dmax:
dmax, dmin = dmin, dmax
delta = relativedelta(dmax, dmin)
return self.tick_values(dmin, dmax)

def tick_values(self, vmin, vmax):
if vmin > vmax:
vmax, vmin = vmin, vmax
delta = relativedelta(vmax, vmin)

# We need to cap at the endpoints of valid datetime
try:
start = dmin - delta
start = vmin - delta
except ValueError:
start = _from_ordinalf(1.0)

try:
stop = dmax + delta
stop = vmax + delta
except ValueError:
# The magic number!
stop = _from_ordinalf(3652059.9999999)
Expand All @@ -688,19 +691,19 @@ def __call__(self):
# estimate the number of ticks very approximately so we don't
# have to do a very expensive (and potentially near infinite)
# 'between' calculation, only to find out it will fail.
nmax, nmin = date2num((dmax, dmin))
nmax, nmin = date2num((vmax, vmin))
estimate = (nmax - nmin) / (self._get_unit() * self._get_interval())
# This estimate is only an estimate, so be really conservative
# about bailing...
if estimate > self.MAXTICKS * 2:
raise RuntimeError(
'RRuleLocator estimated to generate %d ticks from %s to %s: '
'exceeds Locator.MAXTICKS * 2 (%d) ' % (estimate, dmin, dmax,
'exceeds Locator.MAXTICKS * 2 (%d) ' % (estimate, vmin, vmax,
self.MAXTICKS * 2))

dates = self.rule.between(dmin, dmax, True)
dates = self.rule.between(vmin, vmax, True)
if len(dates) == 0:
return date2num([dmin, dmax])
return date2num([vmin, vmax])
return self.raise_if_exceeds(date2num(dates))

def _get_unit(self):
Expand Down Expand Up @@ -866,6 +869,9 @@ def __call__(self):
self.refresh()
return self._locator()

def tick_values(self, vmin, vmax):
return self.get_locator(vmin, vmax).tick_values(vmin, vmax)

def nonsingular(self, vmin, vmax):
# whatever is thrown at us, we can scale the unit.
# But default nonsingular date plots at an ~4 year period.
Expand Down Expand Up @@ -1012,11 +1018,19 @@ def __init__(self, base=1, month=1, day=1, tz=None):
}

def __call__(self):
dmin, dmax = self.viewlim_to_dt()
ymin = self.base.le(dmin.year)
ymax = self.base.ge(dmax.year)
# if no data have been set, this will tank with a ValueError
try:
dmin, dmax = self.viewlim_to_dt()
except ValueError:
return []

return self.tick_values(dmin, dmax)

def tick_values(self, vmin, vmax):
ymin = self.base.le(vmin.year)
ymax = self.base.ge(vmax.year)

ticks = [dmin.replace(year=ymin, **self.replaced)]
ticks = [vmin.replace(year=ymin, **self.replaced)]
while 1:
dt = ticks[-1]
if dt.year >= ymax:
Expand Down Expand Up @@ -1184,11 +1198,20 @@ def set_data_interval(self, vmin, vmax):
self._wrapped_locator.set_data_interval(vmin, vmax)
return DateLocator.set_data_interval(self, vmin, vmax)

def __call__(self, *args, **kwargs):
vmin, vmax = self.axis.get_view_interval()
vmin *= MUSECONDS_PER_DAY
vmax *= MUSECONDS_PER_DAY
ticks = self._wrapped_locator.tick_values(vmin, vmax)
def __call__(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a note to https://github.com/matplotlib/matplotlib/tree/master/doc/api/api_changes about this? It is a harmless change (as the inputs were ignored), but it should still be documented.

# if no data have been set, this will tank with a ValueError
try:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also document this API change? It looks like it is making it consistent with the base class so I think that this is OK.

dmin, dmax = self.viewlim_to_dt()
except ValueError:
return []

return self.tick_values(dmin, dmax)

def tick_values(self, vmin, vmax):
nmin, nmax = date2num((vmin, vmax))
nmin *= MUSECONDS_PER_DAY
nmax *= MUSECONDS_PER_DAY
ticks = self._wrapped_locator.tick_values(nmin, nmax)
ticks = [tick / MUSECONDS_PER_DAY for tick in ticks]
return ticks

Expand Down