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

[FIX] exit MATLAB / Octave on failure when using the python CLI #1277

Merged
merged 7 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion bidspm.m
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function displayArguments(varargin)
end

function value = rootDir()
value = fullfile(fileparts(mfilename('fullpath')));
value = fileparts(mfilename('fullpath'));
end

%% low level actions
Expand Down
2 changes: 1 addition & 1 deletion demos/openneuro/ds002799_run.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
bidspm();

% The directory where the data are located
root_dir = fullfile(fileparts(mfilename('fullpath')));
root_dir = fileparts(mfilename('fullpath'));
root_dir = pwd;

%% Parameters
Expand Down
2 changes: 1 addition & 1 deletion demos/scripts/inverse_normalize.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
% (C) Copyright 2022 bidspm developers

this_dir = fullfile(fileparts(mfilename('fullpath')));
this_dir = fileparts(mfilename('fullpath'));

% we are using the output data from the MoAE demo
% so make sure you have run it before trying this.
Expand Down
2 changes: 1 addition & 1 deletion lib/bids-matlab
2 changes: 1 addition & 1 deletion lib/spm_2_bids
Submodule spm_2_bids updated 1 files
+1 −1 run_tests.m
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ style = [
]

[project.scripts]
bidspm = "bidspm.bidspm:cli"
bidspm = "bidspm.cli:cli"
validate_model = "bidspm.validate:cli"

[project.urls]
Expand Down
110 changes: 13 additions & 97 deletions src/bidspm/bidspm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@
from __future__ import annotations

import subprocess
import sys
from pathlib import Path
from typing import Any

from rich import print

from .matlab import matlab
from .parsers import bidspm_log, common_parser
from .parsers import bidspm_log

log = bidspm_log(name="bidspm")

new_line = ", ...\n\t "
new_line = ", ...\n\t "


def base_cmd(bids_dir: Path, output_dir: Path) -> str:
cmd = " bidspm('init');"
cmd += f" bidspm( '{bids_dir}'{new_line}'{output_dir}'"
cmd = f" bidspm( '{bids_dir}'{new_line}'{output_dir}'"
return cmd


Expand All @@ -28,7 +26,7 @@ def append_main_cmd(cmd: str, analysis_level: str, action: str) -> str:


def end_cmd(cmd: str) -> str:
cmd += "); exit;"
cmd += "); exit;"
return cmd


Expand Down Expand Up @@ -99,10 +97,6 @@ def default_model(
ignore: list[str] | None = None,
options: Path | None = None,
) -> int | str:
if space and len(space) > 1:
log.error(f"Only one space allowed for statistical analysis. Got\n:{space}")
return 1

if task is None:
task = "{''}"

Expand Down Expand Up @@ -141,10 +135,6 @@ def preprocess(
dry_run: bool = False,
options: Path | None = None,
) -> int | str:
if action == "preprocess" and task and len(task) > 1:
log.error(f"Only one task allowed for preprocessing. Got\n:{task}")
return 1

