예제 #1
0
def test_process_startcp(
    startcp: Optional[str],
    starttask: Optional[str],
    expected: str,
    expected_err: Optional[Tuple[Type[Exception], str]],
    monkeypatch: pytest.MonkeyPatch, set_cycling_type: Fixture
) -> None:
    """Test WorkflowConfig.process_start_cycle_point().

    An icp of 1899-05-01T00+0530 is assumed, and "now" is assumed to be
    2005-01-02T06:15+0530

    Params:
        startcp: The start cycle point given by cli option.
        expected: The expected startcp value that gets set.
        expected_err: Expected exception.
    """
    set_cycling_type(ISO8601_CYCLING_TYPE, time_zone="+0530")
    mocked_config = Mock(initial_point='18990501T0000+0530')
    mocked_config.options.startcp = startcp
    mocked_config.options.starttask = starttask
    monkeypatch.setattr('cylc.flow.config.get_current_time_string',
                        lambda: '20050102T0615+0530')
    if expected_err is not None:
        err, msg = expected_err
        with pytest.raises(err) as exc:
            WorkflowConfig.process_start_cycle_point(mocked_config)
        assert msg in str(exc.value)
    else:
        WorkflowConfig.process_start_cycle_point(mocked_config)
        assert str(mocked_config.start_point) == expected
예제 #2
0
def test_process_fcp(cycling_type: str, scheduling_cfg: dict,
                     options_fcp: Optional[str], expected_fcp: Optional[str],
                     expected_err: Optional[Tuple[Type[Exception], str]],
                     set_cycling_type: Fixture) -> None:
    """Test WorkflowConfig.process_final_cycle_point().

    Params:
        cycling_type: Workflow cycling type.
        scheduling_cfg: 'scheduling' section of workflow config.
        options_fcp: The fcp set by cli option.
        expected_fcp: The expected fcp value that gets set.
        expected_err: Exception class expected to be raised plus the message.
    """
    set_cycling_type(cycling_type, time_zone='+0530')
    mocked_config = Mock(cycling_type=cycling_type)
    mocked_config.cfg = {'scheduling': scheduling_cfg}
    mocked_config.initial_point = loader.get_point(
        scheduling_cfg['initial cycle point']).standardise()
    mocked_config.final_point = None
    mocked_config.options.fcp = options_fcp

    if expected_err:
        err, msg = expected_err
        with pytest.raises(err) as exc:
            WorkflowConfig.process_final_cycle_point(mocked_config)
        assert msg in str(exc.value)
    else:
        WorkflowConfig.process_final_cycle_point(mocked_config)
        assert mocked_config.cfg['scheduling'][
            'final cycle point'] == expected_fcp
        assert str(mocked_config.final_point) == str(expected_fcp)
예제 #3
0
def main(_, options: 'Values', *ids) -> None:
    workflow_id, _, flow_file = parse_id(
        *ids,
        src=True,
        constraint='workflows',
    )

    # extract task host platforms from the workflow_id
    config = WorkflowConfig(
        workflow_id, flow_file, options,
        load_template_vars(options.templatevars, options.templatevars_file))

    platforms = {
        config.get_config(['runtime', name, 'platform'])
        for name in config.get_namespace_list('all tasks')
    } - {None, 'localhost'}

    # When "workflow run hosts" are formalised as "flow platforms"
    # we can substitute `localhost` for this, in the mean time
    # we will have to assume that flow hosts are configured correctly.

    if not platforms:
        sys.exit(0)

    verbose = cylc.flow.flags.verbosity > 0

    # get the cylc version on each platform
    versions = {}
    for platform_name in sorted(platforms):
        platform = get_platform(platform_name)
        host = get_host_from_platform(platform, bad_hosts=None)
        cmd = construct_ssh_cmd(['version'], platform, host)
        if verbose:
            print(cmd)
        proc = procopen(cmd, stdin=DEVNULL, stdout=PIPE, stderr=PIPE)
        out, err = proc.communicate()
        out = out.decode()
        err = err.decode()
        if proc.wait() == 0:
            if verbose:
                print("   %s" % out)
            versions[platform_name] = out.strip()
        else:
            versions[platform_name] = f'ERROR: {err.strip()}'

    # report results
    max_len = max((len(platform_name) for platform_name in platforms))
    print(f'{"platform".rjust(max_len)}: cylc version')
    print('-' * (max_len + 14))
    for platform_name, result in versions.items():
        print(f'{platform_name.rjust(max_len)}: {result}')
    if all((version == CYLC_VERSION for version in versions.values())):
        ret_code = 0
    elif options.error:
        ret_code = 1
    else:
        ret_code = 0
    sys.exit(ret_code)
