vContent

Changeset

95:864bd7461d3a
2017-12-03 Paul Boddie raw files shortlog changelog graph Fixed inclusive (UNTIL) selection to avoid non-final selectors causing duplicate selection of values. Removed the 'first' attribute from all but the Pattern selector since it is only relevant to that selector's behaviour.
vRecurrence.py (file)
     1.1 --- a/vRecurrence.py	Sun Dec 03 21:59:54 2017 +0100
     1.2 +++ b/vRecurrence.py	Sun Dec 03 22:45:22 2017 +0100
     1.3 @@ -534,6 +534,10 @@
     1.4  
     1.5              # Add the qualifier to the combined list.
     1.6  
     1.7 +            if isinstance(from_sel, Pattern):
     1.8 +                if not have_sel:
     1.9 +                    have_sel = from_sel.first = True
    1.10 +
    1.11              l.append(from_sel)
    1.12  
    1.13              # Datetime value at same resolution.
    1.14 @@ -565,17 +569,16 @@
    1.15      Take the selector 'from_sel' at the given resolution 'level', using it to
    1.16      create an initial selector, adding it to the combined list 'l' if required.
    1.17  
    1.18 -    Return whether a frequency selector has been introduced or if 'from_sel' is
    1.19 -    such a selector.
    1.20 +    Return whether a frequency selector has been introduced.
    1.21      """
    1.22  
    1.23      if isinstance(from_sel, Enum) and level > 0:
    1.24          parent_level = enum_parent_levels[level]
    1.25 -        repeat = Pattern(parent_level, {"interval" : 1}, freq_levels[parent_level])
    1.26 +        repeat = Pattern(parent_level, {"interval" : 1}, freq_levels[parent_level], first=True)
    1.27          l.append(repeat)
    1.28          return True
    1.29  
    1.30 -    return isinstance(from_sel, Pattern)
    1.31 +    return False
    1.32  
    1.33  def add_datetime_selector(from_dt, l):
    1.34  
    1.35 @@ -871,7 +874,7 @@
    1.36  
    1.37      "A generic selector."
    1.38  
    1.39 -    def __init__(self, level, args, qualifier, selecting=None, first=False):
    1.40 +    def __init__(self, level, args, qualifier, selecting=None):
    1.41  
    1.42          """
    1.43          Initialise at the given 'level' a selector employing the given 'args'
    1.44 @@ -885,12 +888,11 @@
    1.45          self.args = args or {}
    1.46          self.qualifier = qualifier
    1.47          self.selecting = selecting
    1.48 -        self.first = first
    1.49  
    1.50      def __repr__(self):
    1.51 -        return "%s(%s, %r, %r, %r)" % (self.__class__.__name__,
    1.52 -                                       level_labels[self.level],
    1.53 -                                       self.args, self.qualifier, self.first)
    1.54 +        return "%s(%s, %r, %r)" % (self.__class__.__name__,
    1.55 +                                   level_labels[self.level],
    1.56 +                                   self.args, self.qualifier)
    1.57  
    1.58      def select(self, start, end, inclusive=False):
    1.59  
    1.60 @@ -926,8 +928,9 @@
    1.61      "A selector of time periods according to a repeating pattern."
    1.62  
    1.63      def __init__(self, level, args, qualifier, selecting=None, first=False):
    1.64 -        Selector.__init__(self, level, args, qualifier, selecting, first)
    1.65 +        Selector.__init__(self, level, args, qualifier, selecting)
    1.66          multiple = get_multiple(self.qualifier)
    1.67 +        self.first = first
    1.68  
    1.69          # Define the scale of a single period.
    1.70  
    1.71 @@ -977,8 +980,8 @@
    1.72  
    1.73      "A generic value selector."
    1.74  
    1.75 -    def __init__(self, level, args, qualifier, selecting=None, first=False):
    1.76 -        Selector.__init__(self, level, args, qualifier, selecting, first)
    1.77 +    def __init__(self, level, args, qualifier, selecting=None):
    1.78 +        Selector.__init__(self, level, args, qualifier, selecting)
    1.79          self.step = scale(1, level)
    1.80  
    1.81      def materialise_items(self, context, start, end, inclusive=False):
    1.82 @@ -992,8 +995,8 @@
    1.83  
    1.84      "A selector of instances specified in terms of day numbers."
    1.85  
    1.86 -    def __init__(self, level, args, qualifier, selecting=None, first=False):
    1.87 -        Selector.__init__(self, level, args, qualifier, selecting, first)
    1.88 +    def __init__(self, level, args, qualifier, selecting=None):
    1.89 +        Selector.__init__(self, level, args, qualifier, selecting)
    1.90          self.step = scale(1, WEEKS)
    1.91  
    1.92      def materialise_items(self, context, start, end, inclusive=False):
    1.93 @@ -1076,8 +1079,8 @@
    1.94  
    1.95      "A result set position selector."
    1.96  
    1.97 -    def __init__(self, level, args, qualifier, selecting=None, first=False):
    1.98 -        Selector.__init__(self, level, args, qualifier, selecting, first)
    1.99 +    def __init__(self, level, args, qualifier, selecting=None):
   1.100 +        Selector.__init__(self, level, args, qualifier, selecting)
   1.101          if level is not None:
   1.102              self.set_level(level)
   1.103          else:
   1.104 @@ -1181,9 +1184,13 @@
   1.105  
   1.106      def at_limit(self):
   1.107  
   1.108 -        "Obtain periods before the end (and also at the end if inclusive)."
   1.109 +        """
   1.110 +        Obtain periods before the end. If inclusive and selecting in the final
   1.111 +        selector, obtain the period at the end.
   1.112 +        """
   1.113  
   1.114 -        return not self.inclusive and self.current == self.end or \
   1.115 +        inclusive = self.inclusive and not self.selector.selecting
   1.116 +        return not inclusive and self.current == self.end or \
   1.117                 self.current > self.end
   1.118  
   1.119  class PatternIterator(SelectorIterator):
   1.120 @@ -1462,40 +1469,36 @@
   1.121  
   1.122          "Return the next value, initially the start period."
   1.123  
   1.124 -        while not self.at_limit():
   1.125 -
   1.126 -            # Obtain the next item.
   1.127 -
   1.128 -            try:
   1.129 -                result = self.next_item(self.start, self.end)
   1.130 -            except StopIteration:
   1.131 +        # Obtain the next item.
   1.132  
   1.133 -                # With no more values, flush any waiting value.
   1.134 +        try:
   1.135 +            result = self.next_item(self.start, self.end)
   1.136 +        except StopIteration:
   1.137  
   1.138 -                if self.waiting is not None:
   1.139 -                    result = self.waiting
   1.140 -                    self.waiting = None
   1.141 -                    return result
   1.142 -                else:
   1.143 -                    raise
   1.144 -
   1.145 -            # Compare with any waiting value.
   1.146 +            # With no more values, flush any waiting value.
   1.147  
   1.148              if self.waiting is not None:
   1.149 -
   1.150 -                # Produce the waiting value, queue the latest result.
   1.151 +                result = self.waiting
   1.152 +                self.waiting = None
   1.153 +                return result
   1.154 +            else:
   1.155 +                raise
   1.156  
   1.157 -                if result != self.waiting:
   1.158 -                    result, self.waiting = self.waiting, result
   1.159 +        # Compare with any waiting value.
   1.160 +
   1.161 +        if self.waiting is not None:
   1.162  
   1.163 -                # Remove the waiting value if identical to the latest result.
   1.164 +            # Produce the waiting value, queue the latest result.
   1.165 +
   1.166 +            if result != self.waiting:
   1.167 +                result, self.waiting = self.waiting, result
   1.168  
   1.169 -                else:
   1.170 -                    self.waiting = None
   1.171 +            # Remove the waiting value if identical to the latest result.
   1.172  
   1.173 -            return result
   1.174 +            else:
   1.175 +                self.waiting = None
   1.176  
   1.177 -        raise StopIteration
   1.178 +        return result
   1.179  
   1.180  def connect_selectors(selectors):
   1.181  
   1.182 @@ -1506,18 +1509,10 @@
   1.183      """
   1.184  
   1.185      current = selectors[0]
   1.186 -    current.first = first = True
   1.187  
   1.188      for selector in selectors[1:]:
   1.189          current.selecting = selector
   1.190 -
   1.191 -        # Allow selectors within the limit selector to act as if they are first
   1.192 -        # in the chain and will operate using the supplied datetime context.
   1.193 -
   1.194 -        first = isinstance(current, (LimitSelector, StartSelector))
   1.195 -
   1.196          current = selector
   1.197 -        current.first = first
   1.198  
   1.199      return selectors[0]
   1.200