diff --git a/docs/code/conds/api/periodical_restricted.py b/docs/code/conds/api/periodical_restricted.py index 3cae9712..f5882f01 100644 --- a/docs/code/conds/api/periodical_restricted.py +++ b/docs/code/conds/api/periodical_restricted.py @@ -16,10 +16,18 @@ def do_between(): def do_at(): ... +@app.task(daily.at("08:00", "12:00", "18:00")) +def do_at_multiple(): + ... + @app.task(weekly.on("Monday")) def do_on(): ... +@app.task(weekly.on("Monday", "Friday", "Sunday")) +def do_on_multiple(): + ... + @app.task(monthly.starting("3rd")) def do_starting(): ... \ No newline at end of file diff --git a/docs/handbooks/conditions/api/periodical.rst b/docs/handbooks/conditions/api/periodical.rst index 71b02fd7..c565de75 100644 --- a/docs/handbooks/conditions/api/periodical.rst +++ b/docs/handbooks/conditions/api/periodical.rst @@ -88,6 +88,13 @@ and *starting Friday* means the week is set to start on Friday. ``between Monday and Friday`` means Monday at 00:00 (0 am) to Friday 24:00 (12 pm). +.. note:: + + Passing multiple values to *at/on* is a convenient way to schedule + a task to run at multiple times. For example, ``weekly.on("Mon", "Fri")`` + is the same as ``weekly.on("Mon") | weekly.on("Fri")`` and it can be used + to run a task on Mondays and Fridays. + There are also **time of ...** conditions check if the current time is within the given period. diff --git a/rocketry/conditions/api.py b/rocketry/conditions/api.py index 20b77cea..f59a7e11 100644 --- a/rocketry/conditions/api.py +++ b/rocketry/conditions/api.py @@ -1,3 +1,4 @@ +from functools import reduce from typing import Callable, Union from rocketry.conditions import ( DependFailure, DependFinish, DependSuccess, TaskFailed, @@ -45,14 +46,24 @@ def after(self, start): period = self._cls_period(start, None) return self._get_cond(period) - def on(self, span): + def on(self, *spans): # Alias for "at" - return self.at(span) + return self.at(*spans) - def at(self, span): + def at(self, span, *spans): # Alias for "on" - period = self._cls_period(span, time_point=True) - return self._get_cond(period) + n_spans = len(spans) + if n_spans == 0: + # Called like: daily.at("10:00") + period = self._cls_period(span, time_point=True) + return self._get_cond(period) + else: + # Called like: daily.at("10:00", "12:00") + conds = [] + for span in (span, *spans): + cond = self.at(span) + conds.append(cond) + return reduce(lambda a, b: a | b, conds) def starting(self, start): period = self._cls_period.starting(start) diff --git a/rocketry/test/condition/test_api.py b/rocketry/test/condition/test_api.py index 9983cc07..57bf6ad2 100644 --- a/rocketry/test/condition/test_api.py +++ b/rocketry/test/condition/test_api.py @@ -82,6 +82,8 @@ pytest.param(daily.at("23:00:00"), TaskExecutable(period=TimeOfDay("23:00:00", "00:00:00")), id="daily at end"), pytest.param(daily.at("23:00:01"), TaskExecutable(period=TimeOfDay("23:00:01", "00:00:01")), id="daily at specific"), + + pytest.param(weekly.at("Mon", "Wed", "Fri"), TaskExecutable(period=TimeOfWeek("Monday", time_point=True)) | TaskExecutable(period=TimeOfWeek("Wednesday", time_point=True)) | TaskExecutable(period=TimeOfWeek("Friday", time_point=True)), id="weekly at (multiple)"), ] params_pipeline = [ @@ -165,3 +167,5 @@ def test_observe(cond, session): def test_fail(): with pytest.raises(ValueError): every("5 seconds", based="oops") + with pytest.raises(TypeError): + daily.at() \ No newline at end of file