예제 #4
0
def test_implicit_tasks(
    allow_implicit_tasks: Optional[bool],
    cylc7_compat: bool,
    rose_suite_conf: bool,
    expected_exc: Optional[Type[Exception]],
    expected_log_level: Optional[int],
    caplog: pytest.LogCaptureFixture,
    log_filter: Callable,
    monkeypatch: pytest.MonkeyPatch,
    tmp_flow_config: Callable
):
    """Test that the prescence of implicit tasks in the config
    is handled correctly.

    Params:
        allow_implicit_tasks: Value of "[scheduler]allow implicit tasks".
        cylc7_compat: Whether Cylc 7 backwards compatibility is turned on.
        rose_suite_conf: Whether a rose-suite.conf file is present in run dir.
        expected_exc: Exception expected to be raised only when
            "[scheduler]allow implicit tasks" is not set.
        expected_log_level: Expected logging severity level for the
            "implicit tasks detected" message only when
            "[scheduler]allow implicit tasks" is not set.
    """
    # Setup
    reg = 'rincewind'
    flow_file: Path = tmp_flow_config(reg, f"""
    [scheduler]
        {
            f'allow implicit tasks = {allow_implicit_tasks}'
            if allow_implicit_tasks is not None else ''
        }
    [scheduling]
        [[graph]]
            R1 = foo
    """)
    monkeypatch.setattr('cylc.flow.flags.cylc7_back_compat', cylc7_compat)
    if rose_suite_conf:
        (flow_file.parent / 'rose-suite.conf').touch()
    caplog.set_level(logging.DEBUG, CYLC_LOG)
    if allow_implicit_tasks is True:
        expected_exc = None
        expected_log_level = logging.DEBUG
    elif allow_implicit_tasks is False:
        expected_exc = WorkflowConfigError
    # Test
    args: dict = {'workflow': reg, 'fpath': flow_file, 'options': None}
    expected_msg = "implicit tasks detected"
    if expected_exc:
        with pytest.raises(expected_exc) as exc:
            WorkflowConfig(**args)
        assert expected_msg in str(exc.value)
    else:
        WorkflowConfig(**args)
        assert log_filter(
            caplog, level=expected_log_level, contains=expected_msg
        )
예제 #5
0
def main(parser: COP, options: 'Values', workflow_id1: str, workflow_id2: str):
    workflow_id_1, _, workflow_file_1_ = parse_id(
        workflow_id1,
        src=True,
        constraint='workflows',
    )
    workflow_id_2, _, workflow_file_2_ = parse_id(
        workflow_id2,
        src=True,
        constraint='workflows',
    )
    if workflow_file_1_ == workflow_file_2_:
        parser.error("You can't diff a single workflow.")
    print(f"Parsing {workflow_id_1} ({workflow_file_1_})")
    template_vars = load_template_vars(
        options.templatevars, options.templatevars_file
    )
    config1 = WorkflowConfig(
        workflow_id_1, workflow_file_1_, options, template_vars
    ).cfg
    print(f"Parsing {workflow_id_2} ({workflow_file_2_})")
    config2 = WorkflowConfig(
        workflow_id_2, workflow_file_2_, options, template_vars,
        is_reload=True
    ).cfg

    if config1 == config2:
        print(
            f"Workflow definitions {workflow_id_1} and {workflow_id_2} are "
            f"identical"
        )
        sys.exit(0)

    print(f"Workflow definitions {workflow_id_1} and {workflow_id_2} differ")

    workflow1_only = {}  # type: ignore
    workflow2_only = {}  # type: ignore
    diff_1_2 = {}  # type: ignore
    # TODO: this whole file could do wih refactoring at some point

    diffdict(config1, config2, workflow1_only, workflow2_only, diff_1_2)

    if n_oone > 0:
        print(f'\n{n_oone} items only in {workflow_id_1} (<)')
        prdict(workflow1_only, '<', nested=options.nested)

    if n_otwo > 0:
        print(f'\n{n_otwo} items only in {workflow_id_2} (>)')
        prdict(workflow2_only, '>', nested=options.nested)

    if n_diff > 0:
        print(f'\n{n_diff} common items differ {workflow_id_1}(<) '
              f'{workflow_id_2}(>)')
        prdict(diff_1_2, '', diff=True, nested=options.nested)
