Skip to content

Commit 42be86d

Browse files
committed
fix(cli): do not allow writing outputs directly at the BIDS root. close #1571
1 parent a3e185b commit 42be86d

File tree

1 file changed

+33
-25
lines changed

1 file changed

+33
-25
lines changed

fmriprep/cli/run.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ def get_parser():
5656
# Arguments as specified by BIDS-Apps
5757
# required, positional arguments
5858
# IMPORTANT: they must go directly with the parser object
59-
parser.add_argument('bids_dir', action='store',
59+
parser.add_argument('bids_dir', action='store', type=Path,
6060
help='the root folder of a BIDS valid dataset (sub-XXXXX folders should '
6161
'be found at the top level in this folder).')
62-
parser.add_argument('output_dir', action='store',
62+
parser.add_argument('output_dir', action='store', type=Path,
6363
help='the output path for the outcomes of preprocessing and visual '
6464
'reports')
6565
parser.add_argument('analysis_level', choices=['participant'],
@@ -228,7 +228,7 @@ def get_parser():
228228
' Use `--fs-no-reconall` instead.')
229229

230230
g_other = parser.add_argument_group('Other options')
231-
g_other.add_argument('-w', '--work-dir', action='store',
231+
g_other.add_argument('-w', '--work-dir', action='store', type=Path, default=Path('work'),
232232
help='path where intermediate results should be stored')
233233
g_other.add_argument(
234234
'--resource-monitor', action='store_true', default=False,
@@ -323,10 +323,10 @@ def before_send(event, hints):
323323
if exec_env == 'fmriprep-docker':
324324
scope.set_tag('docker_version', os.getenv('DOCKER_VERSION_8395080871'))
325325

326-
dset_desc_path = os.path.join(opts.bids_dir, 'dataset_description.json')
327-
if os.path.exists(dset_desc_path):
328-
with open(dset_desc_path, 'rb') as fp:
329-
scope.set_tag('dset_desc_sha256', hashlib.sha256(fp.read()).hexdigest())
326+
dset_desc_path = opts.bids_dir / 'dataset_description.json'
327+
if dset_desc_path.exists():
328+
desc_content = dset_desc_path.read_bytes()
329+
scope.set_tag('dset_desc_sha256', hashlib.sha256(desc_content).hexdigest())
330330

331331
free_mem_at_start = round(psutil.virtual_memory().free / 1024**3, 1)
332332
scope.set_tag('free_mem_at_start', free_mem_at_start)
@@ -361,7 +361,7 @@ def before_send(event, hints):
361361
if not opts.skip_bids_validation:
362362
print("Making sure the input data is BIDS compliant (warnings can be ignored in most "
363363
"cases).")
364-
validate_input_dir(exec_env, opts.bids_dir, opts.participant_label)
364+
validate_input_dir(exec_env, str(opts.bids_dir), opts.participant_label)
365365

366366
# FreeSurfer license
367367
default_license = str(Path(os.getenv('FREESURFER_HOME')) / 'license.txt')
@@ -636,8 +636,8 @@ def build_workflow(opts, retval):
636636
run_uuid = '%s_%s' % (strftime('%Y%m%d-%H%M%S'), uuid.uuid4())
637637

638638
# First check that bids_dir looks like a BIDS folder
639-
bids_dir = os.path.abspath(opts.bids_dir)
640-
layout = BIDSLayout(bids_dir, validate=False)
639+
bids_dir = opts.bids_dir.resolve()
640+
layout = BIDSLayout(str(bids_dir), validate=False)
641641
subject_list = collect_participants(
642642
layout, participant_label=opts.participant_label)
643643

@@ -681,26 +681,33 @@ def build_workflow(opts, retval):
681681
'threads (--nthreads/--n_cpus=%d)', omp_nthreads, nthreads)
682682

683683
# Set up directories
684-
output_dir = op.abspath(opts.output_dir)
685-
log_dir = op.join(output_dir, 'fmriprep', 'logs')
686-
work_dir = op.abspath(opts.work_dir or 'work') # Set work/ as default
684+
output_dir = opts.output_dir.resolve()
685+
if output_dir == bids_dir:
686+
output_dir = bids_dir / 'derivatives' / ('fmriprep-%s' % __version__.split('+')[0])
687+
logger.warning(
688+
'The selected output folder is the same as the input BIDS folder. '
689+
'Cowardly redirecting outputs to %s', output_dir
690+
)
691+
692+
log_dir = output_dir / 'fmriprep' / 'logs'
693+
work_dir = opts.work_dir
687694

688695
# Check and create output and working directories
689-
os.makedirs(output_dir, exist_ok=True)
690-
os.makedirs(log_dir, exist_ok=True)
691-
os.makedirs(work_dir, exist_ok=True)
696+
output_dir.mkdir(exist_ok=True, parents=True)
697+
log_dir.mkdir(exist_ok=True, parents=True)
698+
work_dir.mkdir(exist_ok=True, parents=True)
692699

693700
# Nipype config (logs and execution)
694701
ncfg.update_config({
695702
'logging': {
696-
'log_directory': log_dir,
703+
'log_directory': str(log_dir),
697704
'log_to_file': True
698705
},
699706
'execution': {
700-
'crashdump_dir': log_dir,
707+
'crashdump_dir': str(log_dir),
701708
'crashfile_format': 'txt',
702709
'get_linked_libs': False,
703-
'stop_on_first_crash': opts.stop_on_first_crash or opts.work_dir is None,
710+
'stop_on_first_crash': opts.stop_on_first_crash,
704711
},
705712
'monitoring': {
706713
'enabled': opts.resource_monitor,
@@ -714,9 +721,9 @@ def build_workflow(opts, retval):
714721

715722
retval['return_code'] = 0
716723
retval['plugin_settings'] = plugin_settings
717-
retval['bids_dir'] = bids_dir
718-
retval['output_dir'] = output_dir
719-
retval['work_dir'] = work_dir
724+
retval['bids_dir'] = str(bids_dir)
725+
retval['output_dir'] = str(output_dir)
726+
retval['work_dir'] = str(work_dir)
720727
retval['subject_list'] = subject_list
721728
retval['run_uuid'] = run_uuid
722729
retval['workflow'] = None
@@ -726,7 +733,8 @@ def build_workflow(opts, retval):
726733
logger.log(25, 'Running --reports-only on participants %s', ', '.join(subject_list))
727734
if opts.run_uuid is not None:
728735
run_uuid = opts.run_uuid
729-
retval['return_code'] = generate_reports(subject_list, output_dir, work_dir, run_uuid)
736+
retval['return_code'] = generate_reports(
737+
subject_list, str(output_dir), str(work_dir), run_uuid)
730738
return retval
731739

732740
# Build main workflow
@@ -761,8 +769,8 @@ def build_workflow(opts, retval):
761769
omp_nthreads=omp_nthreads,
762770
skull_strip_template=opts.skull_strip_template,
763771
skull_strip_fixed_seed=opts.skull_strip_fixed_seed,
764-
work_dir=work_dir,
765-
output_dir=output_dir,
772+
work_dir=str(work_dir),
773+
output_dir=str(output_dir),
766774
freesurfer=opts.run_reconall,
767775
output_spaces=output_spaces,
768776
template=opts.template,

0 commit comments

Comments
 (0)