Commit 2db84cc0 authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

RF: Various bug fixes; adjust configure_* functions to make them more testable

parent e3fbcf8a
...@@ -94,7 +94,7 @@ class Version(object): ...@@ -94,7 +94,7 @@ class Version(object):
# change very infrequently for various # change very infrequently for various
# reasons, so we accept a fourth "hotfix" # reasons, so we accept a fourth "hotfix"
# number. # number.
self.components = map(int, verstr.split('.')[:4]) self.components = list(map(int, verstr.split('.')[:4]))
self.verstr = verstr self.verstr = verstr
def __str__(self): def __str__(self):
...@@ -110,7 +110,7 @@ class Version(object): ...@@ -110,7 +110,7 @@ class Version(object):
for p1, p2 in zip(self.components, other.components): for p1, p2 in zip(self.components, other.components):
if p1 < p2: return True if p1 < p2: return True
if p1 > p2: return False if p1 > p2: return False
return False return len(self.components) < len(other.components)
class Context(object): class Context(object):
...@@ -127,7 +127,7 @@ class Context(object): ...@@ -127,7 +127,7 @@ class Context(object):
""" """
self.args = args self.args = args
self.shell = op.basename(os.environ['SHELL']) self.shell = op.basename(os.environ['SHELL']).lower()
# These attributes are updated on-demand via # These attributes are updated on-demand via
# the property accessors defined below, or all # the property accessors defined below, or all
...@@ -367,7 +367,7 @@ class Context(object): ...@@ -367,7 +367,7 @@ class Context(object):
platforms = { platforms = {
('linux', 'x86_64') : 'linux-64', ('linux', 'x86_64') : 'linux-64',
('darwin', 'x86_64') : 'linux-64', ('darwin', 'x86_64') : 'macos-64',
# M1 builds (and possbily ARM for Linux) # M1 builds (and possbily ARM for Linux)
# will be added in the future # will be added in the future
...@@ -382,7 +382,7 @@ class Context(object): ...@@ -382,7 +382,7 @@ class Context(object):
supported = ', '.join(['[{}, {}]' for s, c in platforms]) supported = ', '.join(['[{}, {}]' for s, c in platforms])
raise Exception('This platform [{}, {}] is unrecognised or ' raise Exception('This platform [{}, {}] is unrecognised or '
'unsupported! Supported platforms: {}'.format( 'unsupported! Supported platforms: {}'.format(
system, cpu, spported)) system, cpu, supported))
return platforms[key] return platforms[key]
...@@ -427,7 +427,8 @@ class Context(object): ...@@ -427,7 +427,8 @@ class Context(object):
"""Returns True if dirname needs administrator privileges to write to, """Returns True if dirname needs administrator privileges to write to,
False otherwise. False otherwise.
""" """
# TODO os.supports_effective_ids added in python 3.3 # os.supports_effective_ids added in
# python 3.3, so can't be used here
return not os.access(dirname, os.W_OK | os.X_OK) return not os.access(dirname, os.W_OK | os.X_OK)
...@@ -436,7 +437,7 @@ class Context(object): ...@@ -436,7 +437,7 @@ class Context(object):
"""Prompt the user for their administrator password.""" """Prompt the user for their administrator password."""
def validate_admin_password(password): def validate_admin_password(password):
proc = sudo_popen(['true'], password) proc = Process.sudo_popen(['true'], password)
proc.communicate() proc.communicate()
return proc.returncode == 0 return proc.returncode == 0
...@@ -734,18 +735,6 @@ def tempdir(override_dir=None): ...@@ -734,18 +735,6 @@ def tempdir(override_dir=None):
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
def memoize(f):
"""Decorator to memoize a function. """
cache = f.cache = {}
def g(*args, **kwargs):
key = (f, tuple(args), frozenset(kwargs.items()))
if key not in cache:
cache[key] = f(*args, **kwargs)
return cache[key]
return g
def sha256(filename, check_against=None, blocksize=1048576): def sha256(filename, check_against=None, blocksize=1048576):
"""Calculate the SHA256 checksum of the given file. If check_against """Calculate the SHA256 checksum of the given file. If check_against
is provided, it is compared against the calculated checksum, and an is provided, it is compared against the calculated checksum, and an
...@@ -771,12 +760,6 @@ def sha256(filename, check_against=None, blocksize=1048576): ...@@ -771,12 +760,6 @@ def sha256(filename, check_against=None, blocksize=1048576):
return checksum return checksum
class DownloadFailed(Exception):
"""Exception type raised by the download_file function if a
download fails for some reason.
"""
def download_file(url, destination, progress=None, blocksize=131072): def download_file(url, destination, progress=None, blocksize=131072):
"""Download a file from url, saving it to destination. """ """Download a file from url, saving it to destination. """
...@@ -1227,8 +1210,14 @@ def patch_file(filename, searchline, numlines, content): ...@@ -1227,8 +1210,14 @@ def patch_file(filename, searchline, numlines, content):
f.write('\n'.join(lines)) f.write('\n'.join(lines))
def configure_shell(ctx): def configure_shell(shell, homedir, fsldir):
"""Configures the user's shell environment (e.g. ~/.bash_profile). """ """Configures the user's shell environment (e.g. ~/.bash_profile).
:arg shell: User's shell (taken from the $SHELL environment variable
:arg homedir: User's home directory, presumed to contain shell profile
file(s).
:arg fsldir: FSL installation directory
"""
bourne_shells = ['sh', 'bash', 'zsh', 'dash'] bourne_shells = ['sh', 'bash', 'zsh', 'dash']
csh_shells = ['csh', 'tcsh'] csh_shells = ['csh', 'tcsh']
...@@ -1242,7 +1231,7 @@ def configure_shell(ctx): ...@@ -1242,7 +1231,7 @@ def configure_shell(ctx):
'csh' : ['.cshrc'], 'csh' : ['.cshrc'],
'tcsh' : ['.tcshrc']} 'tcsh' : ['.tcshrc']}
# Do not change the format of these configurations - # DO NOT CHANGE the format of these configurations -
# they are kept exactly as-is for compatibility with # they are kept exactly as-is for compatibility with
# legacy FSL installations, i.e. so we can modify # legacy FSL installations, i.e. so we can modify
# profiles with an existing configuration from older # profiles with an existing configuration from older
...@@ -1253,27 +1242,27 @@ def configure_shell(ctx): ...@@ -1253,27 +1242,27 @@ def configure_shell(ctx):
PATH=${{FSLDIR}}/share/fsl/bin:${{PATH}} PATH=${{FSLDIR}}/share/fsl/bin:${{PATH}}
export FSLDIR PATH export FSLDIR PATH
. ${{FSLDIR}}/etc/fslconf/fsl.sh . ${{FSLDIR}}/etc/fslconf/fsl.sh
""").format(fsldir=ctx.destdir) """).format(fsldir=fsldir).strip()
csh_cfg = tw.dedent(""" csh_cfg = tw.dedent("""
# FSL Setup # FSL Setup
setenv FSLDIR {fsldir} setenv FSLDIR {fsldir}
setenv PATH ${{FSLDIR}}/share/fsl/bin:${{PATH}} setenv PATH ${{FSLDIR}}/share/fsl/bin:${{PATH}}
source ${{FSLDIR}}/etc/fslconf/fsl.csh source ${{FSLDIR}}/etc/fslconf/fsl.csh
""").format(fsldir=ctx.destdir) """).format(fsldir=fsldir).strip()
if ctx.shell not in bourne_shells + csh_shells: if shell not in bourne_shells + csh_shells:
printmsg('Shell {} not recognised - skipping environment ' printmsg('Shell {} not recognised - skipping environment '
'setup'.format(ctx.shell), WARNING, EMPHASIS) 'setup'.format(shell), WARNING, EMPHASIS)
return return
if ctx.shell in bourne_shells: cfg = bourne_cfg if shell in bourne_shells: cfg = bourne_cfg
else: cfg = csh_cfg else: cfg = csh_cfg
# find the profile file to edit # find the profile file to edit
profile = None profile = None
candidates = [op.join(ctx.args.homedir, p) candidates = [op.join(homedir, p)
for p in shell_profiles[ctx.shell]] for p in shell_profiles[shell]]
for candidate in candidates: for candidate in candidates:
if op.isfile(candidate): if op.isfile(candidate):
profile = candidate profile = candidate
...@@ -1289,11 +1278,11 @@ def configure_shell(ctx): ...@@ -1289,11 +1278,11 @@ def configure_shell(ctx):
patch_file(profile, '# FSL Setup', len(cfg.split('\n')), cfg) patch_file(profile, '# FSL Setup', len(cfg.split('\n')), cfg)
def configure_matlab(ctx): def configure_matlab(homedir, fsldir):
"""Creates/appends FSL configuration code to ~/Documents/MATLAB/startup.m. """Creates/appends FSL configuration code to ~/Documents/MATLAB/startup.m.
""" """
# Do not change the format of this configuration - # DO NOT CHANGE the format of this configuration -
# see in-line comments in configure_shell. # see in-line comments in configure_shell.
cfg = tw.dedent(""" cfg = tw.dedent("""
% FSL Setup % FSL Setup
...@@ -1303,9 +1292,9 @@ def configure_matlab(ctx): ...@@ -1303,9 +1292,9 @@ def configure_matlab(ctx):
fsldirmpath = sprintf('%s/etc/matlab',fsldir); fsldirmpath = sprintf('%s/etc/matlab',fsldir);
path(path, fsldirmpath); path(path, fsldirmpath);
clear fsldir fsldirmpath; clear fsldir fsldirmpath;
""").format(fsldir=ctx.destdir) """).format(fsldir=fsldir).strip()
matlab_dir = op.expanduser(op.join(ctx.args.homedir, 'Documents', 'MATLAB')) matlab_dir = op.expanduser(op.join(homedir, 'Documents', 'MATLAB'))
startup_m = op.join(matlab_dir, 'startup.m') startup_m = op.join(matlab_dir, 'startup.m')
if not op.exists(matlab_dir): if not op.exists(matlab_dir):
...@@ -1618,8 +1607,13 @@ def handle_error(ctx): ...@@ -1618,8 +1607,13 @@ def handle_error(ctx):
log.debug(''.join(tb)) log.debug(''.join(tb))
# Don't remove a failed update, despite # Don't remove a failed update, despite
# it potentially being corrupt # it potentially being corrupt (because
if (not ctx.update) and op.exists(ctx.destdir): # it might also be fine)
if ctx.update:
printmsg('Update failed - your FSL installation '
'might be corrupt!', WARNING, EMPHASIS)
elif op.exists(ctx.destdir):
printmsg('Removing failed installation directory ' printmsg('Removing failed installation directory '
'{}'.format(ctx.destdir), WARNING) '{}'.format(ctx.destdir), WARNING)
Process.check_call('rm -r ' + ctx.destdir, ctx.need_admin, ctx) Process.check_call('rm -r ' + ctx.destdir, ctx.need_admin, ctx)
...@@ -1688,8 +1682,10 @@ def main(argv=None): ...@@ -1688,8 +1682,10 @@ def main(argv=None):
finalise_installation(ctx) finalise_installation(ctx)
post_install_cleanup(ctx) post_install_cleanup(ctx)
if not args.no_shell: configure_shell( ctx) if not args.no_shell:
if not args.no_matlab: configure_matlab(ctx) configure_shell( ctx.shell, args.homedir, ctx.destdir)
if not args.no_matlab:
configure_matlab(ctx.homedir, ctx.destdir)
printmsg('\nFSL successfully installed\n', IMPORTANT) printmsg('\nFSL successfully installed\n', IMPORTANT)
if not args.no_shell: if not args.no_shell:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment