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`
This commit is contained in:
parent
f77a38a96b
commit
a236fce31f
105
lib/spack/llnl/path.py
Normal file
105
lib/spack/llnl/path.py
Normal file
@ -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
|
67
lib/spack/llnl/string.py
Normal file
67
lib/spack/llnl/string.py
Normal file
@ -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"
|
@ -28,7 +28,8 @@
|
|||||||
from llnl.util.symlink import islink, readlink, resolve_link_target_relative_to_the_link, symlink
|
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.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":
|
if sys.platform != "win32":
|
||||||
import grp
|
import grp
|
||||||
@ -336,8 +337,7 @@ def groupid_to_group(x):
|
|||||||
|
|
||||||
if string:
|
if string:
|
||||||
regex = re.escape(regex)
|
regex = re.escape(regex)
|
||||||
filenames = path_to_os_path(*filenames)
|
for filename in path_to_os_path(*filenames):
|
||||||
for filename in filenames:
|
|
||||||
msg = 'FILTER FILE: {0} [replacing "{1}"]'
|
msg = 'FILTER FILE: {0} [replacing "{1}"]'
|
||||||
tty.debug(msg.format(filename, regex))
|
tty.debug(msg.format(filename, regex))
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
from llnl.util import lang, tty
|
from llnl.util import lang, tty
|
||||||
|
|
||||||
import spack.util.string
|
from ..string import plural
|
||||||
|
|
||||||
if sys.platform != "win32":
|
if sys.platform != "win32":
|
||||||
import fcntl
|
import fcntl
|
||||||
@ -169,7 +169,7 @@ def _attempts_str(wait_time, nattempts):
|
|||||||
if nattempts <= 1:
|
if nattempts <= 1:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
attempts = spack.util.string.plural(nattempts, "attempt")
|
attempts = plural(nattempts, "attempt")
|
||||||
return " after {} and {}".format(lang.pretty_seconds(wait_time), attempts)
|
return " after {} and {}".format(lang.pretty_seconds(wait_time), attempts)
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
from llnl.util import lang, tty
|
from llnl.util import lang, tty
|
||||||
|
|
||||||
from spack.error import SpackError
|
from ..path import system_path_filter
|
||||||
from spack.util.path import system_path_filter
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
from win32file import CreateHardLink
|
from win32file import CreateHardLink
|
||||||
@ -338,7 +337,7 @@ def resolve_link_target_relative_to_the_link(link):
|
|||||||
return os.path.join(link_dir, target)
|
return os.path.join(link_dir, target)
|
||||||
|
|
||||||
|
|
||||||
class SymlinkError(SpackError):
|
class SymlinkError(RuntimeError):
|
||||||
"""Exception class for errors raised while creating symlinks,
|
"""Exception class for errors raised while creating symlinks,
|
||||||
junctions and hard links
|
junctions and hard links
|
||||||
"""
|
"""
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
from llnl.string import plural
|
||||||
from llnl.util.filesystem import join_path
|
from llnl.util.filesystem import join_path
|
||||||
from llnl.util.lang import dedupe
|
from llnl.util.lang import dedupe
|
||||||
from llnl.util.symlink import symlink
|
from llnl.util.symlink import symlink
|
||||||
@ -82,7 +83,6 @@
|
|||||||
from spack.util.executable import Executable
|
from spack.util.executable import Executable
|
||||||
from spack.util.log_parse import make_log_context, parse_log_events
|
from spack.util.log_parse import make_log_context, parse_log_events
|
||||||
from spack.util.module_cmd import load_module, module, path_from_modules
|
from spack.util.module_cmd import load_module, module, path_from_modules
|
||||||
from spack.util.string import plural
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# This can be set by the user to globally disable parallel builds.
|
# This can be set by the user to globally disable parallel builds.
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from typing import List, Match, Tuple
|
from typing import List, Match, Tuple
|
||||||
|
|
||||||
|
import llnl.string
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import join_path
|
from llnl.util.filesystem import join_path
|
||||||
from llnl.util.lang import attr_setdefault, index_by
|
from llnl.util.lang import attr_setdefault, index_by
|
||||||
@ -29,7 +30,6 @@
|
|||||||
import spack.user_environment as uenv
|
import spack.user_environment as uenv
|
||||||
import spack.util.spack_json as sjson
|
import spack.util.spack_json as sjson
|
||||||
import spack.util.spack_yaml as syaml
|
import spack.util.spack_yaml as syaml
|
||||||
import spack.util.string
|
|
||||||
|
|
||||||
# cmd has a submodule called "list" so preserve the python list module
|
# cmd has a submodule called "list" so preserve the python list module
|
||||||
python_list = list
|
python_list = list
|
||||||
@ -516,7 +516,7 @@ def print_how_many_pkgs(specs, pkg_type=""):
|
|||||||
category, e.g. if pkg_type is "installed" then the message
|
category, e.g. if pkg_type is "installed" then the message
|
||||||
would be "3 installed packages"
|
would be "3 installed packages"
|
||||||
"""
|
"""
|
||||||
tty.msg("%s" % spack.util.string.plural(len(specs), pkg_type + " package"))
|
tty.msg("%s" % llnl.string.plural(len(specs), pkg_type + " package"))
|
||||||
|
|
||||||
|
|
||||||
def spack_is_git_repo():
|
def spack_is_git_repo():
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
import llnl.util.tty.color as clr
|
import llnl.util.tty.color as clr
|
||||||
|
from llnl.string import plural
|
||||||
from llnl.util.lang import elide_list
|
from llnl.util.lang import elide_list
|
||||||
|
|
||||||
import spack.binary_distribution as bindist
|
import spack.binary_distribution as bindist
|
||||||
@ -32,7 +33,6 @@
|
|||||||
from spack.cmd import display_specs
|
from spack.cmd import display_specs
|
||||||
from spack.spec import Spec, save_dependency_specfiles
|
from spack.spec import Spec, save_dependency_specfiles
|
||||||
from spack.stage import Stage
|
from spack.stage import Stage
|
||||||
from spack.util.string import plural
|
|
||||||
|
|
||||||
description = "create, download and install binary packages"
|
description = "create, download and install binary packages"
|
||||||
section = "packaging"
|
section = "packaging"
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
import llnl.string as string
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.tty.colify import colify
|
from llnl.util.tty.colify import colify
|
||||||
@ -28,7 +29,6 @@
|
|||||||
import spack.schema.env
|
import spack.schema.env
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.tengine
|
import spack.tengine
|
||||||
import spack.util.string as string
|
|
||||||
from spack.util.environment import EnvironmentModifications
|
from spack.util.environment import EnvironmentModifications
|
||||||
|
|
||||||
description = "manage virtual environments"
|
description = "manage virtual environments"
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
import posixpath
|
import posixpath
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from llnl.path import convert_to_posix_path
|
||||||
|
|
||||||
import spack.paths
|
import spack.paths
|
||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
from spack.util.path import convert_to_posix_path
|
|
||||||
|
|
||||||
description = "generate Windows installer"
|
description = "generate Windows installer"
|
||||||
section = "admin"
|
section = "admin"
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import llnl.string
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
import llnl.util.tty.colify as colify
|
import llnl.util.tty.colify as colify
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ def report_tags(category, tags):
|
|||||||
if isatty:
|
if isatty:
|
||||||
num = len(tags)
|
num = len(tags)
|
||||||
fmt = "{0} package tag".format(category)
|
fmt = "{0} package tag".format(category)
|
||||||
buffer.write("{0}:\n".format(spack.util.string.plural(num, fmt)))
|
buffer.write("{0}:\n".format(llnl.string.plural(num, fmt)))
|
||||||
|
|
||||||
if tags:
|
if tags:
|
||||||
colify.colify(tags, output=buffer, tty=isatty, indent=4)
|
colify.colify(tags, output=buffer, tty=isatty, indent=4)
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
import tempfile
|
import tempfile
|
||||||
from typing import List, Optional, Sequence
|
from typing import List, Optional, Sequence
|
||||||
|
|
||||||
|
import llnl.path
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import path_contains_subdirectory, paths_containing_libs
|
from llnl.util.filesystem import path_contains_subdirectory, paths_containing_libs
|
||||||
@ -24,7 +25,6 @@
|
|||||||
import spack.util.module_cmd
|
import spack.util.module_cmd
|
||||||
import spack.version
|
import spack.version
|
||||||
from spack.util.environment import filter_system_paths
|
from spack.util.environment import filter_system_paths
|
||||||
from spack.util.path import system_path_filter
|
|
||||||
|
|
||||||
__all__ = ["Compiler"]
|
__all__ = ["Compiler"]
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ def _parse_link_paths(string):
|
|||||||
return implicit_link_dirs
|
return implicit_link_dirs
|
||||||
|
|
||||||
|
|
||||||
@system_path_filter
|
@llnl.path.system_path_filter
|
||||||
def _parse_non_system_link_dirs(string: str) -> List[str]:
|
def _parse_non_system_link_dirs(string: str) -> List[str]:
|
||||||
"""Parses link paths out of compiler debug output.
|
"""Parses link paths out of compiler debug output.
|
||||||
|
|
||||||
|
@ -120,10 +120,8 @@ def write_host_environment(self, spec):
|
|||||||
versioning. We use it in the case that an analysis later needs to
|
versioning. We use it in the case that an analysis later needs to
|
||||||
easily access this information.
|
easily access this information.
|
||||||
"""
|
"""
|
||||||
from spack.util.environment import get_host_environment_metadata
|
|
||||||
|
|
||||||
env_file = self.env_metadata_path(spec)
|
env_file = self.env_metadata_path(spec)
|
||||||
environ = get_host_environment_metadata()
|
environ = spack.spec.get_host_environment_metadata()
|
||||||
with open(env_file, "w") as fd:
|
with open(env_file, "w") as fd:
|
||||||
sjson.dump(environ, fd)
|
sjson.dump(environ, fd)
|
||||||
|
|
||||||
|
@ -404,7 +404,7 @@ def _write_yaml(data, str_or_file):
|
|||||||
|
|
||||||
def _eval_conditional(string):
|
def _eval_conditional(string):
|
||||||
"""Evaluate conditional definitions using restricted variable scope."""
|
"""Evaluate conditional definitions using restricted variable scope."""
|
||||||
valid_variables = spack.util.environment.get_host_environment()
|
valid_variables = spack.spec.get_host_environment()
|
||||||
valid_variables.update({"re": re, "env": os.environ})
|
valid_variables.update({"re": re, "env": os.environ})
|
||||||
return eval(string, valid_variables)
|
return eval(string, valid_variables)
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
import llnl.util
|
import llnl.util
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
from llnl.string import comma_and, quote
|
||||||
from llnl.util.filesystem import get_single_file, mkdirp, temp_cwd, temp_rename, working_dir
|
from llnl.util.filesystem import get_single_file, mkdirp, temp_cwd, temp_rename, working_dir
|
||||||
from llnl.util.symlink import symlink
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
@ -49,7 +50,6 @@
|
|||||||
import spack.version.git_ref_lookup
|
import spack.version.git_ref_lookup
|
||||||
from spack.util.compression import decompressor_for
|
from spack.util.compression import decompressor_for
|
||||||
from spack.util.executable import CommandNotFoundError, which
|
from spack.util.executable import CommandNotFoundError, which
|
||||||
from spack.util.string import comma_and, quote
|
|
||||||
|
|
||||||
#: List of all fetch strategies, created by FetchStrategy metaclass.
|
#: List of all fetch strategies, created by FetchStrategy metaclass.
|
||||||
all_strategies = []
|
all_strategies = []
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
from llnl.string import plural
|
||||||
from llnl.util.lang import nullcontext
|
from llnl.util.lang import nullcontext
|
||||||
from llnl.util.tty.color import colorize
|
from llnl.util.tty.color import colorize
|
||||||
|
|
||||||
@ -26,7 +27,6 @@
|
|||||||
from spack.installer import InstallError
|
from spack.installer import InstallError
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
from spack.util.prefix import Prefix
|
from spack.util.prefix import Prefix
|
||||||
from spack.util.string import plural
|
|
||||||
|
|
||||||
#: Stand-alone test failure info type
|
#: Stand-alone test failure info type
|
||||||
TestFailureType = Tuple[BaseException, str]
|
TestFailureType = Tuple[BaseException, str]
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from typing import Any, Dict, List, Union
|
from typing import Any, Dict, List, Union
|
||||||
|
|
||||||
|
import llnl.path
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
@ -563,7 +564,7 @@ def __init__(
|
|||||||
self.checker = package_checker
|
self.checker = package_checker
|
||||||
self.packages_path = self.checker.packages_path
|
self.packages_path = self.checker.packages_path
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
self.packages_path = spack.util.path.convert_to_posix_path(self.packages_path)
|
self.packages_path = llnl.path.convert_to_posix_path(self.packages_path)
|
||||||
self.namespace = namespace
|
self.namespace = namespace
|
||||||
|
|
||||||
self.indexers: Dict[str, Indexer] = {}
|
self.indexers: Dict[str, Indexer] = {}
|
||||||
|
@ -54,10 +54,14 @@
|
|||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Callable, List, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
import llnl.path
|
||||||
|
import llnl.string
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.lang as lang
|
import llnl.util.lang as lang
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
@ -82,11 +86,9 @@
|
|||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
import spack.util.hash
|
import spack.util.hash
|
||||||
import spack.util.module_cmd as md
|
import spack.util.module_cmd as md
|
||||||
import spack.util.path as pth
|
|
||||||
import spack.util.prefix
|
import spack.util.prefix
|
||||||
import spack.util.spack_json as sjson
|
import spack.util.spack_json as sjson
|
||||||
import spack.util.spack_yaml as syaml
|
import spack.util.spack_yaml as syaml
|
||||||
import spack.util.string
|
|
||||||
import spack.variant as vt
|
import spack.variant as vt
|
||||||
import spack.version as vn
|
import spack.version as vn
|
||||||
import spack.version.git_ref_lookup
|
import spack.version.git_ref_lookup
|
||||||
@ -1390,7 +1392,7 @@ def _format_module_list(modules):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def external_path(self):
|
def external_path(self):
|
||||||
return pth.path_to_os_path(self._external_path)[0]
|
return llnl.path.path_to_os_path(self._external_path)[0]
|
||||||
|
|
||||||
@external_path.setter
|
@external_path.setter
|
||||||
def external_path(self, ext_path):
|
def external_path(self, ext_path):
|
||||||
@ -1799,7 +1801,7 @@ def prefix(self):
|
|||||||
|
|
||||||
@prefix.setter
|
@prefix.setter
|
||||||
def prefix(self, value):
|
def prefix(self, value):
|
||||||
self._prefix = spack.util.prefix.Prefix(pth.convert_to_platform_path(value))
|
self._prefix = spack.util.prefix.Prefix(llnl.path.convert_to_platform_path(value))
|
||||||
|
|
||||||
def spec_hash(self, hash):
|
def spec_hash(self, hash):
|
||||||
"""Utility method for computing different types of Spec hashes.
|
"""Utility method for computing different types of Spec hashes.
|
||||||
@ -5148,6 +5150,43 @@ def save_dependency_specfiles(root: Spec, output_directory: str, dependencies: L
|
|||||||
fd.write(spec.to_json(hash=ht.dag_hash))
|
fd.write(spec.to_json(hash=ht.dag_hash))
|
||||||
|
|
||||||
|
|
||||||
|
def get_host_environment_metadata() -> Dict[str, str]:
|
||||||
|
"""Get the host environment, reduce to a subset that we can store in
|
||||||
|
the install directory, and add the spack version.
|
||||||
|
"""
|
||||||
|
import spack.main
|
||||||
|
|
||||||
|
environ = get_host_environment()
|
||||||
|
return {
|
||||||
|
"host_os": environ["os"],
|
||||||
|
"platform": environ["platform"],
|
||||||
|
"host_target": environ["target"],
|
||||||
|
"hostname": environ["hostname"],
|
||||||
|
"spack_version": spack.main.get_version(),
|
||||||
|
"kernel_version": platform.version(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_host_environment() -> Dict[str, Any]:
|
||||||
|
"""Return a dictionary (lookup) with host information (not including the
|
||||||
|
os.environ).
|
||||||
|
"""
|
||||||
|
host_platform = spack.platforms.host()
|
||||||
|
host_target = host_platform.target("default_target")
|
||||||
|
host_os = host_platform.operating_system("default_os")
|
||||||
|
arch_fmt = "platform={0} os={1} target={2}"
|
||||||
|
arch_spec = Spec(arch_fmt.format(host_platform, host_os, host_target))
|
||||||
|
return {
|
||||||
|
"target": str(host_target),
|
||||||
|
"os": str(host_os),
|
||||||
|
"platform": str(host_platform),
|
||||||
|
"arch": arch_spec,
|
||||||
|
"architecture": arch_spec,
|
||||||
|
"arch_str": str(arch_spec),
|
||||||
|
"hostname": socket.gethostname(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class SpecParseError(spack.error.SpecError):
|
class SpecParseError(spack.error.SpecError):
|
||||||
"""Wrapper for ParseError for when we're parsing specs."""
|
"""Wrapper for ParseError for when we're parsing specs."""
|
||||||
|
|
||||||
@ -5208,7 +5247,7 @@ class InvalidDependencyError(spack.error.SpecError):
|
|||||||
def __init__(self, pkg, deps):
|
def __init__(self, pkg, deps):
|
||||||
self.invalid_deps = deps
|
self.invalid_deps = deps
|
||||||
super().__init__(
|
super().__init__(
|
||||||
"Package {0} does not depend on {1}".format(pkg, spack.util.string.comma_or(deps))
|
"Package {0} does not depend on {1}".format(pkg, llnl.string.comma_or(deps))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import tempfile
|
import tempfile
|
||||||
from typing import Callable, Dict, Iterable, Optional
|
from typing import Callable, Dict, Iterable, Optional
|
||||||
|
|
||||||
|
import llnl.string
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import (
|
from llnl.util.filesystem import (
|
||||||
@ -37,7 +38,6 @@
|
|||||||
import spack.util.lock
|
import spack.util.lock
|
||||||
import spack.util.path as sup
|
import spack.util.path as sup
|
||||||
import spack.util.pattern as pattern
|
import spack.util.pattern as pattern
|
||||||
import spack.util.string
|
|
||||||
import spack.util.url as url_util
|
import spack.util.url as url_util
|
||||||
from spack.util.crypto import bit_length, prefix_bits
|
from spack.util.crypto import bit_length, prefix_bits
|
||||||
|
|
||||||
@ -897,7 +897,7 @@ def get_checksums_for_versions(
|
|||||||
num_ver = len(sorted_versions)
|
num_ver = len(sorted_versions)
|
||||||
|
|
||||||
tty.msg(
|
tty.msg(
|
||||||
f"Found {spack.util.string.plural(num_ver, 'version')} of {package_name}:",
|
f"Found {llnl.string.plural(num_ver, 'version')} of {package_name}:",
|
||||||
"",
|
"",
|
||||||
*llnl.util.lang.elide_list(
|
*llnl.util.lang.elide_list(
|
||||||
["{0:{1}} {2}".format(str(v), max_len, url_by_version[v]) for v in sorted_versions]
|
["{0:{1}} {2}".format(str(v), max_len, url_by_version[v]) for v in sorted_versions]
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from llnl.path import Path, convert_to_platform_path
|
||||||
from llnl.util.filesystem import HeaderList, LibraryList
|
from llnl.util.filesystem import HeaderList, LibraryList
|
||||||
|
|
||||||
import spack.build_environment
|
import spack.build_environment
|
||||||
@ -21,7 +22,6 @@
|
|||||||
from spack.util.cpus import determine_number_of_jobs
|
from spack.util.cpus import determine_number_of_jobs
|
||||||
from spack.util.environment import EnvironmentModifications
|
from spack.util.environment import EnvironmentModifications
|
||||||
from spack.util.executable import Executable
|
from spack.util.executable import Executable
|
||||||
from spack.util.path import Path, convert_to_platform_path
|
|
||||||
|
|
||||||
|
|
||||||
def os_pathsep_join(path, *pths):
|
def os_pathsep_join(path, *pths):
|
||||||
|
@ -7,13 +7,14 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from llnl.path import convert_to_posix_path
|
||||||
|
|
||||||
import spack.bootstrap
|
import spack.bootstrap
|
||||||
import spack.bootstrap.core
|
import spack.bootstrap.core
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.main
|
import spack.main
|
||||||
import spack.mirror
|
import spack.mirror
|
||||||
from spack.util.path import convert_to_posix_path
|
|
||||||
|
|
||||||
_bootstrap = spack.main.SpackCommand("bootstrap")
|
_bootstrap = spack.main.SpackCommand("bootstrap")
|
||||||
|
|
||||||
|
@ -12,11 +12,12 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from llnl.path import path_to_os_path
|
||||||
|
|
||||||
import spack.paths
|
import spack.paths
|
||||||
import spack.repo
|
import spack.repo
|
||||||
from spack.directory_layout import DirectoryLayout, InvalidDirectoryLayoutParametersError
|
from spack.directory_layout import DirectoryLayout, InvalidDirectoryLayoutParametersError
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
from spack.util.path import path_to_os_path
|
|
||||||
|
|
||||||
# number of packages to test (to reduce test time)
|
# number of packages to test (to reduce test time)
|
||||||
max_packages = 10
|
max_packages = 10
|
||||||
|
43
lib/spack/spack/test/llnl/llnl_string.py
Normal file
43
lib/spack/spack/test/llnl/llnl_string.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# 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)
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import llnl.string
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"arguments,expected",
|
||||||
|
[
|
||||||
|
((0, "thing"), "0 things"),
|
||||||
|
((1, "thing"), "1 thing"),
|
||||||
|
((2, "thing"), "2 things"),
|
||||||
|
((1, "thing", "wombats"), "1 thing"),
|
||||||
|
((2, "thing", "wombats"), "2 wombats"),
|
||||||
|
((2, "thing", "wombats", False), "wombats"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_plural(arguments, expected):
|
||||||
|
assert llnl.string.plural(*arguments) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"arguments,expected",
|
||||||
|
[((["one", "two"],), ["'one'", "'two'"]), ((["one", "two"], "^"), ["^one^", "^two^"])],
|
||||||
|
)
|
||||||
|
def test_quote(arguments, expected):
|
||||||
|
assert llnl.string.quote(*arguments) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,expected_and,expected_or",
|
||||||
|
[
|
||||||
|
(["foo"], "foo", "foo"),
|
||||||
|
(["foo", "bar"], "foo and bar", "foo or bar"),
|
||||||
|
(["foo", "bar", "baz"], "foo, bar, and baz", "foo, bar, or baz"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comma_and_or(input, expected_and, expected_or):
|
||||||
|
assert llnl.string.comma_and(input) == expected_and
|
||||||
|
assert llnl.string.comma_or(input) == expected_or
|
@ -1,14 +0,0 @@
|
|||||||
# 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)
|
|
||||||
|
|
||||||
from spack.util.string import plural
|
|
||||||
|
|
||||||
|
|
||||||
def test_plural():
|
|
||||||
assert plural(0, "thing") == "0 things"
|
|
||||||
assert plural(1, "thing") == "1 thing"
|
|
||||||
assert plural(2, "thing") == "2 things"
|
|
||||||
assert plural(1, "thing", "wombats") == "1 thing"
|
|
||||||
assert plural(2, "thing", "wombats") == "2 wombats"
|
|
@ -31,12 +31,12 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import llnl.url
|
import llnl.url
|
||||||
|
from llnl.path import convert_to_posix_path
|
||||||
from llnl.util.tty.color import cescape, colorize
|
from llnl.util.tty.color import cescape, colorize
|
||||||
|
|
||||||
import spack.error
|
import spack.error
|
||||||
import spack.util.web
|
import spack.util.web
|
||||||
import spack.version
|
import spack.version
|
||||||
from spack.util.path import convert_to_posix_path
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Note: We call the input to most of these functions a "path" but the functions
|
# Note: We call the input to most of these functions a "path" but the functions
|
||||||
|
@ -10,21 +10,16 @@
|
|||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import pickle
|
import pickle
|
||||||
import platform
|
|
||||||
import re
|
import re
|
||||||
import socket
|
|
||||||
import sys
|
import sys
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Callable, Dict, List, MutableMapping, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, List, MutableMapping, Optional, Tuple, Union
|
||||||
|
|
||||||
|
from llnl.path import path_to_os_path, system_path_filter
|
||||||
from llnl.util import tty
|
from llnl.util import tty
|
||||||
from llnl.util.lang import dedupe
|
from llnl.util.lang import dedupe
|
||||||
|
|
||||||
import spack.platforms
|
|
||||||
import spack.spec
|
|
||||||
|
|
||||||
from .executable import Executable, which
|
from .executable import Executable, which
|
||||||
from .path import path_to_os_path, system_path_filter
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
SYSTEM_PATHS = [
|
SYSTEM_PATHS = [
|
||||||
@ -224,43 +219,6 @@ def pickle_environment(path: Path, environment: Optional[Dict[str, str]] = None)
|
|||||||
pickle.dump(dict(environment if environment else os.environ), pickle_file, protocol=2)
|
pickle.dump(dict(environment if environment else os.environ), pickle_file, protocol=2)
|
||||||
|
|
||||||
|
|
||||||
def get_host_environment_metadata() -> Dict[str, str]:
|
|
||||||
"""Get the host environment, reduce to a subset that we can store in
|
|
||||||
the install directory, and add the spack version.
|
|
||||||
"""
|
|
||||||
import spack.main
|
|
||||||
|
|
||||||
environ = get_host_environment()
|
|
||||||
return {
|
|
||||||
"host_os": environ["os"],
|
|
||||||
"platform": environ["platform"],
|
|
||||||
"host_target": environ["target"],
|
|
||||||
"hostname": environ["hostname"],
|
|
||||||
"spack_version": spack.main.get_version(),
|
|
||||||
"kernel_version": platform.version(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_host_environment() -> Dict[str, Any]:
|
|
||||||
"""Return a dictionary (lookup) with host information (not including the
|
|
||||||
os.environ).
|
|
||||||
"""
|
|
||||||
host_platform = spack.platforms.host()
|
|
||||||
host_target = host_platform.target("default_target")
|
|
||||||
host_os = host_platform.operating_system("default_os")
|
|
||||||
arch_fmt = "platform={0} os={1} target={2}"
|
|
||||||
arch_spec = spack.spec.Spec(arch_fmt.format(host_platform, host_os, host_target))
|
|
||||||
return {
|
|
||||||
"target": str(host_target),
|
|
||||||
"os": str(host_os),
|
|
||||||
"platform": str(host_platform),
|
|
||||||
"arch": arch_spec,
|
|
||||||
"architecture": arch_spec,
|
|
||||||
"arch_str": str(arch_spec),
|
|
||||||
"hostname": socket.gethostname(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def set_env(**kwargs):
|
def set_env(**kwargs):
|
||||||
"""Temporarily sets and restores environment variables.
|
"""Temporarily sets and restores environment variables.
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.lang import memoized
|
from llnl.util.lang import memoized
|
||||||
@ -98,31 +97,10 @@ def replacements():
|
|||||||
SPACK_PATH_PADDING_CHARS = "__spack_path_placeholder__"
|
SPACK_PATH_PADDING_CHARS = "__spack_path_placeholder__"
|
||||||
|
|
||||||
|
|
||||||
def is_path_url(path):
|
|
||||||
if "\\" in path:
|
|
||||||
return False
|
|
||||||
url_tuple = urlparse(path)
|
|
||||||
return bool(url_tuple.scheme) and len(url_tuple.scheme) > 1
|
|
||||||
|
|
||||||
|
|
||||||
def win_exe_ext():
|
def win_exe_ext():
|
||||||
return ".exe"
|
return ".exe"
|
||||||
|
|
||||||
|
|
||||||
def path_to_os_path(*pths):
|
|
||||||
"""
|
|
||||||
Takes an arbitrary number of positional parameters
|
|
||||||
converts each arguemnt of type string to use a normalized
|
|
||||||
filepath separator, and returns a list of all values
|
|
||||||
"""
|
|
||||||
ret_pths = []
|
|
||||||
for pth in pths:
|
|
||||||
if isinstance(pth, str) and not is_path_url(pth):
|
|
||||||
pth = convert_to_platform_path(pth)
|
|
||||||
ret_pths.append(pth)
|
|
||||||
return ret_pths
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_filename(filename: str) -> str:
|
def sanitize_filename(filename: str) -> str:
|
||||||
"""
|
"""
|
||||||
Replaces unsupported characters (for the host) in a filename with underscores.
|
Replaces unsupported characters (for the host) in a filename with underscores.
|
||||||
@ -145,42 +123,6 @@ def sanitize_filename(filename: str) -> str:
|
|||||||
return re.sub(r'[\x00-\x1F\x7F"*/:<>?\\|]', "_", filename)
|
return re.sub(r'[\x00-\x1F\x7F"*/:<>?\\|]', "_", filename)
|
||||||
|
|
||||||
|
|
||||||
def system_path_filter(_func=None, arg_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 (slice): a slice object specifying the slice of arguments
|
|
||||||
in the decorated method over which filepath separators are
|
|
||||||
normalized
|
|
||||||
"""
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
def holder_func(func):
|
|
||||||
@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
|
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def get_system_path_max():
|
def get_system_path_max():
|
||||||
# Choose a conservative default
|
# Choose a conservative default
|
||||||
@ -202,54 +144,6 @@ def get_system_path_max():
|
|||||||
return sys_max_path_length
|
return sys_max_path_length
|
||||||
|
|
||||||
|
|
||||||
class Path:
|
|
||||||
"""
|
|
||||||
Describes the filepath separator types
|
|
||||||
in an enum style
|
|
||||||
with a helper attribute
|
|
||||||
exposing the path type of
|
|
||||||
the current platform.
|
|
||||||
"""
|
|
||||||
|
|
||||||
unix = 0
|
|
||||||
windows = 1
|
|
||||||
platform_path = windows if sys.platform == "win32" else unix
|
|
||||||
|
|
||||||
|
|
||||||
def format_os_path(path, mode=Path.unix):
|
|
||||||
"""
|
|
||||||
Format path to use consistent, platform specific
|
|
||||||
separators. Absolute paths are converted between
|
|
||||||
drive letters and a prepended '/' as per platform
|
|
||||||
requirement.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
path (str): the path to be normalized, must be a string
|
|
||||||
or expose the replace method.
|
|
||||||
mode (Path): the path filesperator 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):
|
|
||||||
return format_os_path(path, mode=Path.unix)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_windows_path(path):
|
|
||||||
return format_os_path(path, mode=Path.windows)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_platform_path(path):
|
|
||||||
return format_os_path(path, mode=Path.platform_path)
|
|
||||||
|
|
||||||
|
|
||||||
def substitute_config_variables(path):
|
def substitute_config_variables(path):
|
||||||
"""Substitute placeholders into paths.
|
"""Substitute placeholders into paths.
|
||||||
|
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
# 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)
|
|
||||||
|
|
||||||
|
|
||||||
def comma_list(sequence, article=""):
|
|
||||||
if type(sequence) is not list:
|
|
||||||
sequence = list(sequence)
|
|
||||||
|
|
||||||
if not sequence:
|
|
||||||
return
|
|
||||||
elif len(sequence) == 1:
|
|
||||||
return sequence[0]
|
|
||||||
else:
|
|
||||||
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):
|
|
||||||
return comma_list(sequence, "or")
|
|
||||||
|
|
||||||
|
|
||||||
def comma_and(sequence):
|
|
||||||
return comma_list(sequence, "and")
|
|
||||||
|
|
||||||
|
|
||||||
def quote(sequence, q="'"):
|
|
||||||
return ["%s%s%s" % (q, e, q) for e in sequence]
|
|
||||||
|
|
||||||
|
|
||||||
def plural(n, singular, plural=None, show_n=True):
|
|
||||||
"""Pluralize <singular> word by adding an s if n != 1.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
n (int): number of things there are
|
|
||||||
singular (str): singular form of word
|
|
||||||
plural (str or None): optional plural form, for when it's not just
|
|
||||||
singular + 's'
|
|
||||||
show_n (bool): whether to include n in the result string (default True)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
(str): "1 thing" if n == 1 or "n things" if n != 1
|
|
||||||
"""
|
|
||||||
number = "%s " % n if show_n else ""
|
|
||||||
if n == 1:
|
|
||||||
return "%s%s" % (number, singular)
|
|
||||||
elif plural is not None:
|
|
||||||
return "%s%s" % (number, plural)
|
|
||||||
else:
|
|
||||||
return "%s%ss" % (number, singular)
|
|
@ -14,7 +14,9 @@
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
from spack.util.path import convert_to_posix_path, sanitize_filename
|
from llnl.path import convert_to_posix_path
|
||||||
|
|
||||||
|
from spack.util.path import sanitize_filename
|
||||||
|
|
||||||
|
|
||||||
def validate_scheme(scheme):
|
def validate_scheme(scheme):
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
|
|
||||||
import llnl.util.lang as lang
|
import llnl.util.lang as lang
|
||||||
import llnl.util.tty.color
|
import llnl.util.tty.color
|
||||||
|
from llnl.string import comma_or
|
||||||
|
|
||||||
import spack.directives
|
import spack.directives
|
||||||
import spack.error as error
|
import spack.error as error
|
||||||
from spack.util.string import comma_or
|
|
||||||
|
|
||||||
special_variant_values = [None, "none", "*"]
|
special_variant_values = [None, "none", "*"]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user