예제 #6
0
def test_process_runahead_limit(cycling_type: str, runahead_limit: str,
                                valid: bool,
                                set_cycling_type: Callable) -> None:
    set_cycling_type(cycling_type)
    mock_config = Mock(cycling_type=cycling_type)
    mock_config.cfg = {'scheduling': {'runahead limit': runahead_limit}}
    if valid:
        WorkflowConfig.process_runahead_limit(mock_config)
    else:
        with pytest.raises(WorkflowConfigError) as exc:
            WorkflowConfig.process_runahead_limit(mock_config)
        assert "bad runahead limit" in str(exc.value).lower()
예제 #7
0
 def _test(utc_mode, expected, expected_warnings=0):
     mock_glbl_cfg(
         'cylc.flow.config.glbl_cfg', f'''
         [scheduler]
             UTC mode = {utc_mode['glbl']}
         ''')
     mock_config = Mock()
     mock_config.cfg = {'scheduler': {'UTC mode': utc_mode['workflow']}}
     mock_config.options.utc_mode = utc_mode['stored']
     WorkflowConfig.process_utc_mode(mock_config)
     assert mock_config.cfg['scheduler']['UTC mode'] is expected
     assert get_utc_mode() is expected
     assert len(caplog.record_tuples) == expected_warnings
     caplog.clear()
예제 #8
0
 def _test(cp_tz, utc_mode, expected, expected_warnings=0):
     set_utc_mode(utc_mode)
     mock_config = Mock()
     mock_config.cfg = {
         'scheduler': {
             'cycle point time zone': cp_tz['workflow']
         }
     }
     mock_config.options.cycle_point_tz = cp_tz['stored']
     WorkflowConfig.process_cycle_point_tz(mock_config)
     assert mock_config.cfg['scheduler'][
         'cycle point time zone'] == expected
     assert len(caplog.record_tuples) == expected_warnings
     caplog.clear()
예제 #9
0
 def test_xfunction_not_callable(self, mock_glbl_cfg, tmp_path):
     """Test for error when a xtrigger function is not callable."""
     mock_glbl_cfg(
         'cylc.flow.platforms.glbl_cfg',
         '''
         [platforms]
             [[localhost]]
                 hosts = localhost
         '''
     )
     python_dir = tmp_path / "lib" / "python"
     python_dir.mkdir(parents=True)
     not_callable_file = python_dir / "not_callable.py"
     # NB: we are not returning a lambda, instead we have a scalar
     not_callable_file.write_text("""not_callable = 42""")
     flow_file = tmp_path / WorkflowFiles.FLOW_FILE
     flow_config = """
     [scheduling]
         initial cycle point = 2018-01-01
         [[xtriggers]]
             oopsie = not_callable()
         [[graph]]
             R1 = '@oopsie => qux'
     """
     flow_file.write_text(flow_config)
     with pytest.raises(ValueError) as excinfo:
         WorkflowConfig(
             workflow="workflow_with_not_callable",
             fpath=flow_file,
             options=Mock(spec=[])
         )
     assert "callable" in str(excinfo.value)
