Revision: 8296
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8296&view=rev
Author:   mdboom
Date:     2010-05-05 17:57:19 +0000 (Wed, 05 May 2010)

Log Message:
-----------
Improvements to DateLocators to partially address #2976576:
  - Take into account "interval" when determining autorange (particularly 
important for expanding singular ranges)
  - prevent a near-infinite loop in the "dateutil.rrule.between" method 
(calculating a repeating event between two dates) by estimating with division 
the approximate number, and if this number is way more than reasonable, raise 
an Exception.

Modified Paths:
--------------
    trunk/matplotlib/lib/matplotlib/dates.py

Modified: trunk/matplotlib/lib/matplotlib/dates.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/dates.py    2010-05-04 19:46:28 UTC (rev 
8295)
+++ trunk/matplotlib/lib/matplotlib/dates.py    2010-05-05 17:57:19 UTC (rev 
8296)
@@ -507,11 +507,18 @@
         """
         return 1
 
+    def _get_interval(self):
+        """
+        Return the number of units for each tick.
+        """
+        return 1
+    
     def nonsingular(self, vmin, vmax):
         unit = self._get_unit()
+        interval = self._get_interval()
         if abs(vmax - vmin) < 1e-6:
-            vmin -= 2*unit
-            vmax += 2*unit
+            vmin -= 2*unit*interval
+            vmax += 2*unit*interval
         return vmin, vmax
 
 class RRuleLocator(DateLocator):
@@ -542,8 +549,22 @@
             # The magic number!
             stop = _from_ordinalf( 3652059.9999999 )
 
-        self.rule.set(dtstart=start, until=stop)
+        self.rule.set(dtstart=start, until=stop, count=self.MAXTICKS + 1)
+
+        # 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))
+        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, self.MAXTICKS * 2))
+            
         dates = self.rule.between(dmin, dmax, True)
+        if len(dates) == 0:
+            return date2num([dmin, dmax])
         return self.raise_if_exceeds(date2num(dates))
 
     def _get_unit(self):
@@ -552,14 +573,17 @@
         intelligent autoscaling.
         """
         freq = self.rule._rrule._freq
+        return self.get_unit_generic(freq)
+
+    def get_unit_generic(freq):
         if ( freq == YEARLY ):
-            return 365
+            return 365.0
         elif ( freq == MONTHLY ):
-            return 30
+            return 30.0
         elif ( freq == WEEKLY ):
-            return 7
+            return 7.0
         elif ( freq == DAILY ):
-            return 1
+            return 1.0
         elif ( freq == HOURLY ):
             return (1.0/24.0)
         elif ( freq == MINUTELY ):
@@ -569,7 +593,11 @@
         else:
             # error
             return -1   #or should this just return '1'?
+    get_unit_generic = staticmethod(get_unit_generic)
 
+    def _get_interval(self):
+        return self.rule._rrule._interval
+    
     def autoscale(self):
         """
         Set the view limits to include the data range.
@@ -702,23 +730,7 @@
         self._locator = self.get_locator(dmin, dmax)
 
     def _get_unit(self):
-        if ( self._freq == YEARLY ):
-            return 365.0
-        elif ( self._freq == MONTHLY ):
-            return 30.0
-        elif ( self._freq == WEEKLY ):
-            return 7.0
-        elif ( self._freq == DAILY ):
-            return 1.0
-        elif ( self._freq == HOURLY ):
-            return 1.0/24
-        elif ( self._freq == MINUTELY ):
-            return 1.0/(24*60)
-        elif ( self._freq == SECONDLY ):
-            return 1.0/(24*3600)
-        else:
-            # error
-            return -1
+        return RRuleLocator.get_unit_generic(self._freq)
 
     def autoscale(self):
         'Try to choose the view limits intelligently.'
@@ -828,14 +840,6 @@
                           'tzinfo' : tz
                           }
 
-
-    def _get_unit(self):
-        """
-        Return how many days a unit of the locator is; used for
-        intelligent autoscaling.
-        """
-        return 365
-
     def __call__(self):
         dmin, dmax = self.viewlim_to_dt()
         ymin = self.base.le(dmin.year)
@@ -864,6 +868,7 @@
         vmax = date2num(vmax)
         return self.nonsingular(vmin, vmax)
 
+    
 class MonthLocator(RRuleLocator):
     """
     Make ticks on occurances of each month month, eg 1, 3, 12.
@@ -881,14 +886,7 @@
                          interval=interval, **self.hms0d)
         RRuleLocator.__init__(self, o, tz)
 
-    def _get_unit(self):
-        """
-        Return how many days a unit of the locator is; used for
-        intelligent autoscaling.
-        """
-        return 30
 
-
 class WeekdayLocator(RRuleLocator):
     """
     Make ticks on occurances of each weekday.
@@ -909,14 +907,7 @@
                          interval=interval, **self.hms0d)
         RRuleLocator.__init__(self, o, tz)
 
-    def _get_unit(self):
-        """
-        return how many days a unit of the locator is; used for
-        intelligent autoscaling.
-        """
-        return 7
 
-
 class DayLocator(RRuleLocator):
     """
     Make ticks on occurances of each day of the month.  For example,
@@ -934,13 +925,7 @@
                          interval=interval, **self.hms0d)
         RRuleLocator.__init__(self, o, tz)
 
-    def _get_unit(self):
-        """
-        Return how many days a unit of the locator is; used for
-        intelligent autoscaling.
-        """
-        return 1
-
+        
 class HourLocator(RRuleLocator):
     """
     Make ticks on occurances of each hour.
@@ -958,13 +943,7 @@
                             byminute=0, bysecond=0)
         RRuleLocator.__init__(self, rule, tz)
 
-    def _get_unit(self):
-        """
-        return how many days a unit of the locator is; use for
-        intelligent autoscaling
-        """
-        return 1/24.
-
+    
 class MinuteLocator(RRuleLocator):
     """
     Make ticks on occurances of each minute.
@@ -982,13 +961,7 @@
                             bysecond=0)
         RRuleLocator.__init__(self, rule, tz)
 
-    def _get_unit(self):
-        """
-        Return how many days a unit of the locator is; used for
-        intelligent autoscaling.
-        """
-        return 1./(24*60)
-
+        
 class SecondLocator(RRuleLocator):
     """
     Make ticks on occurances of each second.
@@ -1006,15 +979,7 @@
         rule = rrulewrapper(SECONDLY, bysecond=bysecond, interval=interval)
         RRuleLocator.__init__(self, rule, tz)
 
-    def _get_unit(self):
-        """
-        Return how many days a unit of the locator is; used for
-        intelligent autoscaling.
-        """
-        return 1./(24*60*60)
 
-
-
 def _close_to_dt(d1, d2, epsilon=5):
     'Assert that datetimes *d1* and *d2* are within *epsilon* microseconds.'
     delta = d2-d1


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
_______________________________________________
Matplotlib-checkins mailing list
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to