Changeset 40
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
recur.py
r24 r40 342 342 startDate = sane_date(startDate.year, startDate.month, 343 343 startDate.day + 1) 344 startDate = datetime.datetime.combine(startDate, timeofday)345 344 except AttributeError: 346 345 # datetime.date has no time() attribute 347 346 pass 347 startDate = datetime.datetime.combine(startDate, timeofday) 348 348 349 349 while (endDate is None) or (startDate <= endDate): … … 617 617 else: 618 618 self.regexes[key] = re.compile(regSet, re.IGNORECASE) 619 620 def __call__(self, description): 621 for rule, regSet in self.regexes.items(): 622 if isinstance(regSet, list): 623 for index, regex in enumerate(regSet): 624 matches = regex.match(description) 625 if matches: 626 return rule, (index,) + matches.groups() 627 else: 628 matches = regSet.match(description) 629 if matches: 630 return rule, matches.groups() 631 632 raise ValueError(u"The supplied description ('%s') " 633 u"could not be parsed." % description) 634 619 635 localeEnglish = Locale() 620 636 … … 660 676 self.endDate = endDate 661 677 self.locale = locale 662 663 def setargs(matches, index=None): 664 args = [startDate] 665 if index is not None: 666 args.append(index) 667 for eachArg in matches.groups(): 668 args.append(eachArg) 669 args.append(endDate) 670 self.function = rule 671 a = self.args = tuple(args) 672 try: 673 self.generator = rule(*a) 674 except TypeError: 675 raise StandardError(a) 676 677 for rule, regSet in locale.regexes.items(): 678 if isinstance(regSet, list): 679 for index, regex in enumerate(regSet): 680 matches = regex.match(description) 681 if matches: 682 setargs(matches, index) 683 return 684 else: 685 matches = regSet.match(description) 686 if matches: 687 setargs(matches) 688 return 689 690 raise ValueError(u"The supplied description ('%s') " 691 u"could not be parsed." % description) 678 self.function, args = locale(description) 679 self.args = (startDate,) + args + (endDate,) 680 # Form an initial generator, if for no other reason than to test args early. 681 self.reset() 682 683 def reset(self): 684 try: 685 self.generator = self.function(*self.args) 686 except TypeError, x: 687 x.args += self.args 688 raise 692 689 693 690 def __iter__(self): 694 self. generator = self.function(*self.args)691 self.reset() 695 692 return self 696 693 697 694 def next(self): 698 695 return self.generator.next() 699 700 def interval(self):701 return interval(self)702 703 704 def interval(recurrence):705 """Seconds between the current datetime and the next recurrence.706 707 This is particularly handy for creating threading.Timer objects708 on a recurring schedule, in the form:709 threading.Timer(recurrence.interval(), function).start()710 See the Worker class (below) for an example of this.711 712 Notice that this function advances the generator each time it713 is called. If the generator completes, the StopIteration exception714 is not trapped here; it is passed up to the caller to handle.715 716 On a rare occasion, the return value will be off by one second;717 this is probably due to the seconds "rolling over" while we are718 inside this function. If you feel that this is a drastic oversight,719 feel free to submit a patch.720 """721 diff = recurrence.next() - datetime.datetime.now()722 return (diff.days * 86400) + diff.seconds723 696 724 697 … … 728 701 You must override work(), which is called at each interval. 729 702 730 active: a boolean flag indicating whether or not the Worker's run()703 paused: a boolean flag indicating whether or not the Worker's run() 731 704 method should be executed at each interval. Notice that, even if 732 active is False, recurring Workers will continue to schedule new705 paused is True, recurring Workers will continue to schedule new 733 706 threads--they simply won't do anything at run() time. 734 707 … … 745 718 recurrence = None 746 719 self.recurrence = recurrence 747 if self.recurrence:748 # Throw away the first occurrence value,749 # which is almost always .now()750 self.recurrence.next()751 720 752 721 self.createdate = datetime.datetime.now() 753 722 self.lastrun = None 754 self.active = True 723 self.nextrun = None 724 self.paused = False 755 725 self.terminated = False 756 726 757 727 def motivate(self): 758 728 """Start a new immediate or recurring thread for work.""" 729 self.nextrun = None 759 730 if not self.terminated: 760 731 if self.recurrence: 761 # Start a recurring, timed Thread. 762 self.curthread = threading.Timer(self.recurrence.interval(), 763 self._cycle) 732 # Start a recurring, timed thread. 733 now = datetime.datetime.now() 734 while True: 735 try: 736 next = self.recurrence.next() 737 except StopIteration: 738 # The recurrence series was exhausted. 739 return 740 diff = next - now 741 diff = (diff.days * 86400) + diff.seconds 742 if diff >= 0: 743 self.nextrun = next 744 break 745 iv = diff 746 func = self._cycle 764 747 else: 765 748 # Start a single, non-recurring thread. 766 self.curthread = threading.Thread(None, self.run) 749 iv = 0 750 func = self.run 751 self.curthread = threading.Timer(iv, func) 767 752 self.curthread.start() 768 753 … … 774 759 def run(self): 775 760 """Prepare for work.""" 776 if self.activeand not self.terminated:761 if not self.paused and not self.terminated: 777 762 self.work() 778 763 self.lastrun = datetime.datetime.now() … … 781 766 """Stop work.""" 782 767 self.terminated = True 783 try: 784 self.curthread.cancel() 785 except AttributeError: 786 pass 768 self.curthread.cancel() 787 769 788 770 def work(self):
