Commit 49c91d74 authored by Martin Craig's avatar Martin Craig Committed by Paul McCarthy
Browse files

Moved fslpath and winpath from fsl.utils.platform to fsl.utils.path

Also minor bugfix - allow forward or backslash after \\wsl$ when
specifying FSLDIR as a WSL network share
parent 9d66bea9
...@@ -30,7 +30,9 @@ import os.path as op ...@@ -30,7 +30,9 @@ import os.path as op
import os import os
import glob import glob
import operator import operator
import re
from fsl.utils.platform import platform
class PathError(Exception): class PathError(Exception):
"""``Exception`` class raised by the functions defined in this module """``Exception`` class raised by the functions defined in this module
...@@ -524,3 +526,56 @@ def commonBase(paths): ...@@ -524,3 +526,56 @@ def commonBase(paths):
return base return base
raise PathError('No common base') raise PathError('No common base')
def wslpath(winpath):
"""
Convert Windows path (or a command line argument containing a Windows path)
to the equivalent WSL path (e.g. ``c:\\Users`` -> ``/mnt/c/Users``). Also supports
paths in the form ``\\wsl$\\(distro)\\users\\...``
:param winpath: Command line argument which may (or may not) contain a Windows path. It is assumed to be
either of the form <windows path> or --<arg>=<windows path>. Note that we don't need to
handle --arg <windows path> or -a <windows path> since in these cases the argument
and the path will be parsed as separate entities.
:return: If ``winpath`` matches a Windows path, the converted argument (including the --<arg>= portion).
Otherwise returns ``winpath`` unchanged.
"""
match = re.match(r"^(--[\w-]+=)?\\\\wsl\$[\\\/][^\\^\/]+(.*)$", winpath)
if match:
arg, path = match.group(1, 2)
if arg is None:
arg = ""
return arg + path.replace("\\", "/")
match = re.match(r"^(--[\w-]+=)?([a-zA-z]):(.+)$", winpath)
if match:
arg, drive, path = match.group(1, 2, 3)
if arg is None:
arg = ""
return arg + "/mnt/" + drive.lower() + path.replace("\\", "/")
return winpath
def winpath(wslpath):
"""
Convert a WSL-local filepath (for example ``/usr/local/fsl/``) into a path that can be used from
Windows.
If ``self.fslwsl`` is ``False``, simply returns ``wslpath`` unmodified
Otherwise, uses ``FSLDIR`` to deduce the WSL distro in use for FSL.
This requires WSL2 which supports the ``\\wsl$\`` network path.
wslpath is assumed to be an absolute path.
"""
if not platform.fslwsl:
return wslpath
else:
match = re.match(r"^\\\\wsl\$\\([^\\]+).*$", platform.fsldir)
if match:
distro = match.group(1)
else:
distro = None
if not distro:
raise RuntimeError("Could not identify WSL installation from FSLDIR (%s)" % platform.fsldir)
return "\\\\wsl$\\" + distro + wslpath.replace("/", "\\")
...@@ -17,7 +17,6 @@ import os ...@@ -17,7 +17,6 @@ import os
import os.path as op import os.path as op
import sys import sys
import importlib import importlib
import re
import fsl.utils.notifier as notifier import fsl.utils.notifier as notifier
...@@ -294,7 +293,7 @@ class Platform(notifier.Notifier): ...@@ -294,7 +293,7 @@ class Platform(notifier.Notifier):
@property @property
def fslwsl(self): def fslwsl(self):
"""Boolean flag indicating whether FSL is installed in Windows Subsystem for Linux """ """Boolean flag indicating whether FSL is installed in Windows Subsystem for Linux """
return self.fsldir is not None and self.fsldir.startswith("\\\\wsl$\\") return self.fsldir is not None and self.fsldir.startswith("\\\\wsl$")
@fsldir.setter @fsldir.setter
def fsldir(self, value): def fsldir(self, value):
...@@ -405,59 +404,6 @@ class Platform(notifier.Notifier): ...@@ -405,59 +404,6 @@ class Platform(notifier.Notifier):
""" """
return self.__glIsSoftware return self.__glIsSoftware
def wslpath(self, winpath):
"""
Convert Windows path (or a command line argument containing a Windows path)
to the equivalent WSL path (e.g. ``c:\\Users`` -> ``/mnt/c/Users``). Also supports
paths in the form ``\\wsl$\\(distro)\\users\\...``
:param winpath: Command line argument which may (or may not) contain a Windows path. It is assumed to be
either of the form <windows path> or --<arg>=<windows path>. Note that we don't need to
handle --arg <windows path> or -a <windows path> since in these cases the argument
and the path will be parsed as separate entities.
:return: If ``winpath`` matches a Windows path, the converted argument (including the --<arg>= portion).
Otherwise returns ``winpath`` unchanged.
"""
match = re.match(r"^(--[\w-]+=)?\\\\wsl\$\\[^\\]+(\\.*)$", winpath)
if match:
arg, path = match.group(1, 2)
if arg is None:
arg = ""
return arg + path.replace("\\", "/")
match = re.match(r"^(--[\w-]+=)?([a-zA-z]):(.+)$", winpath)
if match:
arg, drive, path = match.group(1, 2, 3)
if arg is None:
arg = ""
return arg + "/mnt/" + drive.lower() + path.replace("\\", "/")
return winpath
def winpath(self, wslpath):
"""
Convert a WSL-local filepath (for example ``/usr/local/fsl/``) into a path that can be used from
Windows.
If ``self.fslwsl`` is ``False``, simply returns ``wslpath`` unmodified
Otherwise, uses ``FSLDIR`` to deduce the WSL distro in use for FSL.
This requires WSL2 which supports the ``\\wsl$\`` network path.
wslpath is assumed to be an absolute path.
"""
if not self.fslwsl:
return wslpath
else:
match = re.match(r"^\\\\wsl\$\\([^\\]+).*$", self.fsldir)
if match:
distro = match.group(1)
else:
distro = None
if not distro:
raise RuntimeError("Could not identify WSL installation from FSLDIR (%s)" % self.fsldir)
return "\\\\wsl$\\" + distro + wslpath.replace("/", "\\")
platform = Platform() platform = Platform()
"""An instance of the :class:`Platform` class. Feel free to create your own """An instance of the :class:`Platform` class. Feel free to create your own
instance, but be aware that if you do so you will not be updated of changes instance, but be aware that if you do so you will not be updated of changes
......
...@@ -35,7 +35,7 @@ import six ...@@ -35,7 +35,7 @@ import six
from fsl.utils.platform import platform as fslplatform from fsl.utils.platform import platform as fslplatform
import fsl.utils.fslsub as fslsub import fsl.utils.fslsub as fslsub
import fsl.utils.tempdir as tempdir import fsl.utils.tempdir as tempdir
import fsl.utils.path as fslpath
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -393,15 +393,15 @@ def wslcmd(cmdpath, *args): ...@@ -393,15 +393,15 @@ def wslcmd(cmdpath, *args):
""" """
# Check if command exists in WSL (remembering that the command path may include FSLDIR which # Check if command exists in WSL (remembering that the command path may include FSLDIR which
# is a Windows path) # is a Windows path)
cmdpath = fslplatform.wslpath(cmdpath) cmdpath = fslpath.wslpath(cmdpath)
retcode = sp.call(["wsl", "test", "-x", cmdpath]) retcode = sp.call(["wsl", "test", "-x", cmdpath])
if retcode == 0: if retcode == 0:
# Form a new argument list and convert any Windows paths in it into WSL paths # Form a new argument list and convert any Windows paths in it into WSL paths
wslargs = [fslplatform.wslpath(arg) for arg in args] wslargs = [fslpath.wslpath(arg) for arg in args]
wslargs[0] = cmdpath wslargs[0] = cmdpath
local_fsldir = fslplatform.wslpath(fslplatform.fsldir) local_fsldir = fslpath.wslpath(fslplatform.fsldir)
if fslplatform.fsldevdir: if fslplatform.fsldevdir:
local_fsldevdir = fslplatform.wslpath(fslplatform.fsldevdir) local_fsldevdir = fslpath.wslpath(fslplatform.fsldevdir)
else: else:
local_fsldevdir = None local_fsldevdir = None
# Prepend important environment variables - note that it seems we cannot # Prepend important environment variables - note that it seems we cannot
...@@ -418,7 +418,6 @@ def wslcmd(cmdpath, *args): ...@@ -418,7 +418,6 @@ def wslcmd(cmdpath, *args):
] ]
if local_fsldevdir: if local_fsldevdir:
prepargs.append("FSLDEVDIR=%s" % local_fsldevdir) prepargs.append("FSLDEVDIR=%s" % local_fsldevdir)
return prepargs + wslargs return prepargs + wslargs
else: else:
# Command was not found in WSL with this path # Command was not found in WSL with this path
......
...@@ -13,6 +13,7 @@ import shutil ...@@ -13,6 +13,7 @@ import shutil
import tempfile import tempfile
import pytest import pytest
import mock
import fsl.utils.path as fslpath import fsl.utils.path as fslpath
import fsl.data.image as fslimage import fsl.data.image as fslimage
...@@ -1395,3 +1396,19 @@ def test_commonBase(): ...@@ -1395,3 +1396,19 @@ def test_commonBase():
for ft in failtests: for ft in failtests:
with pytest.raises(fslpath.PathError): with pytest.raises(fslpath.PathError):
fslpath.commonBase(ft) fslpath.commonBase(ft)
def test_wslpath():
assert fslpath.wslpath('c:\\Users\\Fishcake\\image.nii.gz') == '/mnt/c/Users/Fishcake/image.nii.gz'
assert fslpath.wslpath('--input=x:\\transfers\\scratch\\image_2.nii') == '--input=/mnt/x/transfers/scratch/image_2.nii'
assert fslpath.wslpath('\\\\wsl$\\centos 7\\users\\fsl\\file.nii') == '/users/fsl/file.nii'
assert fslpath.wslpath('--file=\\\\wsl$\\centos 7\\home\\fsl\\img.nii.gz') == '--file=/home/fsl/img.nii.gz'
assert fslpath.wslpath('\\\\wsl$/centos 7/users\\fsl\\file.nii') == '/users/fsl/file.nii'
def test_winpath():
"""
See comment for ``test_fslwsl`` for why we are overwriting FSLDIR
"""
with mock.patch.dict('os.environ', **{ 'FSLDIR' : '\\\\wsl$\\my cool linux distro v2.0\\usr\\local\\fsl'}):
assert fslpath.winpath("/home/fsl/myfile.dat") == '\\\\wsl$\\my cool linux distro v2.0\\home\\fsl\\myfile.dat'
with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/opt/fsl'}):
assert fslpath.winpath("/home/fsl/myfile.dat") == '/home/fsl/myfile.dat'
...@@ -226,20 +226,3 @@ def test_fslwsl(): ...@@ -226,20 +226,3 @@ def test_fslwsl():
with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/usr/local/fsl'}): with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/usr/local/fsl'}):
assert not p.fslwsl assert not p.fslwsl
def test_wslpath():
p = fslplatform.Platform()
assert p.wslpath('c:\\Users\\Fishcake\\image.nii.gz') == '/mnt/c/Users/Fishcake/image.nii.gz'
assert p.wslpath('--input=x:\\transfers\\scratch\\image_2.nii') == '--input=/mnt/x/transfers/scratch/image_2.nii'
assert p.wslpath('\\\\wsl$\\centos 7\\users\\fsl\\file.nii') == '/users/fsl/file.nii'
assert p.wslpath('--file=\\\\wsl$\\centos 7\\home\\fsl\\img.nii.gz') == '--file=/home/fsl/img.nii.gz'
def test_winpath():
"""
See comment for ``test_fslwsl``
"""
p = fslplatform.Platform()
with mock.patch.dict('os.environ', **{ 'FSLDIR' : '\\\\wsl$\\my cool linux distro v2.0\\usr\\local\\fsl'}):
assert p.winpath("/home/fsl/myfile.dat") == '\\\\wsl$\\my cool linux distro v2.0\\home\\fsl\\myfile.dat'
with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/opt/fsl'}):
assert p.winpath("/home/fsl/myfile.dat") == '/home/fsl/myfile.dat'
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