diff --git a/cylc/flow/cycling/iso8601.py b/cylc/flow/cycling/iso8601.py index 7ceca4174b1..de180b5390a 100644 --- a/cylc/flow/cycling/iso8601.py +++ b/cylc/flow/cycling/iso8601.py @@ -269,8 +269,14 @@ def build_exclusions(self, excl_points): for point in excl_points: try: # Try making an ISO8601Sequence - exclusion = ISO8601Sequence(point, self.exclusion_start_point, - self.exclusion_end_point) + exclusion = ISO8601Sequence( + point, + self.exclusion_start_point, + self.exclusion_end_point, + # disable warnings which are logged when exclusion is a + # time point + zero_duration_warning=False, + ) self.exclusion_sequences.append(exclusion) except (AttributeError, TypeError, ValueError): # Try making an ISO8601Point @@ -284,7 +290,20 @@ class ISO8601Sequence(SequenceBase): """A sequence of ISO8601 date time points separated by an interval. Note that an ISO8601Sequence object (may) contain - ISO8601ExclusionSequences""" + ISO8601ExclusionSequences + + Args: + dep_section: + The full sequence expresion. + context_start_point: + Sequence start point from the global context. + context_end_point: + Sequence end point from the global context. + zero_duration_warning: + If `False`, then zero-duration recurrence warnings will be turned + off. This is set for exclusion parsing. + + """ TYPE = CYCLER_TYPE_ISO8601 TYPE_SORT_KEY = CYCLER_TYPE_SORT_KEY_ISO8601 @@ -303,8 +322,13 @@ def get_async_expr(cls, start_point=None): return "R1" return "R1/" + str(start_point) - def __init__(self, dep_section, context_start_point=None, - context_end_point=None): + def __init__( + self, + dep_section, + context_start_point=None, + context_end_point=None, + zero_duration_warning=True, + ): SequenceBase.__init__( self, dep_section, context_start_point, context_end_point) self.dep_section = dep_section @@ -344,7 +368,9 @@ def __init__(self, dep_section, context_start_point=None, # Parse_recurrence returns an isodatetime TimeRecurrence object # and a list of exclusion strings. self.recurrence, excl_points = self.abbrev_util.parse_recurrence( - dep_section) + dep_section, + zero_duration_warning=zero_duration_warning, + ) # Determine the exclusion start point and end point try: diff --git a/cylc/flow/time_parser.py b/cylc/flow/time_parser.py index b3c17569784..932a62677aa 100644 --- a/cylc/flow/time_parser.py +++ b/cylc/flow/time_parser.py @@ -206,9 +206,23 @@ def parse_recurrence( self, expression: str, context_start_point: Optional['TimePoint'] = None, - context_end_point: Optional['TimePoint'] = None + context_end_point: Optional['TimePoint'] = None, + zero_duration_warning: bool = True, ) -> Tuple[TimeRecurrence, list]: - """Parse an expression in abbrev. or full ISO recurrence format.""" + """Parse an expression in abbrev. or full ISO recurrence format. + + Args: + expression: + The recurrence expression to parse. + context_start_point: + Sequence start point from the global context. + context_end_point: + Sequence end point from the global context. + zero_duration_warning: + If `False`, then zero-duration recurrence warnings will be + turned off. This is set for exclusion parsing. + + """ expression, exclusions = parse_exclusion(str(expression)) if context_start_point is None: @@ -301,8 +315,11 @@ def parse_recurrence( repetitions += 1 end_point = None - if not interval and repetitions != 1 and ( - (format_num != 1 or start_point == end_point) + if ( + zero_duration_warning + and not interval + and repetitions != 1 + and (format_num != 1 or start_point == end_point) ): LOG.warning( "Cannot have more than 1 repetition for zero-duration " diff --git a/tests/unit/cycling/test_iso8601.py b/tests/unit/cycling/test_iso8601.py index 8e95b809c1a..98fc95b9c5b 100644 --- a/tests/unit/cycling/test_iso8601.py +++ b/tests/unit/cycling/test_iso8601.py @@ -584,6 +584,29 @@ def test_multiple_exclusions_extensive(set_cycling_type): assert sequence.get_prev_point(point_4) == point_1 +def test_exclusion_zero_duration_warning(set_cycling_type, caplog, log_filter): + """It should not log zero-duration warnings for exclusion points. + + Exclusions may either be sequences or points. We first attempt to parse + them as sequences, if this fails, we attempt to parse them as points. + + The zero-duration recurrence warning would be logged if we attempted to + parse a point as a sequence. To avoid spurious warnings this should be + turned off for exclusion parsing. + + """ + # parsing a point as a sequences causes a zero-duration warning + set_cycling_type(ISO8601_CYCLING_TYPE, "+05") + with pytest.raises(Exception): + ISO8601Sequence('3000', '2999') + assert log_filter(caplog, contains='zero-duration') + + # parsing a point in an exclusion should not + caplog.clear() + ISO8601Sequence('P1Y ! 3000', '2999') + assert not log_filter(caplog, contains='zero-duration') + + def test_simple(set_cycling_type): """Run some simple tests for date-time cycling.""" set_cycling_type(ISO8601_CYCLING_TYPE, "Z")