Commit 7204420f authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'rel/3.0.1' into 'v3.0'

Rel/3.0.1

See merge request fsl/fslpy!225
parents 537dec2e 776ec115
Pipeline #5220 passed with stages
in 7 minutes and 13 seconds
...@@ -206,7 +206,7 @@ build-doc: ...@@ -206,7 +206,7 @@ build-doc:
- docker - docker
stage: doc stage: doc
image: python:3.6 image: python:3.7
script: script:
- bash ./.ci/build_doc.sh - bash ./.ci/build_doc.sh
...@@ -227,7 +227,7 @@ build-pypi-dist: ...@@ -227,7 +227,7 @@ build-pypi-dist:
<<: *check_version <<: *check_version
stage: build stage: build
image: python:3.6 image: python:3.7
tags: tags:
- docker - docker
...@@ -251,7 +251,7 @@ deploy-doc: ...@@ -251,7 +251,7 @@ deploy-doc:
<<: *setup_ssh <<: *setup_ssh
stage: deploy stage: deploy
when: manual when: manual
image: python:3.6 image: python:3.7
tags: tags:
- docker - docker
...@@ -268,7 +268,7 @@ deploy-pypi: ...@@ -268,7 +268,7 @@ deploy-pypi:
<<: *setup_ssh <<: *setup_ssh
stage: deploy stage: deploy
when: manual when: manual
image: python:3.6 image: python:3.7
tags: tags:
- docker - docker
...@@ -285,7 +285,7 @@ deploy-zenodo: ...@@ -285,7 +285,7 @@ deploy-zenodo:
<<: *setup_ssh <<: *setup_ssh
stage: deploy stage: deploy
when: manual when: manual
image: python:3.6 image: python:3.7
tags: tags:
- docker - docker
......
...@@ -2,6 +2,17 @@ This document contains the ``fslpy`` release history in reverse chronological ...@@ -2,6 +2,17 @@ This document contains the ``fslpy`` release history in reverse chronological
order. order.
3.0.1 (Wednesday 15th April 2020)
---------------------------------
Changed
^^^^^^^
* The :func:`.isMelodicDir` function now accepts directories that do not end
with ``.ica``, as long as all required files are present.
3.0.0 (Sunday 29th March 2020) 3.0.0 (Sunday 29th March 2020)
------------------------------ ------------------------------
...@@ -52,7 +63,7 @@ Changed ...@@ -52,7 +63,7 @@ Changed
:attr:`.LOAD` symbol, can now be accessed as attributes of the returned :attr:`.LOAD` symbol, can now be accessed as attributes of the returned
results object, in addition to being accessed as dict items. results object, in addition to being accessed as dict items.
* Wrapper functions decorated with the :func:`.fileOrImage`, * Wrapper functions decorated with the :func:`.fileOrImage`,
:func:`.fileOrArray`, or :func:`.fileOrText` decorators will now pass all :func:`.fileOrArray`, or :func:`.fileOrText` decorators will now pass all
arguments and return values through unchanged if an argument called ``submit`` arguments and return values through unchanged if an argument called ``submit``
is passed in, and is set to ``True`` (or any non-``False`` is passed in, and is set to ``True`` (or any non-``False``
value). Furthermore, in such a scenario a :exc:`ValueError` will be raised if value). Furthermore, in such a scenario a :exc:`ValueError` will be raised if
......
...@@ -622,8 +622,9 @@ class Nifti(notifier.Notifier, meta.Meta): ...@@ -622,8 +622,9 @@ class Nifti(notifier.Notifier, meta.Meta):
if from_ not in ('voxel', 'fsl', 'world') or \ if from_ not in ('voxel', 'fsl', 'world') or \
to not in ('voxel', 'fsl', 'world'): to not in ('voxel', 'fsl', 'world'):
raise ValueError('Invalid source/reference spaces: ' raise ValueError('Invalid source/reference spaces: "{}" -> "{}".'
'{} -> {}'.format(from_, to)) 'Recognised spaces are "voxel", "fsl", and '
'"world"'.format(from_, to))
return np.copy(self.__affines[from_, to]) return np.copy(self.__affines[from_, to])
......
...@@ -33,7 +33,6 @@ import logging ...@@ -33,7 +33,6 @@ import logging
import os.path as op import os.path as op
import numpy as np import numpy as np
import fsl.utils.path as fslpath
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.data.featanalysis as featanalysis import fsl.data.featanalysis as featanalysis
...@@ -63,10 +62,9 @@ def isMelodicImage(path): ...@@ -63,10 +62,9 @@ def isMelodicImage(path):
def isMelodicDir(path): def isMelodicDir(path):
"""Returns ``True`` if the given path looks like it is contained within """Returns ``True`` if the given path looks like it is a MELODIC directory,
a MELODIC directory, ``False`` otherwise. A melodic directory: ``False`` otherwise. A MELODIC directory:
- Must be named ``*.ica``.
- Must contain a file called ``melodic_IC.nii.gz`` or - Must contain a file called ``melodic_IC.nii.gz`` or
``melodic_oIC.nii.gz``. ``melodic_oIC.nii.gz``.
- Must contain a file called ``melodic_mix``. - Must contain a file called ``melodic_mix``.
...@@ -75,12 +73,7 @@ def isMelodicDir(path): ...@@ -75,12 +73,7 @@ def isMelodicDir(path):
path = op.abspath(path) path = op.abspath(path)
if op.isdir(path): dirname = path if not op.isdir(path):
else: dirname = op.dirname(path)
sufs = ['.ica']
if not any([dirname.endswith(suf) for suf in sufs]):
return False return False
# Must contain an image file called # Must contain an image file called
...@@ -88,7 +81,7 @@ def isMelodicDir(path): ...@@ -88,7 +81,7 @@ def isMelodicDir(path):
prefixes = ['melodic_IC', 'melodic_oIC'] prefixes = ['melodic_IC', 'melodic_oIC']
for p in prefixes: for p in prefixes:
try: try:
fslimage.addExt(op.join(dirname, p)) fslimage.addExt(op.join(path, p))
break break
except fslimage.PathError: except fslimage.PathError:
pass pass
...@@ -97,8 +90,8 @@ def isMelodicDir(path): ...@@ -97,8 +90,8 @@ def isMelodicDir(path):
# Must contain files called # Must contain files called
# melodic_mix and melodic_FTmix # melodic_mix and melodic_FTmix
if not op.exists(op.join(dirname, 'melodic_mix')): return False if not op.exists(op.join(path, 'melodic_mix')): return False
if not op.exists(op.join(dirname, 'melodic_FTmix')): return False if not op.exists(op.join(path, 'melodic_FTmix')): return False
return True return True
...@@ -108,10 +101,13 @@ def getAnalysisDir(path): ...@@ -108,10 +101,13 @@ def getAnalysisDir(path):
to that MELODIC directory is returned. Otherwise, ``None`` is returned. to that MELODIC directory is returned. Otherwise, ``None`` is returned.
""" """
meldir = fslpath.deepest(path, ['.ica']) if not op.isdir(path):
path = op.dirname(path)
if meldir is not None and isMelodicDir(meldir): while path not in (op.sep, ''):
return meldir if isMelodicDir(path):
return path
path = op.dirname(path)
return None return None
......
...@@ -47,7 +47,7 @@ import re ...@@ -47,7 +47,7 @@ import re
import string import string
__version__ = '3.0.0' __version__ = '3.0.1'
"""Current version number, as a string. """ """Current version number, as a string. """
......
...@@ -98,19 +98,26 @@ def invxfm(inmat, omat): ...@@ -98,19 +98,26 @@ def invxfm(inmat, omat):
return ['convert_xfm', '-omat', omat, '-inverse', inmat] return ['convert_xfm', '-omat', omat, '-inverse', inmat]
@wutils.fileOrArray('inmat1', 'inmat2', 'outmat') @wutils.fileOrArray('atob', 'atoc', 'btoc')
@wutils.fslwrapper @wutils.fslwrapper
def concatxfm(inmat1, inmat2, outmat): def concatxfm(atob, btoc, atoc):
"""Use ``convert_xfm`` to concatenate two affines.""" """Use ``convert_xfm`` to concatenate two affines. Note that the
order of the input matrices is the opposite of the order expected
by ``convert_xfm``.
:arg atob: Input matrix, transforming from "A" to "B".
:arg btoc: Input matrix, transforming from "B" to "C".
:arg atoc: Output matrix, transforming from "A" to "C".
"""
asrt.assertFileExists(inmat1, inmat2) asrt.assertFileExists(atob, btoc)
cmd = ['convert_xfm', cmd = ['convert_xfm',
'-omat', '-omat',
outmat, atoc,
'-concat', '-concat',
inmat2, btoc,
inmat1] atob]
return cmd return cmd
......
...@@ -160,14 +160,14 @@ def test_assertIsMelodicDir(): ...@@ -160,14 +160,14 @@ def test_assertIsMelodicDir():
('analysis.ica', [ 'melodic_mix', 'melodic_FTmix'], False), ('analysis.ica', [ 'melodic_mix', 'melodic_FTmix'], False),
('analysis.ica', ['melodic_IC.nii.gz', 'melodic_FTmix'], False), ('analysis.ica', ['melodic_IC.nii.gz', 'melodic_FTmix'], False),
('analysis.ica', ['melodic_IC.nii.gz', 'melodic_mix'], False), ('analysis.ica', ['melodic_IC.nii.gz', 'melodic_mix'], False),
('analysis', ['melodic_IC.nii.gz', 'melodic_mix', 'melodic_FTmix'], False), ('analysis', ['melodic_IC.nii.gz', 'melodic_mix', 'melodic_FTmix'], True),
('analysis', ['melodic_oIC.nii.gz', 'melodic_mix', 'melodic_FTmix'], False), ('analysis', [ 'melodic_mix', 'melodic_FTmix'], False),
] ]
for dirname, paths, expected in tests: for dirname, paths, expected in tests:
with testdir(paths, dirname): with testdir(paths, dirname):
if expected: if expected:
assertions.assertIsMelodicDir(dirname) assertions.assertIsMelodicDir('.')
else: else:
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assertions.assertIsMelodicDir(dirname) assertions.assertIsMelodicDir(dirname)
......
...@@ -13,7 +13,8 @@ import os ...@@ -13,7 +13,8 @@ import os
import os.path as op import os.path as op
import numpy as np import numpy as np
import mock
from unittest import mock
import pytest import pytest
import tests import tests
......
...@@ -22,7 +22,7 @@ from . import mockFSLDIR ...@@ -22,7 +22,7 @@ from . import mockFSLDIR
mock_fsl_sub = """ mock_fsl_sub = """
#!{} #!/usr/bin/env python3
import random import random
import os import os
...@@ -62,8 +62,7 @@ with open('{{}}.o{{}}'.format(cmd, jobid), 'w') as stdout, \ ...@@ -62,8 +62,7 @@ with open('{{}}.o{{}}'.format(cmd, jobid), 'w') as stdout, \
print(str(jobid)) print(str(jobid))
sys.exit(0) sys.exit(0)
""".format(sys.executable, op.dirname(fsl.__file__)).strip() """.format(op.dirname(fsl.__file__)).strip()
@contextlib.contextmanager @contextlib.contextmanager
def fslsub_mockFSLDIR(): def fslsub_mockFSLDIR():
......
...@@ -55,10 +55,10 @@ def test_isMelodicDir(): ...@@ -55,10 +55,10 @@ def test_isMelodicDir():
meldir = op.join(testdir, 'analysis.ica') meldir = op.join(testdir, 'analysis.ica')
assert mela.isMelodicDir(meldir) assert mela.isMelodicDir(meldir)
# Directory must end in .ica # non-.ica prefix is ok
with tests.testdir([p.replace('.ica', '.blob') for p in paths]) as testdir: with tests.testdir([p.replace('.ica', '.blob') for p in paths]) as testdir:
meldir = op.join(testdir, 'analysis.blob') meldir = op.join(testdir, 'analysis.blob')
assert not mela.isMelodicDir(meldir) assert mela.isMelodicDir(meldir)
# Directory must exist! # Directory must exist!
assert not mela.isMelodicDir('non-existent.ica') assert not mela.isMelodicDir('non-existent.ica')
......
...@@ -18,9 +18,9 @@ from .. import mockFSLDIR as mockFSLDIR_base, make_random_image ...@@ -18,9 +18,9 @@ from .. import mockFSLDIR as mockFSLDIR_base, make_random_image
mock_fslstats = """ mock_fslstats = """
#!{} #!/usr/bin/env python3
shape = {{outshape}} shape = {outshape}
import sys import sys
import numpy as np import numpy as np
...@@ -31,7 +31,7 @@ if len(shape) == 1: ...@@ -31,7 +31,7 @@ if len(shape) == 1:
data = data.reshape(1, -1) data = data.reshape(1, -1)
np.savetxt(sys.stdout, data, fmt='%i') np.savetxt(sys.stdout, data, fmt='%i')
""".format(sys.executable).strip() """.strip()
@contextlib.contextmanager @contextlib.contextmanager
......
Markdown is supported
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