Skip to content

Commit 59ee4b6

Browse files
committed
Merge remote-tracking branch 'upstream/master' into pr/1487
2 parents 44708bc + 76bbfc5 commit 59ee4b6

File tree

2 files changed

+68
-60
lines changed

2 files changed

+68
-60
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ RUN apt-get install -y nodejs
109109
RUN npm install -g svgo
110110

111111
# Installing bids-validator
112-
RUN npm install -g bids-validator@1.1.3
112+
RUN npm install -g bids-validator@1.2.3
113113

114114
# Installing and setting up ICA_AROMA
115115
RUN mkdir -p /opt/ICA-AROMA && \

fmriprep/cli/run.py

Lines changed: 67 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"""
88

99
import os
10-
import os.path as op
1110
from pathlib import Path
1211
import logging
1312
import sys
@@ -56,10 +55,10 @@ def get_parser():
5655
# Arguments as specified by BIDS-Apps
5756
# required, positional arguments
5857
# IMPORTANT: they must go directly with the parser object
59-
parser.add_argument('bids_dir', action='store',
58+
parser.add_argument('bids_dir', action='store', type=Path,
6059
help='the root folder of a BIDS valid dataset (sub-XXXXX folders should '
6160
'be found at the top level in this folder).')
62-
parser.add_argument('output_dir', action='store',
61+
parser.add_argument('output_dir', action='store', type=Path,
6362
help='the output path for the outcomes of preprocessing and visual '
6463
'reports')
6564
parser.add_argument('analysis_level', choices=['participant'],
@@ -244,7 +243,7 @@ def get_parser():
244243
' Use `--fs-no-reconall` instead.')
245244

246245
g_other = parser.add_argument_group('Other options')
247-
g_other.add_argument('-w', '--work-dir', action='store',
246+
g_other.add_argument('-w', '--work-dir', action='store', type=Path, default=Path('work'),
248247
help='path where intermediate results should be stored')
249248
g_other.add_argument(
250249
'--resource-monitor', action='store_true', default=False,
@@ -339,10 +338,10 @@ def before_send(event, hints):
339338
if exec_env == 'fmriprep-docker':
340339
scope.set_tag('docker_version', os.getenv('DOCKER_VERSION_8395080871'))
341340

342-
dset_desc_path = os.path.join(opts.bids_dir, 'dataset_description.json')
343-
if os.path.exists(dset_desc_path):
344-
with open(dset_desc_path, 'rb') as fp:
345-
scope.set_tag('dset_desc_sha256', hashlib.sha256(fp.read()).hexdigest())
341+
dset_desc_path = opts.bids_dir / 'dataset_description.json'
342+
if dset_desc_path.exists():
343+
desc_content = dset_desc_path.read_bytes()
344+
scope.set_tag('dset_desc_sha256', hashlib.sha256(desc_content).hexdigest())
346345

347346
free_mem_at_start = round(psutil.virtual_memory().free / 1024**3, 1)
348347
scope.set_tag('free_mem_at_start', free_mem_at_start)
@@ -377,7 +376,7 @@ def before_send(event, hints):
377376
if not opts.skip_bids_validation:
378377
print("Making sure the input data is BIDS compliant (warnings can be ignored in most "
379378
"cases).")
380-
validate_input_dir(exec_env, opts.bids_dir, opts.participant_label)
379+
validate_input_dir(exec_env, str(opts.bids_dir), opts.participant_label)
381380

382381
# FreeSurfer license
383382
default_license = str(Path(os.getenv('FREESURFER_HOME')) / 'license.txt')
@@ -410,39 +409,28 @@ def before_send(event, hints):
410409
p.start()
411410
p.join()
412411

413-
if p.exitcode != 0:
414-
sys.exit(p.exitcode)
415-
416-
fmriprep_wf = retval['workflow']
417-
plugin_settings = retval['plugin_settings']
418-
bids_dir = retval['bids_dir']
419-
output_dir = retval['output_dir']
420-
work_dir = retval['work_dir']
421-
subject_list = retval['subject_list']
422-
run_uuid = retval['run_uuid']
423-
if not opts.notrack:
424-
with sentry_sdk.configure_scope() as scope:
425-
scope.set_tag('run_uuid', run_uuid)
426-
scope.set_tag('npart', len(subject_list))
427-
428-
retcode = retval['return_code']
412+
retcode = p.exitcode or retval.get('return_code', 0)
429413

430-
if fmriprep_wf is None:
431-
sys.exit(1)
432-
433-
if opts.write_graph:
434-
fmriprep_wf.write_graph(graph2use="colored", format='svg', simple_form=True)
414+
bids_dir = retval.get('bids_dir')
415+
output_dir = retval.get('output_dir')
416+
work_dir = retval.get('work_dir')
417+
plugin_settings = retval.get('plugin_settings', None)
418+
subject_list = retval.get('subject_list', None)
419+
fmriprep_wf = retval.get('workflow', None)
420+
run_uuid = retval.get('run_uuid', None)
435421

436422
if opts.reports_only:
437423
sys.exit(int(retcode > 0))
438424

439425
if opts.boilerplate:
440426
sys.exit(int(retcode > 0))
441427

442-
# Sentry tracking
443-
if not opts.notrack:
444-
sentry_sdk.add_breadcrumb(message='fMRIPrep started', level='info')
445-
sentry_sdk.capture_message('fMRIPrep started', level='info')
428+
if fmriprep_wf and opts.write_graph:
429+
fmriprep_wf.write_graph(graph2use="colored", format='svg', simple_form=True)
430+
431+
retcode = retcode or int(fmriprep_wf is None)
432+
if retcode != 0:
433+
sys.exit(retcode)
446434

447435
# Check workflow for missing commands
448436
missing = check_deps(fmriprep_wf)
@@ -451,9 +439,19 @@ def before_send(event, hints):
451439
for iface, cmd in missing:
452440
print("\t{} (Interface: {})".format(cmd, iface))
453441
sys.exit(2)
454-
455442
# Clean up master process before running workflow, which may create forks
456443
gc.collect()
444+
445+
# Sentry tracking
446+
if not opts.notrack:
447+
with sentry_sdk.configure_scope() as scope:
448+
if run_uuid:
449+
scope.set_tag('run_uuid', run_uuid)
450+
if subject_list:
451+
scope.set_tag('npart', len(subject_list))
452+
sentry_sdk.add_breadcrumb(message='fMRIPrep started', level='info')
453+
sentry_sdk.capture_message('fMRIPrep started', level='info')
454+
457455
try:
458456
fmriprep_wf.run(**plugin_settings)
459457
except RuntimeError as e:
@@ -605,6 +603,24 @@ def build_workflow(opts, retval):
605603
* Run identifier: {uuid}.
606604
""".format
607605

