Skip to content

Commit 08ead73

Browse files
authored
Merge pull request #2165 from oesteban/fix/fs-license-canary
FIX: FreeSurfer license manipulation & canary
2 parents c4eed72 + 7674b04 commit 08ead73

File tree

4 files changed

+45
-17
lines changed

4 files changed

+45
-17
lines changed

fmriprep/cli/parser.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ def _path_exists(path, parser):
2323
raise parser.error(f"Path does not exist: <{path}>.")
2424
return Path(path).absolute()
2525

26+
def _is_file(path, parser):
27+
"""Ensure a given path exists and it is a file."""
28+
path = _path_exists(path, parser)
29+
if not path.is_file():
30+
raise parser.error(f"Path should point to a file (or symlink of file): <{path}>.")
31+
return path
32+
2633
def _min_one(value, parser):
2734
"""Ensure an argument is not lower than 1."""
2835
value = int(value)
@@ -63,6 +70,7 @@ def _bids_filter(value):
6370
formatter_class=ArgumentDefaultsHelpFormatter,
6471
)
6572
PathExists = partial(_path_exists, parser=parser)
73+
IsFile = partial(_is_file, parser=parser)
6674
PositiveInt = partial(_min_one, parser=parser)
6775

6876
# Arguments as specified by BIDS-Apps
@@ -128,7 +136,7 @@ def _bids_filter(value):
128136
dest="bids_filters",
129137
action="store",
130138
type=_bids_filter,
131-
metavar="PATH",
139+
metavar="FILE",
132140
help="a JSON file describing custom BIDS input filters using PyBIDS. "
133141
"For further details, please check out "
134142
"https://fmriprep.readthedocs.io/en/%s/faq.html#"
@@ -178,8 +186,10 @@ def _bids_filter(value):
178186
)
179187
g_perfm.add_argument(
180188
"--use-plugin",
189+
"--nipype-plugin-file",
181190
action="store",
182-
default=None,
191+
metavar="FILE",
192+
type=IsFile,
183193
help="nipype plugin configuration file",
184194
)
185195
g_perfm.add_argument(
@@ -414,8 +424,8 @@ def _bids_filter(value):
414424
g_fs = parser.add_argument_group("Specific options for FreeSurfer preprocessing")
415425
g_fs.add_argument(
416426
"--fs-license-file",
417-
metavar="PATH",
418-
type=PathExists,
427+
metavar="FILE",
428+
type=IsFile,
419429
help="Path to FreeSurfer license key file. Get it (for free) by registering"
420430
" at https://surfer.nmr.mgh.harvard.edu/registration.html",
421431
)
@@ -551,7 +561,6 @@ def parse_args(args=None, namespace=None):
551561
"""Parse args and run further checks on the command line."""
552562
import logging
553563
from niworkflows.utils.spaces import Reference, SpatialReferences
554-
from niworkflows.utils.misc import check_valid_fs_license
555564

556565
parser = _build_parser()
557566
opts = parser.parse_args(args, namespace)
@@ -567,15 +576,6 @@ def parse_args(args=None, namespace=None):
567576
# Retrieve logging level
568577
build_log = config.loggers.cli
569578

570-
if not check_valid_fs_license(lic=config.execution.fs_license_file):
571-
raise RuntimeError(
572-
"""\
573-
ERROR: a valid license file is required for FreeSurfer to run. fMRIPrep looked for an existing \
574-
license file at several paths, in this order: 1) command line argument ``--fs-license-file``; \
575-
2) ``$FS_LICENSE`` environment variable; and 3) the ``$FREESURFER_HOME/license.txt`` path. Get it \
576-
(for free) by registering at https://surfer.nmr.mgh.harvard.edu/registration.html"""
577-
)
578-
579579
# Load base plugin_settings from file if --use-plugin
580580
if opts.use_plugin is not None:
581581
import yaml

fmriprep/cli/workflow.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
def build_workflow(config_file, retval):
1414
"""Create the Nipype Workflow that supports the whole execution graph."""
1515
from niworkflows.utils.bids import collect_participants, check_pipeline_version
16+
from niworkflows.utils.misc import check_valid_fs_license
1617
from niworkflows.reports import generate_reports
1718
from .. import config
1819
from ..utils.misc import check_deps
@@ -82,6 +83,16 @@ def build_workflow(config_file, retval):
8283

8384
retval["workflow"] = init_fmriprep_wf()
8485

86+
# Check for FS license after building the workflow
87+
if not check_valid_fs_license():
88+
build_log.critical("""\
89+
ERROR: a valid license file is required for FreeSurfer to run. fMRIPrep looked for an existing \
90+
license file at several paths, in this order: 1) command line argument ``--fs-license-file``; \
91+
2) ``$FS_LICENSE`` environment variable; and 3) the ``$FREESURFER_HOME/license.txt`` path. Get it \
92+
(for free) by registering at https://surfer.nmr.mgh.harvard.edu/registration.html""")
93+
retval["return_code"] = 126 # 126 == Command invoked cannot execute.
94+
return retval
95+
8596
# Check workflow for missing commands
8697
missing = check_deps(retval["workflow"])
8798
if missing:

fmriprep/config.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,11 @@
122122
del _cgroup
123123

124124
_fs_license = os.getenv('FS_LICENSE')
125-
if _fs_license is None and os.getenv('FREESURFER_HOME'):
126-
_fs_license = os.path.join(os.getenv('FREESURFER_HOME'), 'license.txt')
125+
if not _fs_license and os.getenv('FREESURFER_HOME'):
126+
_fs_home = os.getenv('FREESURFER_HOME')
127+
if _fs_home and (Path(_fs_home) / "license.txt").is_file():
128+
_fs_license = str(Path(_fs_home) / "license.txt")
129+
del _fs_home
127130

128131
_templateflow_home = Path(os.getenv(
129132
'TEMPLATEFLOW_HOME',
@@ -377,6 +380,9 @@ class execution(_Config):
377380
@classmethod
378381
def init(cls):
379382
"""Create a new BIDS Layout accessible with :attr:`~execution.layout`."""
383+
if cls.fs_license_file and Path(cls.fs_license_file).is_file():
384+
os.environ["FS_LICENSE"] = str(cls.fs_license_file)
385+
380386
if cls._layout is None:
381387
import re
382388
from bids.layout import BIDSLayout

wrapper/fmriprep_docker.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ def is_in_directory(filepath, directory):
225225
def get_parser():
226226
"""Defines the command line interface of the wrapper"""
227227
import argparse
228+
from functools import partial
228229

229230
class ToDict(argparse.Action):
230231
def __call__(self, parser, namespace, values, option_string=None):
@@ -234,11 +235,21 @@ def __call__(self, parser, namespace, values, option_string=None):
234235
d[k] = os.path.abspath(v)
235236
setattr(namespace, self.dest, d)
236237

238+
def _is_file(path, parser):
239+
"""Ensure a given path exists and it is a file."""
240+
if not os.path.isfile(path):
241+
raise parser.error(
242+
"Path should point to a file (or symlink of file): <%s>." % path
243+
)
244+
return path
245+
237246
parser = argparse.ArgumentParser(
238247
description=__doc__,
239248
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
240249
add_help=False)
241250

251+
IsFile = partial(_is_file, parser=parser)
252+
242253
# Standard FMRIPREP arguments
243254
parser.add_argument('bids_dir', nargs='?', type=os.path.abspath,
244255
default='')
@@ -279,7 +290,7 @@ def __call__(self, parser, namespace, values, option_string=None):
279290
', '.join(NONSTANDARD_REFERENCES)))
280291

281292
g_wrap.add_argument(
282-
'--fs-license-file', metavar='PATH', type=os.path.abspath,
293+
'--fs-license-file', metavar='PATH', type=IsFile,
283294
default=os.getenv('FS_LICENSE', None),
284295
help='Path to FreeSurfer license key file. Get it (for free) by registering'
285296
' at https://surfer.nmr.mgh.harvard.edu/registration.html')

0 commit comments

Comments
 (0)