예제 #10
0
파일: test_config.py 프로젝트: lparkes/cylc
def test_c7_back_compat_optional_outputs(tmp_flow_config, monkeypatch, caplog):
    """Test optional and required outputs Cylc 7 back compat mode.

    Success outputs should be required, others optional. Tested here because
    success is set to required after graph parsing, in taskdef processing.

    """
    caplog.set_level(logging.WARNING, CYLC_LOG)
    monkeypatch.setattr('cylc.flow.flags.cylc7_back_compat', True)
    reg = 'custom_out2'
    flow_file = tmp_flow_config(
        reg, '''
    [scheduling]
        [[graph]]
            R1 = """
            foo:x => bar
            foo:fail = oops
            foo => spoo
            """
    [runtime]
        [[bar, oops, spoo]]
        [[foo]]
           [[[outputs]]]
                x = x
    ''')

    cfg = WorkflowConfig(workflow=reg, fpath=flow_file, options=None)
    assert WorkflowConfig.CYLC7_GRAPH_COMPAT_MSG in caplog.text

    for taskdef in cfg.taskdefs.values():
        for output, (_, required) in taskdef.outputs.items():
            if output in [TASK_OUTPUT_SUBMITTED, TASK_OUTPUT_SUCCEEDED]:
                assert required
            else:
                assert not required
예제 #11
0
 def test_xfunction_imports(
         self, mock_glbl_cfg: Fixture, tmp_path: Path,
         xtrigger_mgr: XtriggerManager):
     """Test for a workflow configuration with valid xtriggers"""
     mock_glbl_cfg(
         'cylc.flow.platforms.glbl_cfg',
         '''
         [platforms]
             [[localhost]]
                 hosts = localhost
         '''
     )
     python_dir = tmp_path / "lib" / "python"
     python_dir.mkdir(parents=True)
     name_a_tree_file = python_dir / "name_a_tree.py"
     # NB: we are not returning a lambda, instead we have a scalar
     name_a_tree_file.write_text("""name_a_tree = lambda: 'jacaranda'""")
     flow_file = tmp_path / WorkflowFiles.FLOW_FILE
     flow_config = """
     [scheduler]
         allow implicit tasks = True
     [scheduling]
         initial cycle point = 2018-01-01
         [[xtriggers]]
             tree = name_a_tree()
         [[graph]]
             R1 = '@tree => qux'
     """
     flow_file.write_text(flow_config)
     workflow_config = WorkflowConfig(
         workflow="name_a_tree", fpath=flow_file, options=Mock(spec=[]),
         xtrigger_mgr=xtrigger_mgr
     )
     assert 'tree' in workflow_config.xtrigger_mgr.functx_map
예제 #12
0
def get_platform_from_task_def(flow: str, task: str) -> Dict[str, Any]:
    """Return the platform dictionary for a particular task.

    Uses the flow definition - designed to be used with tasks
    with unsubmitted jobs. Evaluates platform/host defined as subshell.

    Args:
        flow: The name of the Cylc flow to be queried.
        task: The name of the task to be queried.

    Returns:
        Platform Dictionary.
    """
    _, _, flow_file = parse_id(flow, constraint='workflows', src=True)
    config = WorkflowConfig(flow, flow_file, Values())
    # Get entire task spec to allow Cylc 7 platform from host guessing.
    task_spec = config.pcfg.get(['runtime', task])
    # check for subshell and evaluate
    if (task_spec.get('platform')
            and is_platform_definition_subshell(task_spec['platform'])):
        task_spec['platform'] = eval_subshell(task_spec['platform'])
    elif (task_spec.get('remote', {}).get('host')
          and HOST_REC_COMMAND.match(task_spec['remote']['host'])):
        task_spec['remote']['host'] = eval_subshell(
            task_spec['remote']['host'])
    platform = get_platform(task_spec)
    return platform