606+
bids_dir = opts.bids_dir.resolve()
607+
output_dir = opts.output_dir.resolve()
608+
work_dir = opts.work_dir
609+
610+
retval['return_code'] = 1
611+
retval['workflow'] = None
612+
retval['bids_dir'] = str(bids_dir)
613+
retval['output_dir'] = str(output_dir)
614+
retval['work_dir'] = str(work_dir)
615+
616+
if output_dir == bids_dir:
617+
logger.error(
618+
'The selected output folder is the same as the input BIDS folder. '
619+
'Please modify the output path (suggestion: %s).',
620+
bids_dir / 'derivatives' / ('fmriprep-%s' % __version__.split('+')[0]))
621+
retval['return_code'] = 1
622+
return retval
623+
608624
# Reduce to unique space identifiers
609625
output_spaces = sorted(set(opts.output_space))
610626

@@ -650,12 +666,13 @@ def build_workflow(opts, retval):
650666

651667
# Set up some instrumental utilities
652668
run_uuid = '%s_%s' % (strftime('%Y%m%d-%H%M%S'), uuid.uuid4())
669+
retval['run_uuid'] = run_uuid
653670

654671
# First check that bids_dir looks like a BIDS folder
655-
bids_dir = os.path.abspath(opts.bids_dir)
656-
layout = BIDSLayout(bids_dir, validate=False)
672+
layout = BIDSLayout(str(bids_dir), validate=False)
657673
subject_list = collect_participants(
658674
layout, participant_label=opts.participant_label)
675+
retval['subject_list'] = subject_list
659676

