summaryrefslogtreecommitdiff
path: root/lib/spack/llnl
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2023-09-28 18:21:52 +0200
committerGitHub <noreply@github.com>2023-09-28 16:21:52 +0000
commita236fce31f5d5018c788f51cda50492f1f241cba (patch)
treef8a8bfd3e6e32f3d8ebd60e1bf047e237d893314 /lib/spack/llnl
parentf77a38a96bef7184f872f4225801ac937e825866 (diff)
downloadspack-a236fce31f5d5018c788f51cda50492f1f241cba.tar.gz
spack-a236fce31f5d5018c788f51cda50492f1f241cba.tar.bz2
spack-a236fce31f5d5018c788f51cda50492f1f241cba.tar.xz
spack-a236fce31f5d5018c788f51cda50492f1f241cba.zip
Partial removal of circular dependencies between `spack` and `llnl` (#40090)
Modifications: - [x] Move `spack.util.string` to `llnl.string` - [x] Remove dependency of `llnl` on `spack.error` - [x] Move path of `spack.util.path` to `llnl.path` - [x] Move `spack.util.environment.get_host_*` to `spack.spec`
Diffstat (limited to 'lib/spack/llnl')
-rw-r--r--lib/spack/llnl/path.py105
-rw-r--r--lib/spack/llnl/string.py67
-rw-r--r--lib/spack/llnl/util/filesystem.py6
-rw-r--r--lib/spack/llnl/util/lock.py4
-rw-r--r--lib/spack/llnl/util/symlink.py5
5 files changed, 179 insertions, 8 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
diff --git a/lib/spack/llnl/string.py b/lib/spack/llnl/string.py
new file mode 100644
index 0000000000..a203be8d34
--- /dev/null
+++ b/lib/spack/llnl/string.py
@@ -0,0 +1,67 @@
+# 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)
+"""String manipulation functions that do not have other dependencies than Python
+standard library
+"""
+from typing import List, Optional
+
+
+def comma_list(sequence: List[str], article: str = "") -> str:
+ if type(sequence) is not list:
+ sequence = list(sequence)
+
+ if not sequence:
+ return ""
+ if len(sequence) == 1:
+ return sequence[0]
+
+ out = ", ".join(str(s) for s in sequence[:-1])
+ if len(sequence) != 2:
+ out += "," # oxford comma
+ out += " "
+ if article:
+ out += article + " "
+ out += str(sequence[-1])
+ return out
+
+
+def comma_or(sequence: List[str]) -> str:
+ """Return a string with all the elements of the input joined by comma, but the last
+ one (which is joined by 'or').
+ """
+ return comma_list(sequence, "or")
+
+
+def comma_and(sequence: List[str]) -> str:
+ """Return a string with all the elements of the input joined by comma, but the last
+ one (which is joined by 'and').
+ """
+ return comma_list(sequence, "and")
+
+
+def quote(sequence: List[str], q: str = "'") -> List[str]:
+ """Quotes each item in the input list with the quote character passed as second argument."""
+ return [f"{q}{e}{q}" for e in sequence]
+
+
+def plural(n: int, singular: str, plural: Optional[str] = None, show_n: bool = True) -> str:
+ """Pluralize <singular> word by adding an s if n != 1.
+
+ Arguments:
+ n: number of things there are
+ singular: singular form of word
+ plural: optional plural form, for when it's not just singular + 's'
+ show_n: whether to include n in the result string (default True)
+
+ Returns:
+ "1 thing" if n == 1 or "n things" if n != 1
+ """
+ number = f"{n} " if show_n else ""
+ if n == 1:
+ return f"{number}{singular}"
+ elif plural is not None:
+ return f"{number}{plural}"
+ else:
+ return f"{number}{singular}s"
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index bd203ef200..8f4217049d 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -28,7 +28,8 @@ from llnl.util.lang import dedupe, memoized
from llnl.util.symlink import islink, readlink, resolve_link_target_relative_to_the_link, symlink
from spack.util.executable import Executable, which
-from spack.util.path import path_to_os_path, system_path_filter
+
+from ..path import path_to_os_path, system_path_filter
if sys.platform != "win32":
import grp
@@ -336,8 +337,7 @@ def filter_file(
if string:
regex = re.escape(regex)
- filenames = path_to_os_path(*filenames)
- for filename in filenames:
+ for filename in path_to_os_path(*filenames):
msg = 'FILTER FILE: {0} [replacing "{1}"]'
tty.debug(msg.format(filename, regex))
diff --git a/lib/spack/llnl/util/lock.py b/lib/spack/llnl/util/lock.py
index 74a325acd2..156300b891 100644
--- a/lib/spack/llnl/util/lock.py
+++ b/lib/spack/llnl/util/lock.py
@@ -14,7 +14,7 @@ from typing import IO, Any, Callable, ContextManager, Dict, Generator, Optional,
from llnl.util import lang, tty
-import spack.util.string
+from ..string import plural
if sys.platform != "win32":
import fcntl
@@ -169,7 +169,7 @@ def _attempts_str(wait_time, nattempts):
if nattempts <= 1:
return ""
- attempts = spack.util.string.plural(nattempts, "attempt")
+ attempts = plural(nattempts, "attempt")
return " after {} and {}".format(lang.pretty_seconds(wait_time), attempts)
diff --git a/lib/spack/llnl/util/symlink.py b/lib/spack/llnl/util/symlink.py
index ab6654e847..4f4f965f13 100644
--- a/lib/spack/llnl/util/symlink.py
+++ b/lib/spack/llnl/util/symlink.py
@@ -11,8 +11,7 @@ import tempfile
from llnl.util import lang, tty
-from spack.error import SpackError
-from spack.util.path import system_path_filter
+from ..path import system_path_filter
if sys.platform == "win32":
from win32file import CreateHardLink
@@ -338,7 +337,7 @@ def resolve_link_target_relative_to_the_link(link):
return os.path.join(link_dir, target)
-class SymlinkError(SpackError):
+class SymlinkError(RuntimeError):
"""Exception class for errors raised while creating symlinks,
junctions and hard links
"""