예제 #13
0
 def test_xfunction_import_error(self, mock_glbl_cfg, tmp_path):
     """Test for error when a xtrigger function cannot be imported."""
     mock_glbl_cfg(
         'cylc.flow.platforms.glbl_cfg',
         '''
         [platforms]
             [[localhost]]
                 hosts = localhost
         '''
     )
     python_dir = tmp_path / "lib" / "python"
     python_dir.mkdir(parents=True)
     caiman_file = python_dir / "caiman.py"
     # NB: we are not returning a lambda, instead we have a scalar
     caiman_file.write_text("""caiman = lambda: True""")
     flow_file = tmp_path / WorkflowFiles.FLOW_FILE
     flow_config = """
     [scheduling]
         initial cycle point = 2018-01-01
         [[xtriggers]]
             oopsie = piranha()
         [[graph]]
             R1 = '@oopsie => qux'
     """
     flow_file.write_text(flow_config)
     with pytest.raises(ImportError) as excinfo:
         WorkflowConfig(
             workflow="caiman_workflow",
             fpath=flow_file,
             options=Mock(spec=[])
         )
     assert "not found" in str(excinfo.value)
예제 #14
0
파일: graph.py 프로젝트: lparkes/cylc
def get_config(workflow: str, opts: 'Values') -> WorkflowConfig:
    """Return a WorkflowConfig object for the provided reg / path."""
    workflow, flow_file = parse_reg(workflow, src=True)
    template_vars = get_template_vars(opts)
    return WorkflowConfig(workflow,
                          flow_file,
                          opts,
                          template_vars=template_vars)
예제 #15
0
파일: test_config.py 프로젝트: lparkes/cylc
def test_valid_rsync_includes_returns_correct_list(tmp_flow_config):
    """Test that the rsync includes in the correct """
    reg = 'rsynctest'
    flow_file = tmp_flow_config(
        reg, """
    [scheduling]
        initial cycle point = 2020-01-01
        [[dependencies]]
            graph = "blah => deeblah"
    [scheduler]
        install = dir/, dir2/, file1, file2
        allow implicit tasks = True
    """)

    config = WorkflowConfig(workflow=reg, fpath=flow_file, options=Values())

    rsync_includes = WorkflowConfig.get_validated_rsync_includes(config)
    assert rsync_includes == ['dir/', 'dir2/', 'file1', 'file2']
예제 #16
0
def test_process_icp(
    cycling_type: str,
    scheduling_cfg: Dict[str, Any],
    expected_icp: Optional[str],
    expected_opt_icp: Optional[str],
    expected_err: Optional[Tuple[Type[Exception], str]],
    monkeypatch: pytest.MonkeyPatch, set_cycling_type: Fixture
) -> None:
    """Test WorkflowConfig.process_initial_cycle_point().

    "now" is assumed to be 2005-01-02T06:15+0530

    Params:
        cycling_type: Workflow cycling type.
        scheduling_cfg: 'scheduling' section of workflow config.
        expected_icp: The expected icp value that gets set.
        expected_opt_icp: The expected value of options.icp that gets set
            (this gets stored in the workflow DB).
        expected_err: Exception class expected to be raised plus the message.
    """
    set_cycling_type(cycling_type, time_zone="+0530")
    mocked_config = Mock(cycling_type=cycling_type)
    mocked_config.cfg = {
        'scheduling': scheduling_cfg
    }
    mocked_config.options.icp = None
    monkeypatch.setattr('cylc.flow.config.get_current_time_string',
                        lambda: '20050102T0615+0530')

    if expected_err:
        err, msg = expected_err
        with pytest.raises(err) as exc:
            WorkflowConfig.process_initial_cycle_point(mocked_config)
        assert msg in str(exc.value)
    else:
        WorkflowConfig.process_initial_cycle_point(mocked_config)
        assert mocked_config.cfg[
            'scheduling']['initial cycle point'] == expected_icp
        assert str(mocked_config.initial_point) == expected_icp
        opt_icp = mocked_config.options.icp
        if opt_icp is not None:
            opt_icp = str(loader.get_point(opt_icp).standardise())
        assert opt_icp == expected_opt_icp