660677
# Load base plugin_settings from file if --use-plugin
661678
if opts.use_plugin is not None:
@@ -695,28 +712,26 @@ def build_workflow(opts, retval):
695712
logger.warning(
696713
'Per-process threads (--omp-nthreads=%d) exceed total '
697714
'threads (--nthreads/--n_cpus=%d)', omp_nthreads, nthreads)
715+
retval['plugin_settings'] = plugin_settings
698716

699717
# Set up directories
700-
output_dir = op.abspath(opts.output_dir)
701-
log_dir = op.join(output_dir, 'fmriprep', 'logs')
702-
work_dir = op.abspath(opts.work_dir or 'work') # Set work/ as default
703-
718+
log_dir = output_dir / 'fmriprep' / 'logs'
704719
# Check and create output and working directories
705-
os.makedirs(output_dir, exist_ok=True)
706-
os.makedirs(log_dir, exist_ok=True)
707-
os.makedirs(work_dir, exist_ok=True)
720+
output_dir.mkdir(exist_ok=True, parents=True)
721+
log_dir.mkdir(exist_ok=True, parents=True)
722+
work_dir.mkdir(exist_ok=True, parents=True)
708723

709724
# Nipype config (logs and execution)
710725
ncfg.update_config({
711726
'logging': {
712-
'log_directory': log_dir,
727+
'log_directory': str(log_dir),
713728
'log_to_file': True
714729
},
715730
'execution': {
716-
'crashdump_dir': log_dir,
731+
'crashdump_dir': str(log_dir),
717732
'crashfile_format': 'txt',
718733
'get_linked_libs': False,
719-
'stop_on_first_crash': opts.stop_on_first_crash or opts.work_dir is None,
734+
'stop_on_first_crash': opts.stop_on_first_crash,
720735
},
721736
'monitoring': {
722737
'enabled': opts.resource_monitor,
@@ -728,21 +743,14 @@ def build_workflow(opts, retval):
728743
if opts.resource_monitor:
729744
ncfg.enable_resource_monitor()
730745

731-
retval['return_code'] = 0
732-
retval['plugin_settings'] = plugin_settings
733-
retval['bids_dir'] = bids_dir
734-
retval['output_dir'] = output_dir
735-
retval['work_dir'] = work_dir
736-
retval['subject_list'] = subject_list
737-
retval['run_uuid'] = run_uuid
738-
retval['workflow'] = None
739-
740746
# Called with reports only
741747
if opts.reports_only:
742748
logger.log(25, 'Running --reports-only on participants %s', ', '.join(subject_list))
743749
if opts.run_uuid is not None:
744750
run_uuid = opts.run_uuid
745-
retval['return_code'] = generate_reports(subject_list, output_dir, work_dir, run_uuid)
751+
retval['run_uuid'] = run_uuid
752+
retval['return_code'] = generate_reports(
753+
subject_list, str(output_dir), str(work_dir), run_uuid)
746754
return retval
747755

748756
# Build main workflow
@@ -777,8 +785,8 @@ def build_workflow(opts, retval):
777785
omp_nthreads=omp_nthreads,
778786
skull_strip_template=opts.skull_strip_template,
779787
skull_strip_fixed_seed=opts.skull_strip_fixed_seed,
780-
work_dir=work_dir,
781-
output_dir=output_dir,
788+
work_dir=str(work_dir),
789+
output_dir=str(output_dir),
782790
freesurfer=opts.run_reconall,
783791
output_spaces=output_spaces,
784792
template=opts.template,

0 commit comments

Comments
 (0)