From f3652bb212967c6a51f8ae499a25b4d0c1ff2414 Mon Sep 17 00:00:00 2001 From: martclanor Date: Thu, 26 Dec 2024 18:05:40 +0100 Subject: [PATCH 1/8] set mode=0 to allow which() to return exe without suffix --- flopy/mbase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index c662854d0..0cf00eeff 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -72,10 +72,11 @@ def _resolve(exe_name): else: if exe_name.lower().endswith(".exe"): # try removing .exe suffix - exe = which(exe_name[:-4]) + # mode=0 effectively allows which() to return exe without suffix + exe = which(exe_name[:-4], mode=0) if exe is not None: # in case which() returned a relative path, resolve it - exe = which(str(Path(exe).resolve())) + exe = which(str(Path(exe).resolve()), mode=0) else: # try tilde-expanded abspath exe = which(Path(exe_name).expanduser().absolute()) From 30d7867e946989251e20c11a98ae62371e984057 Mon Sep 17 00:00:00 2001 From: martclanor Date: Thu, 26 Dec 2024 21:38:52 +0100 Subject: [PATCH 2/8] refactor resolve_exe --- flopy/mbase.py | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index 0cf00eeff..ac989f2a6 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -65,31 +65,25 @@ def resolve_exe(exe_name: Union[str, os.PathLike], forgive: bool = False) -> str """ def _resolve(exe_name): - exe = which(exe_name) - if exe is not None: - # if which() returned a relative path, resolve it - exe = which(str(Path(exe).resolve())) - else: - if exe_name.lower().endswith(".exe"): - # try removing .exe suffix - # mode=0 effectively allows which() to return exe without suffix - exe = which(exe_name[:-4], mode=0) - if exe is not None: - # in case which() returned a relative path, resolve it - exe = which(str(Path(exe).resolve()), mode=0) - else: - # try tilde-expanded abspath - exe = which(Path(exe_name).expanduser().absolute()) - if exe is None and exe_name.lower().endswith(".exe"): - # try tilde-expanded abspath without .exe suffix - exe = which(Path(exe_name[:-4]).expanduser().absolute()) - return exe - - name = str(exe_name) - exe_path = _resolve(name) - if exe_path is None and on_windows and Path(name).suffix == "": - # try adding .exe suffix on windows (for portability from other OS) - exe_path = _resolve(f"{name}.exe") + # exe_name is found (not None), ensure absolute path is returned + if exe := which(exe_name): + return which(Path(exe).resolve()) + + # exe_name has "~", expand first before returning absolute path + if "~" in exe_name and (exe := which(Path(exe_name).expanduser().resolve())): + return exe + + # exe_name is relative path + if not Path(exe_name).is_absolute() and ( + exe := which(Path(exe_name).resolve(), mode=0) + ): # mode=0 effectively allows which() to find exe without suffix in windows + return exe + + # try removing .exe suffix + if exe_name.lower().endswith(".exe"): + return _resolve(exe_name[:-4]) + + exe_path = _resolve(exe_name) # raise if we are unforgiving, otherwise return None if exe_path is None: From 433b7545bdcd862afc347a709e0cf86c91106b35 Mon Sep 17 00:00:00 2001 From: martclanor Date: Thu, 26 Dec 2024 19:41:54 +0100 Subject: [PATCH 3/8] remove unused inner_dir --- autotest/test_mbase.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/autotest/test_mbase.py b/autotest/test_mbase.py index 59e796b05..f250d9a65 100644 --- a/autotest/test_mbase.py +++ b/autotest/test_mbase.py @@ -135,10 +135,8 @@ def test_run_model_exe_rel_path(mf6_model_path, function_tmpdir, use_ext): bin_dir = function_tmpdir / "bin" bin_dir.mkdir() - inner_dir = function_tmpdir / "inner" - inner_dir.mkdir() - with set_dir(inner_dir): + with set_dir(ws): # copy exe to relative dir copy(mf6, bin_dir / "mf6") assert (bin_dir / "mf6").is_file() From 29ce6266a5c34c1ea19b11c94f53cab8eecda859 Mon Sep 17 00:00:00 2001 From: martclanor Date: Fri, 27 Dec 2024 17:45:11 +0100 Subject: [PATCH 4/8] ensure that output of resolve_exe is str --- flopy/mbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index ac989f2a6..99e32bf1f 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -98,7 +98,7 @@ def _resolve(exe_name): f"The program {exe_name} does not exist or is not executable." ) - return exe_path + return str(exe_path) # external exceptions for users From d9ab617718e9de3065f88376da90661a838f6887 Mon Sep 17 00:00:00 2001 From: martclanor Date: Fri, 27 Dec 2024 19:22:51 +0100 Subject: [PATCH 5/8] try adding .exe suffix on windows --- flopy/mbase.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index 99e32bf1f..1d541e128 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -79,9 +79,11 @@ def _resolve(exe_name): ): # mode=0 effectively allows which() to find exe without suffix in windows return exe - # try removing .exe suffix - if exe_name.lower().endswith(".exe"): + # try adding/removing .exe suffix + if on_windows and exe_name.lower().endswith(".exe"): return _resolve(exe_name[:-4]) + elif on_windows and "." not in Path(exe_name).stem: + return _resolve(f"{exe_name}.exe") exe_path = _resolve(exe_name) From f59496d8a2ba7a619e228af341d92bb6ebc67221 Mon Sep 17 00:00:00 2001 From: martclanor Date: Fri, 27 Dec 2024 19:23:53 +0100 Subject: [PATCH 6/8] prevent infinite recursion in removing/adding .exe suffix on windows --- flopy/mbase.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index 1d541e128..f2d05e6b1 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -64,7 +64,12 @@ def resolve_exe(exe_name: Union[str, os.PathLike], forgive: bool = False) -> str str: absolute path to the executable """ - def _resolve(exe_name): + def _resolve(exe_name, checked=set()): + # Prevent infinite recursion by checking if exe_name has been checked + if exe_name in checked: + return None + checked.add(exe_name) + # exe_name is found (not None), ensure absolute path is returned if exe := which(exe_name): return which(Path(exe).resolve()) @@ -80,10 +85,10 @@ def _resolve(exe_name): return exe # try adding/removing .exe suffix - if on_windows and exe_name.lower().endswith(".exe"): - return _resolve(exe_name[:-4]) + if exe_name.lower().endswith(".exe"): + return _resolve(exe_name[:-4], checked) elif on_windows and "." not in Path(exe_name).stem: - return _resolve(f"{exe_name}.exe") + return _resolve(f"{exe_name}.exe", checked) exe_path = _resolve(exe_name) From 86be08a1512a1ea60365be64b8d85a103cb58d0b Mon Sep 17 00:00:00 2001 From: martclanor Date: Fri, 27 Dec 2024 20:41:51 +0100 Subject: [PATCH 7/8] typecast arg of "which" to str for python 3.9 compatibility --- flopy/mbase.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index f2d05e6b1..5d9455724 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -71,16 +71,18 @@ def _resolve(exe_name, checked=set()): checked.add(exe_name) # exe_name is found (not None), ensure absolute path is returned - if exe := which(exe_name): - return which(Path(exe).resolve()) + if exe := which(str(exe_name)): + return which(str(Path(exe).resolve())) # exe_name has "~", expand first before returning absolute path - if "~" in exe_name and (exe := which(Path(exe_name).expanduser().resolve())): + if "~" in exe_name and ( + exe := which(str(Path(exe_name).expanduser().resolve())) + ): return exe # exe_name is relative path if not Path(exe_name).is_absolute() and ( - exe := which(Path(exe_name).resolve(), mode=0) + exe := which(str(Path(exe_name).resolve()), mode=0) ): # mode=0 effectively allows which() to find exe without suffix in windows return exe From 391c4d8f3246c012d3d347c0a88649961f9016b2 Mon Sep 17 00:00:00 2001 From: martclanor Date: Thu, 9 Jan 2025 15:11:06 +0100 Subject: [PATCH 8/8] expanduser for all relative paths to simplify --- flopy/mbase.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/flopy/mbase.py b/flopy/mbase.py index 5d9455724..02c6ad2dd 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -74,16 +74,12 @@ def _resolve(exe_name, checked=set()): if exe := which(str(exe_name)): return which(str(Path(exe).resolve())) - # exe_name has "~", expand first before returning absolute path - if "~" in exe_name and ( - exe := which(str(Path(exe_name).expanduser().resolve())) - ): - return exe - # exe_name is relative path if not Path(exe_name).is_absolute() and ( - exe := which(str(Path(exe_name).resolve()), mode=0) - ): # mode=0 effectively allows which() to find exe without suffix in windows + exe := which(str(Path(exe_name).expanduser().resolve()), mode=0) + ): + # expanduser() in case of ~ in path + # mode=0 effectively allows which() to find exe without suffix in windows return exe # try adding/removing .exe suffix