예제 #17
0
파일: diff.py 프로젝트: MetRonnie/cylc-flow
def main(parser, options, *args):
    workflow1_name, workflow1_config = parse_workflow_arg(options, args[0])
    workflow2_name, workflow2_config = parse_workflow_arg(options, args[1])
    if workflow1_name == workflow2_name:
        parser.error("You can't diff a single workflow.")
    print(f"Parsing {workflow1_name} ({workflow1_config})")
    template_vars = load_template_vars(
        options.templatevars, options.templatevars_file)
    config1 = WorkflowConfig(
        workflow1_name, workflow1_config, options, template_vars).cfg
    print(f"Parsing {workflow2_name} ({workflow2_config})")
    config2 = WorkflowConfig(
        workflow2_name, workflow2_config, options, template_vars,
        is_reload=True).cfg

    if config1 == config2:
        print(
            f"Workflow definitions {workflow1_name} and {workflow2_name} are "
            f"identical"
        )
        sys.exit(0)

    print(f"Workflow definitions {workflow1_name} and {workflow2_name} differ")

    workflow1_only = {}
    workflow2_only = {}
    diff_1_2 = {}

    diffdict(config1, config2, workflow1_only, workflow2_only, diff_1_2)

    if n_oone > 0:
        print(f'\n{n_oone} items only in {workflow1_name} (<)')
        prdict(workflow1_only, '<', nested=options.nested)

    if n_otwo > 0:
        print(f'\n{n_otwo} items only in {workflow2_name} (>)')
        prdict(workflow2_only, '>', nested=options.nested)

    if n_diff > 0:
        print(f'\n{n_diff} common items differ {workflow1_name}(<) '
              f'{workflow2_name}(>)')
        prdict(diff_1_2, '', diff=True, nested=options.nested)
예제 #18
0
def test_valid_rsync_includes_returns_correct_list(tmp_path):
    """Test that the rsync includes in the correct """

    flow_cylc_content = """
    [scheduling]
        initial cycle point = 2020-01-01
        [[dependencies]]
            graph = "blah => deeblah"
    [scheduler]
        install = dir/, dir2/, file1, file2
        allow implicit tasks = True
    """
    flow_cylc = tmp_path.joinpath(WorkflowFiles.FLOW_FILE)
    flow_cylc.write_text(flow_cylc_content)

    config = WorkflowConfig(workflow="rsynctest", fpath=flow_cylc,
                            options=Mock(spec=[]))

    rsync_includes = WorkflowConfig.get_validated_rsync_includes(config)
    assert rsync_includes == ['dir/', 'dir2/', 'file1', 'file2']
예제 #19
0
def test_process_startcp(startcp: Optional[str], expected: str,
                         monkeypatch: pytest.MonkeyPatch,
                         set_cycling_type: Fixture) -> None:
    """Test WorkflowConfig.process_start_cycle_point().

    An icp of 1899-05-01T00+0530 is assumed, and "now" is assumed to be
    2005-01-02T06:15+0530

    Params:
        startcp: The start cycle point given by cli option.
        expected: The expected startcp value that gets set.
    """
    set_cycling_type(ISO8601_CYCLING_TYPE, time_zone="+0530")
    mocked_config = Mock(initial_point='18990501T0000+0530')
    mocked_config.options.startcp = startcp
    monkeypatch.setattr('cylc.flow.config.get_current_time_string',
                        lambda: '20050102T0615+0530')

    WorkflowConfig.process_start_cycle_point(mocked_config)
    assert str(mocked_config.start_point) == expected
예제 #20
0
def get_config(workflow_id: str, opts: 'Values') -> WorkflowConfig:
    """Return a WorkflowConfig object for the provided reg / path."""
    workflow_id, _, flow_file = parse_id(
        workflow_id,
        src=True,
        constraint='workflows',
    )
    template_vars = get_template_vars(opts)
    return WorkflowConfig(workflow_id,
                          flow_file,
                          opts,
                          template_vars=template_vars)
