From bcd0ca1728fd37a828cceb6f5dc22c1e33715cbe Mon Sep 17 00:00:00 2001 From: Laurent Guerard Date: Wed, 6 Nov 2024 14:51:44 +0100 Subject: [PATCH 01/10] Add fix for \n and \t getting interpretated as new line and tabs --- src/imcflibs/pathtools.py | 1 + tests/test_pathtools.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/imcflibs/pathtools.py b/src/imcflibs/pathtools.py index 73c61427..78090e8a 100644 --- a/src/imcflibs/pathtools.py +++ b/src/imcflibs/pathtools.py @@ -101,6 +101,7 @@ def parse_path(path, prefix=""): 'path': '/path/to/some/'} """ path = str(path) + path = path.replace("\n", "\\n").replace("\t", "\\t") if prefix: # remove leading slash, otherwise join() will discard the first path: if path.startswith("/"): diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index fb81e811..b78b3e41 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import pytest from imcflibs.pathtools import parse_path from imcflibs.pathtools import jython_fiji_exists from imcflibs.pathtools import image_basename @@ -44,6 +43,14 @@ def test_parse_path_windows(): assert parsed['dname'] == 'foo' +def test_parse_path_windows_tabs_and_lines(): + path = "C:\new_folder\test" + parsed = parse_path(path) + + assert parsed["full"] == r"C:\new_folder\test" + assert parsed["fname"] == "test" + + def test_jython_fiji_exists(tmpdir): assert jython_fiji_exists(str(tmpdir)) == True From 92771c098516100d685f9c789d1371069e0f9947 Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 11:58:11 +0100 Subject: [PATCH 02/10] Add test docstring --- tests/test_pathtools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index b78b3e41..2e842eee 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -44,6 +44,7 @@ def test_parse_path_windows(): def test_parse_path_windows_tabs_and_lines(): + r"""Test non-raw string containing newline \n and tab \t sequences.""" path = "C:\new_folder\test" parsed = parse_path(path) From 770fe1e8bc721e54d3fa6cb70f631ba0d51c136a Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 22:24:06 +0100 Subject: [PATCH 03/10] Add note about Windows paths and raw strings As discussed in issue #10 it is tedious trying to guard against funny escape sequences, instead it is explicitly pointed out to use a raw string. --- src/imcflibs/pathtools.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/imcflibs/pathtools.py b/src/imcflibs/pathtools.py index 78090e8a..7f693d85 100644 --- a/src/imcflibs/pathtools.py +++ b/src/imcflibs/pathtools.py @@ -10,7 +10,7 @@ def parse_path(path, prefix=""): - """Parse a path into its components. + r"""Parse a path into its components. If the path doesn't end with the pathsep, it is assumed being a file! No tests based on existing files are done, as this is supposed to also work @@ -20,6 +20,11 @@ def parse_path(path, prefix=""): *Script Parameter* `#@ File`) for either of the parameters, so it is safe to use this in ImageJ Python scripts without additional measures. + **WARNING**: when passing in **Windows paths** literally, make sure to + declare them as **raw strings** using the `r""` notation, otherwise + unexpected things might happen if the path contains sections that Python + will interpret as escape sequences (e.g. `\n`, `\t`, `\u2324`, ...). + Parameters ---------- path : str or str-like @@ -64,6 +69,7 @@ def parse_path(path, prefix=""): 'parent': '/tmp/', 'path': '/tmp/foo/'} + POSIX-style path to a directory: >>> parse_path('/tmp/foo/') @@ -76,17 +82,19 @@ def parse_path(path, prefix=""): 'parent': '/tmp/', 'path': '/tmp/foo/'} + Windows-style path to a file: - >>> parse_path('C:\\Temp\\foo\\file.ext') - {'dname': 'foo', + >>> parse_path(r'C:\Temp\new\file.ext') + {'dname': 'new', 'ext': '.ext', 'fname': 'file.ext', - 'full': 'C:/Temp/foo/file.ext', + 'full': 'C:/Temp/new/file.ext', 'basename': 'file', - 'orig': 'C:\\Temp\\foo\\file.ext', + 'orig': 'C:\\Temp\\new\\file.ext', 'parent': 'C:/Temp/', - 'path': 'C:/Temp/foo/'} + 'path': 'C:/Temp/new/'} + Special treatment for *OME-TIFF* suffixes: From 70ed32de625f92dbbcc10d24e32c25cac61a9e1f Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 22:27:23 +0100 Subject: [PATCH 04/10] Do not mess with escape sequences As discussed in issue #10 --- src/imcflibs/pathtools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/imcflibs/pathtools.py b/src/imcflibs/pathtools.py index 7f693d85..4e68efc9 100644 --- a/src/imcflibs/pathtools.py +++ b/src/imcflibs/pathtools.py @@ -109,7 +109,6 @@ def parse_path(path, prefix=""): 'path': '/path/to/some/'} """ path = str(path) - path = path.replace("\n", "\\n").replace("\t", "\\t") if prefix: # remove leading slash, otherwise join() will discard the first path: if path.startswith("/"): From 47834445e0f06ccd2c294516ec3d9af767f38cd7 Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 22:27:38 +0100 Subject: [PATCH 05/10] Test for Windows raw-string paths Relates to #10 --- tests/test_pathtools.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index 2e842eee..d1904ace 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -43,6 +43,23 @@ def test_parse_path_windows(): assert parsed['dname'] == 'foo' +def test_parse_path_windows_newline(): + path = r'C:\Temp\new\file.ext' + parsed = parse_path(path) + + assert parsed == { + 'dname': 'new', + 'ext': '.ext', + 'fname': 'file.ext', + 'full': 'C:/Temp/new/file.ext', + 'basename': 'file', + 'orig': 'C:\\Temp\\new\\file.ext', + 'parent': 'C:/Temp', + 'path': 'C:/Temp/new/', + } + + + def test_parse_path_windows_tabs_and_lines(): r"""Test non-raw string containing newline \n and tab \t sequences.""" path = "C:\new_folder\test" From e338326d9d95e285fc1f6ebc9ebb627f56e5a6da Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 22:40:00 +0100 Subject: [PATCH 06/10] Adjust non-raw Windows escape-sequence path test Relates to #10 --- tests/test_pathtools.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index d1904ace..4b7e1d0b 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -59,14 +59,17 @@ def test_parse_path_windows_newline(): } - def test_parse_path_windows_tabs_and_lines(): - r"""Test non-raw string containing newline \n and tab \t sequences.""" + r"""Test non-raw string containing newline `\n` and tab `\t` sequences. + + As `parse_path()` cannot work on non-raw strings containing escape + sequences, the parsed result will not be the expected one. + """ path = "C:\new_folder\test" parsed = parse_path(path) - assert parsed["full"] == r"C:\new_folder\test" - assert parsed["fname"] == "test" + assert parsed["full"] != r"C:\new_folder\test" + assert parsed["fname"] != "test" def test_jython_fiji_exists(tmpdir): From 6ef9896d170839ebfa532d81be47436f456f6c2d Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 23:34:52 +0100 Subject: [PATCH 07/10] Reformat docstring code --- src/imcflibs/pathtools.py | 72 ++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/src/imcflibs/pathtools.py b/src/imcflibs/pathtools.py index 4e68efc9..b08d0672 100644 --- a/src/imcflibs/pathtools.py +++ b/src/imcflibs/pathtools.py @@ -60,53 +60,61 @@ def parse_path(path, prefix=""): POSIX-style path to a file with a suffix: >>> parse_path('/tmp/foo/file.suffix') - {'dname': 'foo', - 'ext': '', - 'fname': 'file', - 'full': '/tmp/foo/file', - 'basename': 'file', - 'orig': '/tmp/foo/file', - 'parent': '/tmp/', - 'path': '/tmp/foo/'} + { + "dname": "foo", + "ext": "", + "fname": "file", + "full": "/tmp/foo/file", + "basename": "file", + "orig": "/tmp/foo/file", + "parent": "/tmp/", + "path": "/tmp/foo/", + } POSIX-style path to a directory: >>> parse_path('/tmp/foo/') - {'dname': 'foo', - 'ext': '', - 'fname': '', - 'full': '/tmp/foo/', - 'basename': '', - 'orig': '/tmp/foo/', - 'parent': '/tmp/', - 'path': '/tmp/foo/'} + { + "dname": "foo", + "ext": "", + "fname": "", + "full": "/tmp/foo/", + "basename": "", + "orig": "/tmp/foo/", + "parent": "/tmp/", + "path": "/tmp/foo/", + } Windows-style path to a file: >>> parse_path(r'C:\Temp\new\file.ext') - {'dname': 'new', - 'ext': '.ext', - 'fname': 'file.ext', - 'full': 'C:/Temp/new/file.ext', - 'basename': 'file', - 'orig': 'C:\\Temp\\new\\file.ext', - 'parent': 'C:/Temp/', - 'path': 'C:/Temp/new/'} + { + "dname": "new", + "ext": ".ext", + "fname": "file.ext", + "full": "C:/Temp/new/file.ext", + "basename": "file", + "orig": "C:\\Temp\\new\\file.ext", + "parent": "C:/Temp", + "path": "C:/Temp/new/", + } Special treatment for *OME-TIFF* suffixes: >>> parse_path("/path/to/some/nice.OME.tIf") - {'basename': 'nice', - 'dname': 'some', - 'ext': '.OME.tIf', - 'fname': 'nice.OME.tIf', - 'full': '/path/to/some/nice.OME.tIf', - 'orig': '/path/to/some/nice.OME.tIf', - 'parent': '/path/to/', - 'path': '/path/to/some/'} + { + "basename": "nice", + "dname": "some", + "ext": ".OME.tIf", + "fname": "nice.OME.tIf", + "full": "/path/to/some/nice.OME.tIf", + "orig": "/path/to/some/nice.OME.tIf", + "parent": "/path/to/", + "path": "/path/to/some/", + } """ path = str(path) if prefix: From e2c3a7140e5b70999ccb309a430ff8d58db18379 Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 23:50:09 +0100 Subject: [PATCH 08/10] Add tests docstrings --- tests/test_pathtools.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index 4b7e1d0b..4a2bf6cf 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -11,6 +11,7 @@ def test_parse_path(): + """Tests using regular POSIX-style paths.""" path = '/tmp/foo/' path_to_dir = parse_path(path) path_to_file = parse_path(path + 'file.ext') @@ -35,6 +36,7 @@ def test_parse_path(): def test_parse_path_windows(): path = r'C:\foo\bar' + """Test using a Windows-style path.""" parsed = parse_path(path) assert parsed['orig'] == path @@ -44,6 +46,7 @@ def test_parse_path_windows(): def test_parse_path_windows_newline(): + """Test a Windows path with newline and tab sequences as raw string.""" path = r'C:\Temp\new\file.ext' parsed = parse_path(path) @@ -73,10 +76,12 @@ def test_parse_path_windows_tabs_and_lines(): def test_jython_fiji_exists(tmpdir): + """Test the Jython/Fiji `os.path.exists()` workaround.""" assert jython_fiji_exists(str(tmpdir)) == True def test_image_basename(): + """Test basename extraction for various image file names.""" assert image_basename('/path/to/image_file_01.png') == 'image_file_01' assert image_basename('more-complex-stack.ome.tif') == 'more-complex-stack' assert image_basename('/tmp/FoObAr.OMe.tIf') == 'FoObAr' From a47732f4f5627844a911905b2a179cac8edf2e9c Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 23:50:28 +0100 Subject: [PATCH 09/10] Rename tests to reflect their function --- tests/test_pathtools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index 4a2bf6cf..e8c661ec 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -45,7 +45,7 @@ def test_parse_path_windows(): assert parsed['dname'] == 'foo' -def test_parse_path_windows_newline(): +def test_parse_path_windows_newline_tab(): """Test a Windows path with newline and tab sequences as raw string.""" path = r'C:\Temp\new\file.ext' parsed = parse_path(path) @@ -62,7 +62,7 @@ def test_parse_path_windows_newline(): } -def test_parse_path_windows_tabs_and_lines(): +def test_parse_path_windows_nonraw(): r"""Test non-raw string containing newline `\n` and tab `\t` sequences. As `parse_path()` cannot work on non-raw strings containing escape From ca0cc6ceb46025262de666930f59c6626d5d2e69 Mon Sep 17 00:00:00 2001 From: Niko Ehrenfeuchter Date: Thu, 13 Mar 2025 23:51:16 +0100 Subject: [PATCH 10/10] Minor Windows path adjustments for test --- tests/test_pathtools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_pathtools.py b/tests/test_pathtools.py index e8c661ec..bbcfacd1 100644 --- a/tests/test_pathtools.py +++ b/tests/test_pathtools.py @@ -35,14 +35,14 @@ def test_parse_path(): def test_parse_path_windows(): - path = r'C:\foo\bar' """Test using a Windows-style path.""" + path = r'C:\Foo\Bar' parsed = parse_path(path) assert parsed['orig'] == path - assert parsed['full'] == r'C:/foo/bar' - assert parsed['fname'] == 'bar' - assert parsed['dname'] == 'foo' + assert parsed['full'] == 'C:/Foo/Bar' + assert parsed['fname'] == 'Bar' + assert parsed['dname'] == 'Foo' def test_parse_path_windows_newline_tab():