From 49c91d74aea5326a70fdebbac6f9d2c88a4abb63 Mon Sep 17 00:00:00 2001
From: Martin Craig <martin.craig@eng.ox.ac.uk>
Date: Wed, 22 Apr 2020 13:53:28 +0100
Subject: [PATCH] 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

---
 fsl/utils/path.py            | 55 +++++++++++++++++++++++++++++++++++
 fsl/utils/platform.py        | 56 +-----------------------------------
 fsl/utils/run.py             | 11 ++++---
 tests/test_fsl_utils_path.py | 17 +++++++++++
 tests/test_platform.py       | 17 -----------
 5 files changed, 78 insertions(+), 78 deletions(-)

diff --git a/fsl/utils/path.py b/fsl/utils/path.py
index 8d312bef5..7f7f80cf0 100644
--- a/fsl/utils/path.py
+++ b/fsl/utils/path.py
@@ -30,7 +30,9 @@ import os.path as op
 import            os
 import            glob
 import            operator
+import re
 
+from fsl.utils.platform import platform
 
 class PathError(Exception):
     """``Exception`` class raised by the functions defined in this module
@@ -524,3 +526,56 @@ def commonBase(paths):
             return 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("/", "\\")
diff --git a/fsl/utils/platform.py b/fsl/utils/platform.py
index 173b031cb..73d0937d2 100644
--- a/fsl/utils/platform.py
+++ b/fsl/utils/platform.py
@@ -17,7 +17,6 @@ import os
 import os.path as op
 import sys
 import importlib
-import re
 
 import fsl.utils.notifier as notifier
 
@@ -294,7 +293,7 @@ class Platform(notifier.Notifier):
     @property
     def fslwsl(self):
         """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
     def fsldir(self, value):
@@ -405,59 +404,6 @@ class Platform(notifier.Notifier):
         """
         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()
 """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
diff --git a/fsl/utils/run.py b/fsl/utils/run.py
index 2523f50dc..acb77c741 100644
--- a/fsl/utils/run.py
+++ b/fsl/utils/run.py
@@ -35,7 +35,7 @@ import               six
 from   fsl.utils.platform import platform as fslplatform
 import fsl.utils.fslsub                   as fslsub
 import fsl.utils.tempdir                  as tempdir
-
+import fsl.utils.path                     as fslpath
 
 log = logging.getLogger(__name__)
 
@@ -393,15 +393,15 @@ def wslcmd(cmdpath, *args):
     """
     # Check if command exists in WSL (remembering that the command path may include FSLDIR which
     # is a Windows path)
-    cmdpath = fslplatform.wslpath(cmdpath)
+    cmdpath = fslpath.wslpath(cmdpath)
     retcode = sp.call(["wsl", "test", "-x", cmdpath])
     if retcode == 0:
         # 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
-        local_fsldir = fslplatform.wslpath(fslplatform.fsldir)
+        local_fsldir = fslpath.wslpath(fslplatform.fsldir)
         if fslplatform.fsldevdir:
-            local_fsldevdir = fslplatform.wslpath(fslplatform.fsldevdir)
+            local_fsldevdir = fslpath.wslpath(fslplatform.fsldevdir)
         else:
             local_fsldevdir = None
         # Prepend important environment variables - note that it seems we cannot
@@ -418,7 +418,6 @@ def wslcmd(cmdpath, *args):
         ]
         if local_fsldevdir:
             prepargs.append("FSLDEVDIR=%s" % local_fsldevdir)
-
         return prepargs + wslargs
     else:
         # Command was not found in WSL with this path
diff --git a/tests/test_fsl_utils_path.py b/tests/test_fsl_utils_path.py
index d4cc1bec5..0361ced5e 100644
--- a/tests/test_fsl_utils_path.py
+++ b/tests/test_fsl_utils_path.py
@@ -13,6 +13,7 @@ import            shutil
 import            tempfile
 
 import pytest
+import mock
 
 import fsl.utils.path as fslpath
 import fsl.data.image as fslimage
@@ -1395,3 +1396,19 @@ def test_commonBase():
     for ft in failtests:
         with pytest.raises(fslpath.PathError):
             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'
diff --git a/tests/test_platform.py b/tests/test_platform.py
index 46dad0ce5..9024bec82 100644
--- a/tests/test_platform.py
+++ b/tests/test_platform.py
@@ -226,20 +226,3 @@ def test_fslwsl():
 
     with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/usr/local/fsl'}):
         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'
-- 
GitLab