예제 #21
0
def test_prelim_process_graph(scheduling_cfg: Dict[str, Any],
                              scheduling_expected: Optional[Dict[str, Any]],
                              expected_err: Optional[Tuple[Type[Exception],
                                                           str]]):
    """Test WorkflowConfig.prelim_process_graph().

    Params:
        scheduling_cfg: 'scheduling' section of workflow config.
        scheduling_expected: The expected scheduling section after preliminary
            processing.
        expected_err: Exception class expected to be raised plus the message.
    """
    mock_config = Mock(cfg={'scheduling': scheduling_cfg})

    if expected_err:
        err, msg = expected_err
        with pytest.raises(err) as exc:
            WorkflowConfig.prelim_process_graph(mock_config)
        assert msg in str(exc.value)
    else:
        WorkflowConfig.prelim_process_graph(mock_config)
        assert mock_config.cfg['scheduling'] == scheduling_expected
예제 #22
0
def test_success_after_optional_submit(tmp_flow_config, graph):
    """Check foo:succeed is not required if foo:submit is optional."""
    reg = 'blargh'
    flow_file = tmp_flow_config(reg, f"""
    [scheduling]
        [[graph]]
            R1 = {graph}
    [runtime]
        [[bar]]
        [[foo]]
    """)
    cfg = WorkflowConfig(workflow=reg, fpath=flow_file, options=None)
    assert not cfg.taskdefs['foo'].outputs[TASK_OUTPUT_SUCCEEDED][1]
예제 #23
0
def test_family_inheritance_and_quotes(
    fam_txt: str,
    mock_glbl_cfg: Callable, tmp_path: Path
) -> None:
    """Test that inheritance does not ignore items, if not all quoted.

    For example:

        inherit = 'MAINFAM<major, minor>', SOMEFAM
        inherit = 'BIGFAM', SOMEFAM

    See bug #2700 for more/
    """
    mock_glbl_cfg(
        'cylc.flow.platforms.glbl_cfg',
        '''
        [platforms]
            [[localhost]]
                hosts = localhost
        '''
    )
    cfg = f'''
        [scheduler]
            allow implicit tasks = True
        [task parameters]
            major = 1..5
            minor = 10..20
        [scheduling]
            [[graph]]
                R1 = """hello => MAINFAM<major, minor>
                        hello => SOMEFAM"""
        [runtime]
            [[root]]
                script = true
            [[MAINFAM<major, minor>]]
            [[SOMEFAM]]
            [[ goodbye_0<major, minor> ]]
                inherit = 'MAINFAM<major, minor>', {fam_txt}
    '''
    file_path = tmp_path / 'thing.cylc'
    file_path.write_text(cfg)
    config = WorkflowConfig(
        'test',
        str(file_path),
        template_vars={},
        options=Mock(spec=[])
    )
    assert ('goodbye_0_major1_minor10' in
            config.runtime['descendants']['MAINFAM_major1_minor10'])
    assert ('goodbye_0_major1_minor10' in
            config.runtime['descendants']['SOMEFAM'])
예제 #24
0
def test_undefined_custom_output(tmp_flow_config: Callable):
    """Test error on undefined custom output referenced in graph."""
    reg = 'custom_out1'
    flow_file = tmp_flow_config(reg, """
    [scheduling]
        [[graph]]
            R1 = "foo:x => bar"
    [runtime]
        [[foo, bar]]
    """)

    with pytest.raises(WorkflowConfigError) as cm:
        WorkflowConfig(workflow=reg, fpath=flow_file, options=Values())
    assert "Undefined custom output" in str(cm.value)
예제 #25
0
def test_success_after_optional_submit(tmp_path, graph):
    """Check foo:succeed is not required if foo:submit is optional."""
    flow_config = f"""
    [scheduling]
        [[graph]]
            R1 = {graph}
    [runtime]
        [[bar]]
        [[foo]]
    """
    flow_file = tmp_path.joinpath(WorkflowFiles.FLOW_FILE)
    flow_file.write_text(flow_config)
    cfg = WorkflowConfig(workflow='blargh', fpath=flow_file, options=None)
    assert not cfg.taskdefs['foo'].outputs[TASK_OUTPUT_SUCCEEDED][1]
