Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make cylc set --pre & cylc set --out skip not override already-satisfied outputs #71

Merged
merged 1 commit into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions cylc/flow/prerequisite.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def satisfy_me(
mode: Optional[RunMode] = None,
forced: bool = False,
) -> 'Set[Tokens]':
"""Set the given outputs as satisfied.
"""Set the given outputs as satisfied (if they are not already).

Return outputs that match.

Expand All @@ -280,12 +280,12 @@ def satisfy_me(
if output_tuple not in self._satisfied:
continue
valid.add(output)
msg: SatisfiedState = (
'satisfied by skip mode' if mode == RunMode.SKIP
else 'satisfied naturally'
)
if self._satisfied[output_tuple] != msg:
self[output_tuple] = 'force satisfied' if forced else msg
if not self._satisfied[output_tuple]:
self[output_tuple] = (
'force satisfied' if forced
else 'satisfied by skip mode' if mode == RunMode.SKIP
else 'satisfied naturally'
)
return valid

def api_dump(self) -> Optional[PbPrerequisite]:
Expand Down Expand Up @@ -328,8 +328,8 @@ def api_dump(self) -> Optional[PbPrerequisite]:
def set_satisfied(self) -> None:
"""Force this prerequisite into the satisfied state.

State can be overridden by calling `self.satisfy_me`.

Sets all of the outputs in this prerequisite to satisfied if not
already.
"""
for task_output in self._satisfied:
if not self._satisfied[task_output]:
Expand Down
60 changes: 46 additions & 14 deletions tests/unit/test_prerequisite.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from functools import partial
from typing import Optional

import pytest

from cylc.flow.cycling.integer import IntegerPoint
from cylc.flow.cycling.loader import ISO8601_CYCLING_TYPE, get_point
from cylc.flow.id import Tokens, detokenise
from cylc.flow.prerequisite import Prerequisite, SatisfiedState
from cylc.flow.run_modes import RunMode


detok = partial(detokenise, selectors=True, relative=True)
Expand Down Expand Up @@ -151,6 +153,7 @@ def satisfied_states_prereq():
prereq[('1', 'b', 'x')] = False
prereq[('1', 'c', 'x')] = 'satisfied from database'
prereq[('1', 'd', 'x')] = 'force satisfied'
prereq[('1', 'e', 'x')] = 'satisfied by skip mode'
return prereq


Expand All @@ -162,6 +165,7 @@ def test_unset_naturally_satisfied(satisfied_states_prereq: Prerequisite):
('1/b', False),
('1/c', True),
('1/d', False),
('1/e', True),
]:
assert (
satisfied_states_prereq.unset_naturally_satisfied(id_) == expected
Expand All @@ -173,6 +177,18 @@ def test_unset_naturally_satisfied(satisfied_states_prereq: Prerequisite):
('1', 'b', 'x'): False,
('1', 'c', 'x'): False,
('1', 'd', 'x'): 'force satisfied',
('1', 'e', 'x'): False,
}


def test_set_satisfied(satisfied_states_prereq: Prerequisite):
satisfied_states_prereq.set_satisfied()
assert satisfied_states_prereq._satisfied == {
('1', 'a', 'x'): 'satisfied naturally',
('1', 'b', 'x'): 'force satisfied',
('1', 'c', 'x'): 'satisfied from database',
('1', 'd', 'x'): 'force satisfied',
('1', 'e', 'x'): 'satisfied by skip mode',
}


Expand Down Expand Up @@ -208,24 +224,40 @@ def test_satisfy_me():
}


@pytest.mark.parametrize('forced', [False, True])
@pytest.mark.parametrize('existing, expected_when_forced', [
(False, 'force satisfied'),
('satisfied from database', 'force satisfied'),
('force satisfied', 'force satisfied'),
('satisfied naturally', 'satisfied naturally'),
@pytest.mark.parametrize('forced, mode, expected', [
(False, None, 'satisfied naturally'),
(True, None, 'force satisfied'),
(True, RunMode.SKIP, 'force satisfied'),
(False, RunMode.SKIP, 'satisfied by skip mode'),
])
def test_satisfy_me__override(
def test_satisfy_me__override_false(
forced: bool,
mode: Optional[RunMode],
expected: SatisfiedState,
):
"""Test satisfying an unsatisfied prereq with different states."""
prereq = Prerequisite(IntegerPoint('2'))
prereq[('1', 'a', 'x')] = False

prereq.satisfy_me([Tokens('//1/a:x')], forced=forced, mode=mode)
assert prereq[('1', 'a', 'x')] == expected


@pytest.mark.parametrize('mode', [None, RunMode.SKIP])
@pytest.mark.parametrize('forced', [True, False])
@pytest.mark.parametrize('existing', [
'satisfied from database',
'force satisfied',
'satisfied naturally',
])
def test_satisfy_me__override_truthy(
existing: SatisfiedState,
expected_when_forced: SatisfiedState,
forced: bool,
mode: Optional[RunMode],
):
"""Test that satisfying a prereq with a different state works as expected
with and without the `forced` arg."""
"""Test that satisfying an already-satisfied prereq doesn't change it."""
prereq = Prerequisite(IntegerPoint('2'))
prereq[('1', 'a', 'x')] = existing

prereq.satisfy_me([Tokens('//1/a:x')], forced=forced)
assert prereq[('1', 'a', 'x')] == (
expected_when_forced if forced else 'satisfied naturally'
)
prereq.satisfy_me([Tokens('//1/a:x')], forced=forced, mode=mode)
assert prereq[('1', 'a', 'x')] == existing
Loading