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' ],
@@ -244,7 +243,7 @@ def get_parser():
244
243
' Use `--fs-no-reconall` instead.' )
245
244
246
245
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' ),
248
247
help = 'path where intermediate results should be stored' )
249
248
g_other .add_argument (
250
249
'--resource-monitor' , action = 'store_true' , default = False ,
@@ -339,10 +338,10 @@ def before_send(event, hints):
339
338
if exec_env == 'fmriprep-docker' :
340
339
scope .set_tag ('docker_version' , os .getenv ('DOCKER_VERSION_8395080871' ))
341
340
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 ())
346
345
347
346
free_mem_at_start = round (psutil .virtual_memory ().free / 1024 ** 3 , 1 )
348
347
scope .set_tag ('free_mem_at_start' , free_mem_at_start )
@@ -377,7 +376,7 @@ def before_send(event, hints):
377
376
if not opts .skip_bids_validation :
378
377
print ("Making sure the input data is BIDS compliant (warnings can be ignored in most "
379
378
"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 )
381
380
382
381
# FreeSurfer license
383
382
default_license = str (Path (os .getenv ('FREESURFER_HOME' )) / 'license.txt' )
@@ -410,39 +409,28 @@ def before_send(event, hints):
410
409
p .start ()
411
410
p .join ()
412
411
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 )
429
413
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 )
435
421
436
422
if opts .reports_only :
437
423
sys .exit (int (retcode > 0 ))
438
424
439
425
if opts .boilerplate :
440
426
sys .exit (int (retcode > 0 ))
441
427
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 )
446
434
447
435
# Check workflow for missing commands
448
436
missing = check_deps (fmriprep_wf )
@@ -451,9 +439,19 @@ def before_send(event, hints):
451
439
for iface , cmd in missing :
452
440
print ("\t {} (Interface: {})" .format (cmd , iface ))
453
441
sys .exit (2 )
454
-
455
442
# Clean up master process before running workflow, which may create forks
456
443
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
+
457
455
try :
458
456
fmriprep_wf .run (** plugin_settings )
459
457
except RuntimeError as e :
@@ -605,6 +603,24 @@ def build_workflow(opts, retval):
605
603
* Run identifier: {uuid}.
606
604
""" .format
607
605
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
+
608
624
# Reduce to unique space identifiers
609
625
output_spaces = sorted (set (opts .output_space ))
610
626
@@ -650,12 +666,13 @@ def build_workflow(opts, retval):
650
666
651
667
# Set up some instrumental utilities
652
668
run_uuid = '%s_%s' % (strftime ('%Y%m%d-%H%M%S' ), uuid .uuid4 ())
669
+ retval ['run_uuid' ] = run_uuid
653
670
654
671
# 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 )
657
673
subject_list = collect_participants (
658
674
layout , participant_label = opts .participant_label )
675
+ retval ['subject_list' ] = subject_list
659
676
660
677
# Load base plugin_settings from file if --use-plugin
661
678
if opts .use_plugin is not None :
@@ -695,28 +712,26 @@ def build_workflow(opts, retval):
695
712
logger .warning (
696
713
'Per-process threads (--omp-nthreads=%d) exceed total '
697
714
'threads (--nthreads/--n_cpus=%d)' , omp_nthreads , nthreads )
715
+ retval ['plugin_settings' ] = plugin_settings
698
716
699
717
# 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'
704
719
# 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 )
708
723
709
724
# Nipype config (logs and execution)
710
725
ncfg .update_config ({
711
726
'logging' : {
712
- 'log_directory' : log_dir ,
727
+ 'log_directory' : str ( log_dir ) ,
713
728
'log_to_file' : True
714
729
},
715
730
'execution' : {
716
- 'crashdump_dir' : log_dir ,
731
+ 'crashdump_dir' : str ( log_dir ) ,
717
732
'crashfile_format' : 'txt' ,
718
733
'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 ,
720
735
},
721
736
'monitoring' : {
722
737
'enabled' : opts .resource_monitor ,
@@ -728,21 +743,14 @@ def build_workflow(opts, retval):
728
743
if opts .resource_monitor :
729
744
ncfg .enable_resource_monitor ()
730
745
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
-
740
746
# Called with reports only
741
747
if opts .reports_only :
742
748
logger .log (25 , 'Running --reports-only on participants %s' , ', ' .join (subject_list ))
743
749
if opts .run_uuid is not None :
744
750
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 )
746
754
return retval
747
755
748
756
# Build main workflow
@@ -777,8 +785,8 @@ def build_workflow(opts, retval):
777
785
omp_nthreads = omp_nthreads ,
778
786
skull_strip_template = opts .skull_strip_template ,
779
787
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 ) ,
782
790
freesurfer = opts .run_reconall ,
783
791
output_spaces = output_spaces ,
784
792
template = opts .template ,
0 commit comments