summaryrefslogtreecommitdiff
path: root/lib/spack/llnl/path.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/llnl/path.py')
-rw-r--r--lib/spack/llnl/path.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/lib/spack/llnl/path.py b/lib/spack/llnl/path.py
new file mode 100644
index 0000000000..bef9e14ada
--- /dev/null
+++ b/lib/spack/llnl/path.py
@@ -0,0 +1,105 @@
+# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+"""Path primitives that just require Python standard library."""
+import functools
+import sys
+from typing import List, Optional
+from urllib.parse import urlparse
+
+
+class Path:
+ """Enum to identify the path-style."""
+
+ unix: int = 0
+ windows: int = 1
+ platform_path: int = windows if sys.platform == "win32" else unix
+
+
+def format_os_path(path: str, mode: int = Path.unix) -> str:
+ """Formats the input path to use consistent, platform specific separators.
+
+ Absolute paths are converted between drive letters and a prepended '/' as per platform
+ requirement.
+
+ Parameters:
+ path: the path to be normalized, must be a string or expose the replace method.
+ mode: the path file separator style to normalize the passed path to.
+ Default is unix style, i.e. '/'
+ """
+ if not path:
+ return path
+ if mode == Path.windows:
+ path = path.replace("/", "\\")
+ else:
+ path = path.replace("\\", "/")
+ return path
+
+
+def convert_to_posix_path(path: str) -> str:
+ """Converts the input path to POSIX style."""
+ return format_os_path(path, mode=Path.unix)
+
+
+def convert_to_windows_path(path: str) -> str:
+ """Converts the input path to Windows style."""
+ return format_os_path(path, mode=Path.windows)
+
+
+def convert_to_platform_path(path: str) -> str:
+ """Converts the input path to the current platform's native style."""
+ return format_os_path(path, mode=Path.platform_path)
+
+
+def path_to_os_path(*parameters: str) -> List[str]:
+ """Takes an arbitrary number of positional parameters, converts each argument of type
+ string to use a normalized filepath separator, and returns a list of all values.
+ """
+
+ def _is_url(path_or_url: str) -> bool:
+ if "\\" in path_or_url:
+ return False
+ url_tuple = urlparse(path_or_url)
+ return bool(url_tuple.scheme) and len(url_tuple.scheme) > 1
+
+ result = []
+ for item in parameters:
+ if isinstance(item, str) and not _is_url(item):
+ item = convert_to_platform_path(item)
+ result.append(item)
+ return result
+
+
+def system_path_filter(_func=None, arg_slice: Optional[slice] = None):
+ """Filters function arguments to account for platform path separators.
+ Optional slicing range can be specified to select specific arguments
+
+ This decorator takes all (or a slice) of a method's positional arguments
+ and normalizes usage of filepath separators on a per platform basis.
+
+ Note: `**kwargs`, urls, and any type that is not a string are ignored
+ so in such cases where path normalization is required, that should be
+ handled by calling path_to_os_path directly as needed.
+
+ Parameters:
+ arg_slice: a slice object specifying the slice of arguments
+ in the decorated method over which filepath separators are
+ normalized
+ """
+
+ def holder_func(func):
+ @functools.wraps(func)
+ def path_filter_caller(*args, **kwargs):
+ args = list(args)
+ if arg_slice:
+ args[arg_slice] = path_to_os_path(*args[arg_slice])
+ else:
+ args = path_to_os_path(*args)
+ return func(*args, **kwargs)
+
+ return path_filter_caller
+
+ if _func:
+ return holder_func(_func)
+ return holder_func