Skip to content

runner_functions_low_level

_call_command_wrapper(cmd, log_path)

Call a command and write its stdout and stderr to files

Raises:

Type Description
TaskExecutionError

If the subprocess.run call returns a positive exit code

JobExecutionError

If the subprocess.run call returns a negative exit code (e.g. due to the subprocess receiving a TERM or KILL signal)

Source code in fractal_server/app/runner/v2/runner_functions_low_level.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def _call_command_wrapper(cmd: str, log_path: Path) -> None:
    """
    Call a command and write its stdout and stderr to files

    Raises:
        TaskExecutionError: If the `subprocess.run` call returns a positive
                            exit code
        JobExecutionError:  If the `subprocess.run` call returns a negative
                            exit code (e.g. due to the subprocess receiving a
                            TERM or KILL signal)
    """
    try:
        validate_cmd(cmd)
    except ValueError as e:
        raise TaskExecutionError(f"Invalid command. Original error: {str(e)}")

    # Verify that task command is executable
    if shutil.which(shlex_split(cmd)[0]) is None:
        msg = (
            f'Command "{shlex_split(cmd)[0]}" is not valid. '
            "Hint: make sure that it is executable."
        )
        raise TaskExecutionError(msg)

    with open(log_path, "w") as fp_log:
        try:
            result = subprocess.run(  # nosec
                shlex_split(cmd),
                stderr=fp_log,
                stdout=fp_log,
            )
        except Exception as e:
            raise e

    if result.returncode > 0:
        with log_path.open("r") as fp_stderr:
            err = fp_stderr.read()
        raise TaskExecutionError(err)
    elif result.returncode < 0:
        raise JobExecutionError(
            info=f"Task failed with returncode={result.returncode}"
        )

run_single_task(args, command, wftask, workflow_dir_local, workflow_dir_remote=None, logger_name=None)

Runs within an executor.

Source code in fractal_server/app/runner/v2/runner_functions_low_level.py
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def run_single_task(
    args: dict[str, Any],
    command: str,
    wftask: WorkflowTaskV2,
    workflow_dir_local: Path,
    workflow_dir_remote: Optional[Path] = None,
    logger_name: Optional[str] = None,
) -> dict[str, Any]:
    """
    Runs within an executor.
    """

    logger = logging.getLogger(logger_name)
    logger.debug(f"Now start running {command=}")

    if not workflow_dir_remote:
        workflow_dir_remote = workflow_dir_local

    task_name = wftask.task.name

    component = args.pop(_COMPONENT_KEY_, None)
    task_files = get_task_file_paths(
        workflow_dir_local=workflow_dir_local,
        workflow_dir_remote=workflow_dir_remote,
        task_order=wftask.order,
        task_name=task_name,
        component=component,
    )

    # Write arguments to args.json file
    with task_files.args.open("w") as f:
        json.dump(args, f, indent=2)

    # Assemble full command
    full_command = (
        f"{command} "
        f"--args-json {task_files.args.as_posix()} "
        f"--out-json {task_files.metadiff.as_posix()}"
    )

    try:
        _call_command_wrapper(
            full_command,
            log_path=task_files.log,
        )
    except TaskExecutionError as e:
        e.workflow_task_order = wftask.order
        e.workflow_task_id = wftask.id
        e.task_name = wftask.task.name
        raise e

    try:
        with task_files.metadiff.open("r") as f:
            out_meta = json.load(f)
    except FileNotFoundError as e:
        logger.debug(
            "Task did not produce output metadata. "
            f"Original FileNotFoundError: {str(e)}"
        )
        out_meta = None

    if out_meta == {}:
        return None
    return out_meta