예제 #26
0
def test_implicit_success_required(tmp_flow_config, graph):
    """Check foo:succeed is required if success/fail not used in the graph."""
    reg = 'blargh'
    flow_file = tmp_flow_config(reg, f"""
    [scheduling]
        [[graph]]
            R1 = {graph}
    [runtime]
        [[bar]]
        [[foo]]
           [[[outputs]]]
               x = "the quick brown fox"
    """)
    cfg = WorkflowConfig(workflow=reg, fpath=flow_file, options=None)
    assert cfg.taskdefs['foo'].outputs[TASK_OUTPUT_SUCCEEDED][1]
예제 #27
0
파일: test_config.py 프로젝트: lparkes/cylc
def test_rsync_includes_will_not_accept_sub_directories(tmp_flow_config):
    reg = 'rsynctest'
    flow_file = tmp_flow_config(
        reg, """
    [scheduling]
        initial cycle point = 2020-01-01
        [[dependencies]]
            graph = "blah => deeblah"
    [scheduler]
        install = dir/, dir2/subdir2/, file1, file2
    """)

    with pytest.raises(WorkflowConfigError) as exc:
        WorkflowConfig(workflow=reg, fpath=flow_file, options=Values())
    assert "Directories can only be from the top level" in str(exc.value)
예제 #28
0
def test_undefined_custom_output(tmp_path):
    """Test error on undefined custom output referenced in graph."""
    flow_config = """
    [scheduling]
        [[graph]]
            R1 = "foo:x => bar"
    [runtime]
        [[foo, bar]]
    """
    flow_file = tmp_path.joinpath(WorkflowFiles.FLOW_FILE)
    flow_file.write_text(flow_config)

    with pytest.raises(WorkflowConfigError) as cm:
        WorkflowConfig(
            workflow='custom_out1', fpath=flow_file, options=None)
    assert "Undefined custom output" in str(cm.value)
예제 #29
0
def test_implicit_success_required(tmp_path, graph):
    """Check foo:succeed is required if success/fail not used in the graph."""
    flow_config = f"""
    [scheduling]
        [[graph]]
            R1 = {graph}
    [runtime]
        [[bar]]
        [[foo]]
           [[[outputs]]]
               x = "the quick brown fox"
    """
    flow_file = tmp_path.joinpath(WorkflowFiles.FLOW_FILE)
    flow_file.write_text(flow_config)
    cfg = WorkflowConfig(workflow='blargh', fpath=flow_file, options=None)
    assert cfg.taskdefs['foo'].outputs[TASK_OUTPUT_SUCCEEDED][1]
예제 #30
0
def main(
    parser: COP,
    options: 'Values',
    *ids,
) -> None:

    if options.print_platform_names and options.print_platforms:
        options.print_platform_names = False

    if options.print_platform_names or options.print_platforms:
        # Get platform information:
        if ids:
            raise UserInputError(
                "Workflow IDs are incompatible with --platform options.")
        glbl_cfg().platform_dump(options.print_platform_names,
                                 options.print_platforms)
        return

    if not ids:
        if options.print_hierarchy:
            print("\n".join(get_config_file_hierarchy()))
            return

        glbl_cfg().idump(options.item,
                         not options.defaults,
                         oneline=options.oneline,
                         none_str=options.none_str)
        return

    workflow_id, _, flow_file = parse_id(
        *ids,
        src=True,
        constraint='workflows',
    )

    if options.print_hierarchy:
        print("\n".join(get_config_file_hierarchy(workflow_id)))
        return

    config = WorkflowConfig(workflow_id, flow_file, options,
                            get_template_vars(options))

    config.pcfg.idump(options.item,
                      not options.defaults,
                      oneline=options.oneline,
                      none_str=options.none_str)