Skip to content

Commit 6495372

Browse files
committed
brainprep/decorators: add and apply signature hook.
1 parent 357a7cf commit 6495372

19 files changed

Lines changed: 325 additions & 30 deletions

brainprep/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def main():
150150
"subject-level-fmriprep": wf.brainprep_fmriprep,
151151
"group-level-fmriprep": wf.brainprep_group_fmriprep,
152152
"subject-level-sulcirec": wf.brainprep_sulcirec,
153+
"group-level-sulcirec": wf.brainprep_group_sulcirec,
153154
}
154155
for key, fn in commands.items():
155156
commands[key] = make_wrapped(fn, is_vbm=key.endswith("vbm"))

brainprep/decorators.py

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import json
1616
import platform
1717
import subprocess
18+
import time
1819
from collections.abc import Callable, Iterable
1920
from pathlib import Path
2021
from typing import (
@@ -41,6 +42,7 @@
4142
coerce_to_list,
4243
coerce_to_path,
4344
parse_bids_keys,
45+
print_call,
4446
print_command,
4547
print_title,
4648
)
@@ -706,8 +708,8 @@ class LogRuntimeHook(Hook):
706708
Log runtime metadata and input/output details of a function call.
707709
708710
This hook uses an ``RSTReport`` instance to record metadata about the
709-
execution of the decorated function. It captures the follwoing
710-
informations:
711+
execution of the decorated function. It captures the following
712+
information:
711713
712714
- the function's name, module, and docstring
713715
- the input arguments passed to the function
@@ -1048,3 +1050,69 @@ def step(
10481050
for plug in hooks:
10491051
outputs = plug.after_call(outputs)
10501052
return outputs
1053+
1054+
1055+
class SignatureHook(Hook):
1056+
"""
1057+
Decorator that prints which function is called, its arguments and
1058+
execution time.
1059+
1060+
Examples
1061+
--------
1062+
>>> from brainprep.decorators import step, SignatureHook
1063+
1064+
>>> @step(
1065+
... hooks=[SignatureHook()]
1066+
... )
1067+
... def add(a, b):
1068+
... '''Adds two numbers.'''
1069+
... return a + b
1070+
1071+
>>> result = add(3, 5) # doctest: +SKIP
1072+
"""
1073+
1074+
def before_call(
1075+
self,
1076+
func: Callable,
1077+
inputs: dict[str, Any],
1078+
) -> dict[str, Any]:
1079+
"""
1080+
Display start information.
1081+
1082+
Parameters
1083+
----------
1084+
func : Callable
1085+
The function to be decorated.
1086+
inputs : dict[str, Any]
1087+
Positional and keyword arguments passed to `func`.
1088+
1089+
Returns
1090+
-------
1091+
inputs : dict[str, Any]
1092+
Unchanged positional and keyword arguments passed to `func`.
1093+
"""
1094+
self._start = time.perf_counter()
1095+
if inputs:
1096+
args_lines = ",\n".join(
1097+
f" {k}={v!r}" for k, v in inputs.items()
1098+
)
1099+
signature = f"{func.__qualname__}(\n{args_lines},\n)"
1100+
else:
1101+
signature = f"{func.__qualname__}()"
1102+
1103+
print_call("_" * 80)
1104+
print_call(f"[call] {signature}")
1105+
return inputs
1106+
1107+
def after_call(
1108+
self,
1109+
outputs: Any,
1110+
) -> Any:
1111+
"""
1112+
Display end information.
1113+
"""
1114+
end = time.perf_counter()
1115+
duration = end - self._start
1116+
msg = f"{duration:.2f}s, {duration / 60:.2f}min"
1117+
print_call("_" * max(0, 80 - len(msg)) + msg)
1118+
return outputs

brainprep/interfaces/ants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
CommandLineWrapperHook,
1717
LogRuntimeHook,
1818
OutputdirHook,
19+
SignatureHook,
1920
step,
2021
)
2122
from ..typing import (
@@ -32,6 +33,7 @@
3233
bunched=False
3334
),
3435
CommandLineWrapperHook(),
36+
SignatureHook(),
3537
]
3638
)
3739
def biasfield(

brainprep/interfaces/cat12.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
LogRuntimeHook,
2828
OutputdirHook,
2929
PythonWrapperHook,
30+
SignatureHook,
3031
step,
3132
)
3233
from ..typing import (
@@ -50,6 +51,7 @@
5051
bunched=False
5152
),
5253
CommandLineWrapperHook(),
54+
SignatureHook(),
5355
]
5456
)
5557
def cat12vbm_wf(
@@ -95,7 +97,8 @@ def cat12vbm_wf(
9597
for im_file, trg_dir in zip(t1_files, output_dirs, strict=True)
9698
]
9799
qc_files = [
98-
trg_dir / "report" / f"catreport_{im_file.name.replace('.gz', '')}"
100+
trg_dir / "report" /
101+
f"catreport_{im_file.name.replace('nii.gz', 'pdf')}"
99102
for im_file, trg_dir in zip(t1_files, output_dirs, strict=True)
100103
]
101104

@@ -117,6 +120,7 @@ def cat12vbm_wf(
117120
bunched=False
118121
),
119122
PythonWrapperHook(),
123+
SignatureHook(),
120124
]
121125
)
122126
def write_catbatch(
@@ -224,6 +228,7 @@ def write_catbatch(
224228
bunched=False
225229
),
226230
PythonWrapperHook(),
231+
SignatureHook(),
227232
]
228233
)
229234
def cat12vbm_morphometry(

brainprep/interfaces/fmriprep.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
LogRuntimeHook,
3434
OutputdirHook,
3535
PythonWrapperHook,
36+
SignatureHook,
3637
step,
3738
)
3839
from ..typing import (
@@ -53,6 +54,7 @@
5354
bunched=False
5455
),
5556
CommandLineWrapperHook(),
57+
SignatureHook(),
5658
]
5759
)
5860
def fmriprep_wf(
@@ -225,6 +227,7 @@ def fmriprep_wf(
225227
bunched=False
226228
),
227229
PythonWrapperHook(),
230+
SignatureHook(),
228231
]
229232
)
230233
def func_vol_connectivity(

brainprep/interfaces/freesurfer.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
LogRuntimeHook,
2727
OutputdirHook,
2828
PythonWrapperHook,
29+
SignatureHook,
2930
step,
3031
)
3132
from ..typing import (
@@ -47,6 +48,7 @@
4748
bunched=False
4849
),
4950
CommandLineWrapperHook(),
51+
SignatureHook(),
5052
]
5153
)
5254
def brainmask(
@@ -105,6 +107,7 @@ def brainmask(
105107
bunched=False
106108
),
107109
CommandLineWrapperHook(),
110+
SignatureHook(),
108111
]
109112
)
110113
def reconall(
@@ -188,6 +191,7 @@ def reconall(
188191
bunched=False
189192
),
190193
CommandLineWrapperHook(),
194+
SignatureHook(),
191195
]
192196
)
193197
def reconall_longitudinal(
@@ -309,6 +313,7 @@ def reconall_longitudinal(
309313
bunched=False
310314
),
311315
PythonWrapperHook(),
316+
SignatureHook(),
312317
]
313318
)
314319
def freesurfer_command_status(
@@ -389,6 +394,7 @@ def freesurfer_command_status(
389394
bunched=False
390395
),
391396
CommandLineWrapperHook(),
397+
SignatureHook(),
392398
]
393399
)
394400
def localgi(
@@ -443,6 +449,7 @@ def localgi(
443449
bunched=False
444450
),
445451
CommandLineWrapperHook(),
452+
SignatureHook(),
446453
]
447454
)
448455
def surfreg(
@@ -519,6 +526,7 @@ def surfreg(
519526
bunched=False
520527
),
521528
CommandLineWrapperHook(),
529+
SignatureHook(),
522530
]
523531
)
524532
def xhemireg(
@@ -564,6 +572,7 @@ def xhemireg(
564572
LogRuntimeHook(
565573
bunched=False
566574
),
575+
SignatureHook(),
567576
]
568577
)
569578
def fsaveragesym_surfreg(
@@ -638,6 +647,7 @@ def fsaveragesym_surfreg(
638647
bunched=False
639648
),
640649
CommandLineWrapperHook(),
650+
SignatureHook(),
641651
]
642652
)
643653
def mris_apply_reg(
@@ -687,6 +697,7 @@ def mris_apply_reg(
687697
bunched=False
688698
),
689699
PythonWrapperHook(),
700+
SignatureHook(),
690701
]
691702
)
692703
def fsaveragesym_projection(
@@ -786,6 +797,7 @@ def fsaveragesym_projection(
786797
bunched=False
787798
),
788799
CommandLineWrapperHook(),
800+
SignatureHook(),
789801
]
790802
)
791803
def mri_convert(
@@ -831,6 +843,7 @@ def mri_convert(
831843
LogRuntimeHook(
832844
bunched=False
833845
),
846+
SignatureHook(),
834847
]
835848
)
836849
def mgz_to_nii(
@@ -895,6 +908,7 @@ def mgz_to_nii(
895908
bunched=False
896909
),
897910
CommandLineWrapperHook(),
911+
SignatureHook(),
898912
]
899913
)
900914
def aparcstats2table(
@@ -972,6 +986,7 @@ def aparcstats2table(
972986
bunched=False
973987
),
974988
CommandLineWrapperHook(),
989+
SignatureHook(),
975990
]
976991
)
977992
def asegstats2table(
@@ -1022,6 +1037,7 @@ def asegstats2table(
10221037
LogRuntimeHook(
10231038
bunched=False
10241039
),
1040+
SignatureHook(),
10251041
]
10261042
)
10271043
def freesurfer_features_summary(
@@ -1124,19 +1140,16 @@ def freesurfer_features_summary(
11241140
)
11251141
summary_files.append(volume_stat_file)
11261142

1127-
# sort by participant_id
1128-
output_files = output_dir.glob('*')
1129-
for file in output_files:
1130-
if file.suffix == ".csv":
1131-
sep = ','
1132-
elif file.suffix == ".tsv":
1133-
sep = '\t'
1134-
else:
1143+
output_files = output_dir.glob("*")
1144+
seps = {".csv": ",", ".tsv": "\t"}
1145+
for table_file in output_files:
1146+
if table_file.suffix not in seps:
11351147
continue
1136-
df = pd.read_csv(file, sep=sep)
1148+
sep = seps[table_file.suffix]
1149+
df = pd.read_csv(table_file, sep=sep)
11371150
first_col = df.columns[0]
11381151
df = df.sort_values(by=first_col)
1139-
df.to_csv(file, sep=sep, index=False)
1152+
df.to_csv(table_file, sep=sep, index=False)
11401153

11411154
return summary_files
11421155

@@ -1149,6 +1162,7 @@ def freesurfer_features_summary(
11491162
bunched=False
11501163
),
11511164
PythonWrapperHook(),
1165+
SignatureHook(),
11521166
]
11531167
)
11541168
def freesurfer_tissues(
@@ -1305,6 +1319,7 @@ def freesurfer_tissues(
13051319
bunched=False
13061320
),
13071321
CommandLineWrapperHook(),
1322+
SignatureHook(),
13081323
]
13091324
)
13101325
def nextbrain(

0 commit comments

Comments
 (0)