7
7
"""
8
8
9
9
import os
10
- import os .path as op
11
10
from pathlib import Path
12
11
import logging
13
12
import sys
@@ -56,10 +55,10 @@ def get_parser():
56
55
# Arguments as specified by BIDS-Apps
57
56
# required, positional arguments
58
57
# 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 ,
60
59
help = 'the root folder of a BIDS valid dataset (sub-XXXXX folders should '
61
60
'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 ,
63
62
help = 'the output path for the outcomes of preprocessing and visual '
64
63
'reports' )
65
64
parser .add_argument ('analysis_level' , choices = ['participant' ],
@@ -228,7 +227,7 @@ def get_parser():
228
227
' Use `--fs-no-reconall` instead.' )
229
228
230
229
g_other = parser .add_argument_group ('Other options' )
231
- g_other .add_argument ('-w' , '--work-dir' , action = 'store' ,
230
+ g_other .add_argument ('-w' , '--work-dir' , action = 'store' , type = Path , default = Path ( 'work' ),
232
231
help = 'path where intermediate results should be stored' )
233
232
g_other .add_argument (
234
233
'--resource-monitor' , action = 'store_true' , default = False ,
@@ -323,10 +322,10 @@ def before_send(event, hints):
323
322
if exec_env == 'fmriprep-docker' :
324
323
scope .set_tag ('docker_version' , os .getenv ('DOCKER_VERSION_8395080871' ))
325
324
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 ())
325
+ dset_desc_path = opts .bids_dir / 'dataset_description.json'
326
+ if dset_desc_path . exists ():
327
+ desc_content = dset_desc_path . read_bytes ()
328
+ scope .set_tag ('dset_desc_sha256' , hashlib .sha256 (desc_content ).hexdigest ())
330
329
331
330
free_mem_at_start = round (psutil .virtual_memory ().free / 1024 ** 3 , 1 )
332
331
scope .set_tag ('free_mem_at_start' , free_mem_at_start )
@@ -361,7 +360,7 @@ def before_send(event, hints):
361
360
if not opts .skip_bids_validation :
362
361
print ("Making sure the input data is BIDS compliant (warnings can be ignored in most "
363
362
"cases)." )
364
- validate_input_dir (exec_env , opts .bids_dir , opts .participant_label )
363
+ validate_input_dir (exec_env , str ( opts .bids_dir ) , opts .participant_label )
365
364
366
365
# FreeSurfer license
367
366
default_license = str (Path (os .getenv ('FREESURFER_HOME' )) / 'license.txt' )
@@ -394,39 +393,28 @@ def before_send(event, hints):
394
393
p .start ()
395
394
p .join ()
396
395
397
- if p .exitcode != 0 :
398
- sys .exit (p .exitcode )
399
-
400
- fmriprep_wf = retval ['workflow' ]
401
- plugin_settings = retval ['plugin_settings' ]
402
- bids_dir = retval ['bids_dir' ]
403
- output_dir = retval ['output_dir' ]
404
- work_dir = retval ['work_dir' ]
405
- subject_list = retval ['subject_list' ]
406
- run_uuid = retval ['run_uuid' ]
407
- if not opts .notrack :
408
- with sentry_sdk .configure_scope () as scope :
409
- scope .set_tag ('run_uuid' , run_uuid )
410
- scope .set_tag ('npart' , len (subject_list ))
411
-
412
- retcode = retval ['return_code' ]
396
+ retcode = p .exitcode or retval .get ('return_code' , 0 )
413
397
414
- if fmriprep_wf is None :
415
- sys .exit (1 )
416
-
417
- if opts .write_graph :
418
- fmriprep_wf .write_graph (graph2use = "colored" , format = 'svg' , simple_form = True )
398
+ bids_dir = retval .get ('bids_dir' )
399
+ output_dir = retval .get ('output_dir' )
400
+ work_dir = retval .get ('work_dir' )
401
+ plugin_settings = retval .get ('plugin_settings' , None )
402
+ subject_list = retval .get ('subject_list' , None )
403
+ fmriprep_wf = retval .get ('workflow' , None )
404
+ run_uuid = retval .get ('run_uuid' , None )
419
405
420
406
if opts .reports_only :
421
407
sys .exit (int (retcode > 0 ))
422
408
423
409
if opts .boilerplate :
424
410
sys .exit (int (retcode > 0 ))
425
411
426
- # Sentry tracking
427
- if not opts .notrack :
428
- sentry_sdk .add_breadcrumb (message = 'fMRIPrep started' , level = 'info' )
429
- sentry_sdk .capture_message ('fMRIPrep started' , level = 'info' )
412
+ if fmriprep_wf and opts .write_graph :
413
+ fmriprep_wf .write_graph (graph2use = "colored" , format = 'svg' , simple_form = True )
414
+
415
+ retcode = retcode or int (fmriprep_wf is None )
416
+ if retcode != 0 :
417
+ sys .exit (retcode )
430
418
431
419
# Check workflow for missing commands
432
420
missing = check_deps (fmriprep_wf )
@@ -435,9 +423,19 @@ def before_send(event, hints):
435
423
for iface , cmd in missing :
436
424
print ("\t {} (Interface: {})" .format (cmd , iface ))
437
425
sys .exit (2 )
438
-
439
426
# Clean up master process before running workflow, which may create forks
440
427
gc .collect ()
428
+
429
+ # Sentry tracking
430
+ if not opts .notrack :
431
+ with sentry_sdk .configure_scope () as scope :
432
+ if run_uuid :
433
+ scope .set_tag ('run_uuid' , run_uuid )
434
+ if subject_list :
435
+ scope .set_tag ('npart' , len (subject_list ))
436
+ sentry_sdk .add_breadcrumb (message = 'fMRIPrep started' , level = 'info' )
437
+ sentry_sdk .capture_message ('fMRIPrep started' , level = 'info' )
438
+
441
439
try :
442
440
fmriprep_wf .run (** plugin_settings )
443
441
except RuntimeError as e :
@@ -589,6 +587,24 @@ def build_workflow(opts, retval):
589
587
* Run identifier: {uuid}.
590
588
""" .format
591
589
590
+ bids_dir = opts .bids_dir .resolve ()
591
+ output_dir = opts .output_dir .resolve ()
592
+ work_dir = opts .work_dir
593
+
594
+ retval ['return_code' ] = 1
595
+ retval ['workflow' ] = None
596
+ retval ['bids_dir' ] = str (bids_dir )
597
+ retval ['output_dir' ] = str (output_dir )
598
+ retval ['work_dir' ] = str (work_dir )
599
+
600
+ if output_dir == bids_dir :
601
+ logger .error (
602
+ 'The selected output folder is the same as the input BIDS folder. '
603
+ 'Please modify the output path (suggestion: %s).' ,
604
+ bids_dir / 'derivatives' / ('fmriprep-%s' % __version__ .split ('+' )[0 ]))
605
+ retval ['return_code' ] = 1
606
+ return retval
607
+
592
608
# Reduce to unique space identifiers
593
609
output_spaces = sorted (set (opts .output_space ))
594
610
@@ -634,12 +650,13 @@ def build_workflow(opts, retval):
634
650
635
651
# Set up some instrumental utilities
636
652
run_uuid = '%s_%s' % (strftime ('%Y%m%d-%H%M%S' ), uuid .uuid4 ())
653
+ retval ['run_uuid' ] = run_uuid
637
654
638
655
# 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 )
656
+ layout = BIDSLayout (str (bids_dir ), validate = False )
641
657
subject_list = collect_participants (
642
658
layout , participant_label = opts .participant_label )
659
+ retval ['subject_list' ] = subject_list
643
660
644
661
# Load base plugin_settings from file if --use-plugin
645
662
if opts .use_plugin is not None :
@@ -679,28 +696,26 @@ def build_workflow(opts, retval):
679
696
logger .warning (
680
697
'Per-process threads (--omp-nthreads=%d) exceed total '
681
698
'threads (--nthreads/--n_cpus=%d)' , omp_nthreads , nthreads )
699
+ retval ['plugin_settings' ] = plugin_settings
682
700
683
701
# 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
687
-
702
+ log_dir = output_dir / 'fmriprep' / 'logs'
688
703
# 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 )
704
+ output_dir . mkdir ( exist_ok = True , parents = True )
705
+ log_dir . mkdir ( exist_ok = True , parents = True )
706
+ work_dir . mkdir ( exist_ok = True , parents = True )
692
707
693
708
# Nipype config (logs and execution)
694
709
ncfg .update_config ({
695
710
'logging' : {
696
- 'log_directory' : log_dir ,
711
+ 'log_directory' : str ( log_dir ) ,
697
712
'log_to_file' : True
698
713
},
699
714
'execution' : {
700
- 'crashdump_dir' : log_dir ,
715
+ 'crashdump_dir' : str ( log_dir ) ,
701
716
'crashfile_format' : 'txt' ,
702
717
'get_linked_libs' : False ,
703
- 'stop_on_first_crash' : opts .stop_on_first_crash or opts . work_dir is None ,
718
+ 'stop_on_first_crash' : opts .stop_on_first_crash ,
704
719
},
705
720
'monitoring' : {
706
721
'enabled' : opts .resource_monitor ,
@@ -712,21 +727,14 @@ def build_workflow(opts, retval):
712
727
if opts .resource_monitor :
713
728
ncfg .enable_resource_monitor ()
714
729
715
- retval ['return_code' ] = 0
716
- retval ['plugin_settings' ] = plugin_settings
717
- retval ['bids_dir' ] = bids_dir
718
- retval ['output_dir' ] = output_dir
719
- retval ['work_dir' ] = work_dir
720
- retval ['subject_list' ] = subject_list
721
- retval ['run_uuid' ] = run_uuid
722
- retval ['workflow' ] = None
723
-
724
730
# Called with reports only
725
731
if opts .reports_only :
726
732
logger .log (25 , 'Running --reports-only on participants %s' , ', ' .join (subject_list ))
727
733
if opts .run_uuid is not None :
728
734
run_uuid = opts .run_uuid
729
- retval ['return_code' ] = generate_reports (subject_list , output_dir , work_dir , run_uuid )
735
+ retval ['run_uuid' ] = run_uuid
736
+ retval ['return_code' ] = generate_reports (
737
+ subject_list , str (output_dir ), str (work_dir ), run_uuid )
730
738
return retval
731
739
732
740
# Build main workflow
@@ -761,8 +769,8 @@ def build_workflow(opts, retval):
761
769
omp_nthreads = omp_nthreads ,
762
770
skull_strip_template = opts .skull_strip_template ,
763
771
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 ) ,
766
774
freesurfer = opts .run_reconall ,
767
775
output_spaces = output_spaces ,
768
776
template = opts .template ,
0 commit comments