diff --git a/changes.d/6431.fix.md b/changes.d/6431.fix.md new file mode 100644 index 0000000000..900bfee61d --- /dev/null +++ b/changes.d/6431.fix.md @@ -0,0 +1 @@ +The `cycle point format` was imposing an undesirable constraint on `wall_clock` offsets, this has been fixed. diff --git a/cylc/flow/task_proxy.py b/cylc/flow/task_proxy.py index 4e7b60d6e0..0ba67438fc 100644 --- a/cylc/flow/task_proxy.py +++ b/cylc/flow/task_proxy.py @@ -48,7 +48,6 @@ from cylc.flow.cycling.iso8601 import ( point_parse, interval_parse, - ISO8601Interval ) if TYPE_CHECKING: @@ -424,14 +423,18 @@ def get_clock_trigger_time( """ offset_str = offset_str if offset_str else 'P0Y' if offset_str not in self.clock_trigger_times: + # Convert ISO8601Point into metomi-isodatetime TimePoint at full + # second precision (N.B. it still dumps at the same precision + # as workflow cycle point format): + point_time = point_parse(str(point)) if offset_str == 'P0Y': - trigger_time = point + trigger_time = point_time else: - trigger_time = point + ISO8601Interval(offset_str) + trigger_time = point_time + interval_parse(offset_str) - offset = int( - point_parse(str(trigger_time)).seconds_since_unix_epoch) - self.clock_trigger_times[offset_str] = offset + self.clock_trigger_times[offset_str] = int( + trigger_time.seconds_since_unix_epoch + ) return self.clock_trigger_times[offset_str] def get_try_num(self): diff --git a/tests/integration/test_xtrigger_mgr.py b/tests/integration/test_xtrigger_mgr.py index d04c202bd4..73dd66fc2c 100644 --- a/tests/integration/test_xtrigger_mgr.py +++ b/tests/integration/test_xtrigger_mgr.py @@ -29,30 +29,38 @@ def get_task_ids(schd: Scheduler) -> Set[str]: async def test_2_xtriggers(flow, start, scheduler, monkeypatch): - """Test that if an itask has 2 wall_clock triggers with different - offsets that xtrigger manager gets both of them. + """Test that if an itask has 4 wall_clock triggers with different + offsets that xtrigger manager gets all of them. https://github.com/cylc/cylc-flow/issues/5783 n.b. Clock 3 exists to check the memoization path is followed, and causing this test to give greater coverage. + Clock 4 & 5 test higher precision offsets than the CPF. """ task_point = 1588636800 # 2020-05-05 ten_years_ahead = 1904169600 # 2030-05-05 + PT2H35M31S_ahead = 1588646131 # 2020-05-05 02:35:31 + PT2H35M31S_behind = 1588627469 # 2020-05-04 21:24:29 monkeypatch.setattr( 'cylc.flow.xtriggers.wall_clock.time', lambda: ten_years_ahead - 1 ) id_ = flow({ + 'scheduler': { + 'cycle point format': 'CCYY-MM-DD', + }, 'scheduling': { 'initial cycle point': '2020-05-05', 'xtriggers': { 'clock_1': 'wall_clock()', 'clock_2': 'wall_clock(offset=P10Y)', 'clock_3': 'wall_clock(offset=P10Y)', + 'clock_4': 'wall_clock(offset=PT2H35M31S)', + 'clock_5': 'wall_clock(offset=-PT2H35M31S)', }, 'graph': { - 'R1': '@clock_1 & @clock_2 & @clock_3 => foo' + 'R1': '@clock_1 & @clock_2 & @clock_3 & @clock_4 & @clock_5 => foo' } } }) @@ -62,16 +70,22 @@ async def test_2_xtriggers(flow, start, scheduler, monkeypatch): clock_1_ctx = schd.xtrigger_mgr.get_xtrig_ctx(foo_proxy, 'clock_1') clock_2_ctx = schd.xtrigger_mgr.get_xtrig_ctx(foo_proxy, 'clock_2') clock_3_ctx = schd.xtrigger_mgr.get_xtrig_ctx(foo_proxy, 'clock_2') + clock_4_ctx = schd.xtrigger_mgr.get_xtrig_ctx(foo_proxy, 'clock_4') + clock_5_ctx = schd.xtrigger_mgr.get_xtrig_ctx(foo_proxy, 'clock_5') assert clock_1_ctx.func_kwargs['trigger_time'] == task_point assert clock_2_ctx.func_kwargs['trigger_time'] == ten_years_ahead assert clock_3_ctx.func_kwargs['trigger_time'] == ten_years_ahead + assert clock_4_ctx.func_kwargs['trigger_time'] == PT2H35M31S_ahead + assert clock_5_ctx.func_kwargs['trigger_time'] == PT2H35M31S_behind schd.xtrigger_mgr.call_xtriggers_async(foo_proxy) assert foo_proxy.state.xtriggers == { 'clock_1': True, 'clock_2': False, 'clock_3': False, + 'clock_4': True, + 'clock_5': True, }