From 7ae286cfdc031bf316649b767035482919a702d2 Mon Sep 17 00:00:00 2001 From: "clarence.c.xu" <20210430@htdk> Date: Wed, 8 Jun 2022 07:20:05 +0800 Subject: [PATCH] and function run between time start and end --- schedule/__init__.py | 97 +++++++++++++++++++++++++++++++++-------- test_schedule.py | 101 ++++++++++++++++++++++++------------------- 2 files changed, 137 insertions(+), 61 deletions(-) diff --git a/schedule/__init__.py b/schedule/__init__.py index 8d02837d..3fa38501 100644 --- a/schedule/__init__.py +++ b/schedule/__init__.py @@ -75,6 +75,17 @@ class CancelJob(object): pass +class DuringTime: + + def __init__(self, + run_start: Optional[datetime.datetime] = None, + run_end: Optional[datetime.datetime] = None, + format: Optional[str] = None): + self.run_start = run_start + self.run_end = run_end + self.format = format + + class Scheduler(object): """ Objects instantiated by the :class:`Scheduler ` are @@ -174,7 +185,7 @@ def _run_job(self, job: "Job") -> None: self.cancel_job(job) def get_next_run( - self, tag: Optional[Hashable] = None + self, tag: Optional[Hashable] = None ) -> Optional[datetime.datetime]: """ Datetime when the next job should run. @@ -252,6 +263,9 @@ def __init__(self, interval: int, scheduler: Scheduler = None): # optional time of final run self.cancel_after: Optional[datetime.datetime] = None + # optional time of start run and end run + self.run_during: Optional[DuringTime] = DuringTime + self.tags: Set[Hashable] = set() # unique set of tags for the job self.scheduler: Optional[Scheduler] = scheduler # scheduler to register with @@ -306,9 +320,9 @@ def is_repr(j): ) else: fmt = ( - "Every %(interval)s " - + ("to %(latest)s " if self.latest is not None else "") - + "%(unit)s do %(call_repr)s %(timestats)s" + "Every %(interval)s " + + ("to %(latest)s " if self.latest is not None else "") + + "%(unit)s do %(call_repr)s %(timestats)s" ) return fmt % dict( @@ -572,8 +586,8 @@ def to(self, latest: int): return self def until( - self, - until_time: Union[datetime.datetime, datetime.timedelta, datetime.time, str], + self, + until_time: Union[datetime.datetime, datetime.timedelta, datetime.time, str], ): """ Schedule job to run until the specified moment. @@ -609,7 +623,7 @@ def until( datetime.datetime.now(), until_time ) elif isinstance(until_time, str): - cancel_after = self._decode_datetimestr( + cancel_after, f = self._decode_datetimestr( until_time, [ "%Y-%m-%d %H:%M:%S", @@ -639,6 +653,34 @@ def until( ) return self + def time_between(self, start: str, end: str): + start_time, f1 = self._decode_datetimestr( + start, + [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + "%H:%M:%S", + "%H:%M", + ], + ) + end_time, f2 = self._decode_datetimestr( + end, + [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + "%H:%M:%S", + "%H:%M", + ], + ) + + if f1 != f2: + raise RuntimeError('The format of the start time and end time must be same!') + + self.run_during = DuringTime(start_time, end_time, f1) + return self + def do(self, job_func: Callable, *args, **kwargs): """ Specifies the job_func that should be called every time the @@ -681,6 +723,10 @@ def run(self): deadline is reached. """ + if not self._is_during_time(datetime.datetime.now()): + logger.debug(f"Cancelling job {self} because of out-of the during time") + return CancelJob + if self._is_overdue(datetime.datetime.now()): logger.debug("Cancelling job %s", self) return CancelJob @@ -760,17 +806,17 @@ def _schedule_next_run(self) -> None: if not self.last_run or (self.next_run - self.last_run) > self.period: now = datetime.datetime.now() if ( - self.unit == "days" - and self.at_time > now.time() - and self.interval == 1 + self.unit == "days" + and self.at_time > now.time() + and self.interval == 1 ): self.next_run = self.next_run - datetime.timedelta(days=1) elif self.unit == "hours" and ( - self.at_time.minute > now.minute - or ( - self.at_time.minute == now.minute - and self.at_time.second > now.second - ) + self.at_time.minute > now.minute + or ( + self.at_time.minute == now.minute + and self.at_time.second > now.second + ) ): self.next_run = self.next_run - datetime.timedelta(hours=1) elif self.unit == "minutes" and self.at_time.second > now.second: @@ -783,12 +829,29 @@ def _schedule_next_run(self) -> None: def _is_overdue(self, when: datetime.datetime): return self.cancel_after is not None and when > self.cancel_after + def _is_during_time(self, when: datetime.datetime): + during_bool = False + f = self.run_during.format + if f is not None: + now_time, f3 = self._decode_datetimestr( + when.strftime(f), + [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + "%H:%M:%S", + "%H:%M", + ], + ) + during_bool = self.run_during.run_start < now_time < self.run_during.run_end + return during_bool + def _decode_datetimestr( - self, datetime_str: str, formats: List[str] + self, datetime_str: str, formats: List[str] ) -> Optional[datetime.datetime]: for f in formats: try: - return datetime.datetime.strptime(datetime_str, f) + return datetime.datetime.strptime(datetime_str, f), f except ValueError: pass return None diff --git a/test_schedule.py b/test_schedule.py index 7b4b83da..100fca36 100644 --- a/test_schedule.py +++ b/test_schedule.py @@ -95,66 +95,66 @@ def test_time_units(self): with self.assertRaises(IntervalError): job_instance.week with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.monday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.monday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.monday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.monday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.monday with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.tuesday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.tuesday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.tuesday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.tuesday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.tuesday with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.wednesday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.wednesday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.wednesday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.wednesday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.wednesday with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.thursday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.thursday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.thursday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.thursday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.thursday with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.friday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.friday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.friday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.friday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.friday with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.saturday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.saturday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.saturday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.saturday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.saturday with self.assertRaisesRegex( - IntervalError, - ( - r"Scheduling \.sunday\(\) jobs is only allowed for weekly jobs\. " - r"Using \.sunday\(\) on a job scheduled to run every 2 or more " - r"weeks is not supported\." - ), + IntervalError, + ( + r"Scheduling \.sunday\(\) jobs is only allowed for weekly jobs\. " + r"Using \.sunday\(\) on a job scheduled to run every 2 or more " + r"weeks is not supported\." + ), ): job_instance.sunday @@ -363,6 +363,19 @@ def test_until_time(self): assert mock_job.call_count == 0 assert len(schedule.jobs) == 0 + def test_between_time(self): + mock_job = make_mock_job() + with mock_datetime(2020, 1, 1, 11, 35, 10): + every(1).seconds.time_between("11:35:00", "11:36:00").do(mock_job) + with mock_datetime(2020, 1, 1, 11, 35, 15): + schedule.run_pending() + assert mock_job.call_count == 1 + assert len(schedule.jobs) == 1 + with mock_datetime(2020, 1, 1, 11, 36, 10): + schedule.run_all() + assert mock_job.call_count == 1 + assert len(schedule.jobs) == 0 + def test_weekday_at_todady(self): mock_job = make_mock_job() @@ -455,8 +468,8 @@ def test_next_run_time(self): assert every().saturday.do(mock_job).next_run.day == 9 assert every().sunday.do(mock_job).next_run.day == 10 assert ( - every().minute.until(datetime.time(12, 17)).do(mock_job).next_run.minute - == 16 + every().minute.until(datetime.time(12, 17)).do(mock_job).next_run.minute + == 16 ) def test_next_run_time_day_end(self):