cmd = generate_cmd(
bids_dir=bids_dir,
output_dir=output_dir,
Expand Down Expand Up @@ -229,8 +219,8 @@ def stats(
output_dir: Path,
analysis_level: str,
action: str,
preproc_dir: Path,
model_file: Path,
preproc_dir: Path | None,
model_file: Path | None,
verbosity: int = 2,
participant_label: list[str] | None = None,
fwhm: Any = 6,
Expand All @@ -246,10 +236,6 @@ def stats(
keep_residuals: bool = False,
options: Path | None = None,
) -> int | str:
if space and len(space) > 1:
log.error(f"Only one space allowed for statistical analysis. Got\n:{space}")
return 1

cmd = generate_cmd(
bids_dir=bids_dir,
output_dir=output_dir,
Expand Down Expand Up @@ -281,7 +267,7 @@ def stats(
cmd += f"{new_line}'keep_residuals', true"
cmd = end_cmd(cmd)

log.info("Running statistics.")
log.info(f"Running {action}.")

return cmd

Expand Down Expand Up @@ -310,63 +296,6 @@ def generate_cmd(
return cmd


def cli(argv: Any = sys.argv) -> None:
parser = common_parser()

args, _ = parser.parse_known_args(argv[1:])

bids_dir = Path(args.bids_dir[0]).absolute()
output_dir = Path(args.output_dir[0]).absolute()
analysis_level = args.analysis_level[0]
action = args.action[0]
roi_atlas = args.roi_atlas[0]
bids_filter_file = (
Path(args.bids_filter_file[0]).absolute()
if args.bids_filter_file is not None
else None
)
preproc_dir = (
Path(args.preproc_dir[0]).absolute() if args.preproc_dir is not None else None
)
model_file = (
Path(args.model_file[0]).absolute() if args.model_file is not None else None
)
options = Path(args.options).absolute() if args.options is not None else None

return_code = bidspm(
bids_dir,
output_dir,
analysis_level,
action=action,
participant_label=args.participant_label,
verbosity=args.verbosity,
task=args.task,
space=args.space,
ignore=args.ignore,
fwhm=args.fwhm,
bids_filter_file=bids_filter_file,
dummy_scans=args.dummy_scans,
anat_only=args.anat_only,
skip_validation=args.skip_validation,
dry_run=args.dry_run,
preproc_dir=preproc_dir,
model_file=model_file,
roi_based=args.roi_based,
roi_atlas=roi_atlas,
roi_name=args.roi_name,
roi_dir=args.roi_dir,
concatenate=args.concatenate,
design_only=args.design_only,
keep_residuals=args.keep_residuals,
options=options,
)

if return_code == 1:
sys.exit(1)
else:
sys.exit(0)


def bidspm(
bids_dir: Path,
output_dir: Path,
Expand Down Expand Up @@ -394,13 +323,6 @@ def bidspm(
keep_residuals: bool = False,
options: Path | None = None,
) -> int:
if not bids_dir.is_dir():
log.error(f"The 'bids_dir' does not exist:\n\t{bids_dir}")
return 1

if preproc_dir is not None and not preproc_dir.is_dir():
log.error(f"The 'preproc_dir' does not exist:\n\t{preproc_dir}")
return 1

if action == "default_model":
cmd = default_model(
Expand Down Expand Up @@ -448,14 +370,6 @@ def bidspm(
)

elif action in {"stats", "contrasts", "results"}:
if preproc_dir is None or not preproc_dir.exists():
log.error(f"'preproc_dir' must be specified for stats. Got:\n{preproc_dir}")
return 1

if model_file is None or not model_file.exists():
log.error(f"'model_file' must be specified for stats. Got:\n{model_file}")
return 1

cmd = stats(
bids_dir=bids_dir,
output_dir=output_dir,
Expand All @@ -477,11 +391,13 @@ def bidspm(
keep_residuals=keep_residuals,
options=options,
)
else:
log.error(f"\nunknown action: {action}")
return 1

return cmd if isinstance(cmd, int) else run_command(cmd)
if isinstance(cmd, int):
return cmd

cmd = f" bidspm('init'); try; {cmd} catch; exit; end;"

return run_command(cmd)


def run_command(cmd: str, platform: str | None = None) -> int:
Expand Down
136 changes: 136 additions & 0 deletions src/bidspm/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any

from .bidspm import bidspm
from .parsers import ALLOWED_ACTIONS, bidspm_log, common_parser

log = bidspm_log(name="bidspm")

NOT_IMPLEMENTED = {
"bms",
"bms-bms",
"bms-posterior",
"copy",
"specify_only",
}

with open(Path(__file__).parent / "data" / "exit_codes.json") as f:
EXIT_CODES = json.load(f)

SUPPORTED_ACTIONS = set(ALLOWED_ACTIONS) - NOT_IMPLEMENTED


def validate_actions(action: str) -> None:
if action not in ALLOWED_ACTIONS:
log.error(
f"\nUnknown action: '{action}'."
f"\nSupported actions are: \n\t- {'\n\t- '.join(SUPPORTED_ACTIONS)}"
)
raise SystemExit(EXIT_CODES["USAGE"]["Value"])

if action in NOT_IMPLEMENTED:
log.error(
f"\tThe action '{action}' is not yet implemented."
f"\nSupported actions are: \n\t- {'\n\t- '.join(SUPPORTED_ACTIONS)}"
)
raise SystemExit(EXIT_CODES["USAGE"]["Value"])


def cli(argv: Any = sys.argv) -> None:
parser = common_parser()

args, _ = parser.parse_known_args(argv[1:])

bids_dir = Path(args.bids_dir[0]).absolute()
output_dir = Path(args.output_dir[0]).absolute()
analysis_level = args.analysis_level[0]
action = args.action[0]
roi_atlas = args.roi_atlas[0]
bids_filter_file = (
Path(args.bids_filter_file[0]).absolute()
if args.bids_filter_file is not None
else None
)

preproc_dir: Path | None = (
Path(args.preproc_dir[0]).absolute() if args.preproc_dir is not None else None
)
model_file: Path | None = (
Path(args.model_file[0]).absolute() if args.model_file is not None else None
)
options: Path | None = (
Path(args.options).absolute() if args.options is not None else None
)

space = args.space

task = args.task

validate_actions(action)

if not bids_dir.is_dir():
log.error(f"The 'bids_dir' does not exist:\n\t{bids_dir}")
exit(EXIT_CODES["NOINPUT"]["Value"])

if preproc_dir is not None and not preproc_dir.is_dir():
log.error(f"The 'preproc_dir' does not exist:\n\t{preproc_dir}")
exit(EXIT_CODES["NOINPUT"]["Value"])

if action in {"stats", "contrasts", "results"}:
if preproc_dir is None or not preproc_dir.exists():
log.error(
"'preproc_dir' must be specified for stats.\n" f"Got: {preproc_dir}"
)
raise SystemExit(EXIT_CODES["USAGE"]["Value"])

if model_file is None or not model_file.exists():
log.error("'model_file' must be specified for stats.\n" f"Got: {model_file}")
raise SystemExit(EXIT_CODES["USAGE"]["Value"])

if action in {"stats", "contrasts", "results", "default_model"} and (
space and len(space) > 1
):
log.error("Only one space allowed for statistical analysis.\n" f"Got: {space}")
raise SystemExit(EXIT_CODES["USAGE"]["Value"])

if action == "preprocess" and task and len(task) > 1:
log.error("Only one task allowed for preprocessing.\n" f"Got: {task}")
raise SystemExit(EXIT_CODES["USAGE"]["Value"])

return_code = bidspm(
bids_dir,
output_dir,
analysis_level,
action=action,
participant_label=args.participant_label,
verbosity=args.verbosity,
task=task,
space=space,
ignore=args.ignore,
fwhm=args.fwhm,
bids_filter_file=bids_filter_file,
dummy_scans=args.dummy_scans,
anat_only=args.anat_only,
skip_validation=args.skip_validation,
dry_run=args.dry_run,
preproc_dir=preproc_dir,
model_file=model_file,
roi_based=args.roi_based,
roi_atlas=roi_atlas,
roi_name=args.roi_name,
roi_dir=args.roi_dir,
concatenate=args.concatenate,
design_only=args.design_only,
keep_residuals=args.keep_residuals,
options=options,
)

if return_code == 1:
raise SystemExit(EXIT_CODES["FAILURE"]["Value"])
else:
sys.exit(EXIT_CODES["SUCCESS"]["Value"])
14 changes: 14 additions & 0 deletions src/bidspm/data/allowed_actions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
"bms",
"bms-bms",
"bms-posterior",
"copy",
"contrasts",
"create_roi",
"default_model",
"preprocess",
"results",
"smooth",
"specify_only",
"stats"
]
Loading
Loading