summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/cmd/edit.py119
-rw-r--r--lib/spack/spack/test/cmd/edit.py46
-rwxr-xr-xshare/spack/spack-completion.fish2
3 files changed, 105 insertions, 62 deletions
diff --git a/lib/spack/spack/cmd/edit.py b/lib/spack/spack/cmd/edit.py
index 0134a522d8..e7d64c77ea 100644
--- a/lib/spack/spack/cmd/edit.py
+++ b/lib/spack/spack/cmd/edit.py
@@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import errno
import glob
import os
@@ -11,43 +12,13 @@ import llnl.util.tty as tty
import spack.cmd
import spack.paths
import spack.repo
-from spack.spec import Spec
-from spack.util.editor import editor
+import spack.util.editor
description = "open package files in $EDITOR"
section = "packaging"
level = "short"
-def edit_package(name, repo_path, namespace):
- """Opens the requested package file in your favorite $EDITOR.
-
- Args:
- name (str): The name of the package
- repo_path (str): The path to the repository containing this package
- namespace (str): A valid namespace registered with Spack
- """
- # Find the location of the package
- if repo_path:
- repo = spack.repo.Repo(repo_path)
- elif namespace:
- repo = spack.repo.PATH.get_repo(namespace)
- else:
- repo = spack.repo.PATH
- path = repo.filename_for_package_name(name)
-
- spec = Spec(name)
- if os.path.exists(path):
- if not os.path.isfile(path):
- tty.die("Something is wrong. '{0}' is not a file!".format(path))
- if not os.access(path, os.R_OK):
- tty.die("Insufficient permissions on '%s'!" % path)
- else:
- raise spack.repo.UnknownPackageError(spec.name)
-
- editor(path)
-
-
def setup_parser(subparser):
excl_args = subparser.add_mutually_exclusive_group()
@@ -98,41 +69,67 @@ def setup_parser(subparser):
excl_args.add_argument("-r", "--repo", default=None, help="path to repo to edit package in")
excl_args.add_argument("-N", "--namespace", default=None, help="namespace of package to edit")
- subparser.add_argument("package", nargs="?", default=None, help="package name")
+ subparser.add_argument("package", nargs="*", default=None, help="package name")
-def edit(parser, args):
- name = args.package
+def locate_package(name: str, repo: spack.repo.Repo) -> str:
+ path = repo.filename_for_package_name(name)
- # By default, edit package files
- path = spack.paths.packages_path
+ try:
+ with open(path, "r"):
+ return path
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ raise spack.repo.UnknownPackageError(name) from e
+ tty.die(f"Cannot edit package: {e}")
+
+
+def locate_file(name: str, path: str) -> str:
+ # convert command names to python module name
+ if path == spack.paths.command_path:
+ name = spack.cmd.python_name(name)
+
+ file_path = os.path.join(path, name)
+
+ # Try to open direct match.
+ try:
+ with open(file_path, "r"):
+ return file_path
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ tty.die(f"Cannot edit file: {e}")
+ pass
+
+ # Otherwise try to find a file that starts with the name
+ candidates = glob.glob(file_path + "*")
+ exclude_list = [".pyc", "~"] # exclude binaries and backups
+ files = [f for f in candidates if not any(f.endswith(ext) for ext in exclude_list)]
+ if len(files) > 1:
+ tty.die(
+ f"Multiple files start with `{name}`:\n"
+ + "\n".join(f" {os.path.basename(f)}" for f in files)
+ )
+ elif not files:
+ tty.die(f"No file for '{name}' was found in {path}")
+ return files[0]
+
+
+def edit(parser, args):
+ names = args.package
# If `--command`, `--test`, or `--module` is chosen, edit those instead
if args.path:
- path = args.path
- if name:
- # convert command names to python module name
- if path == spack.paths.command_path:
- name = spack.cmd.python_name(name)
-
- path = os.path.join(path, name)
- if not os.path.exists(path):
- files = glob.glob(path + "*")
- exclude_list = [".pyc", "~"] # exclude binaries and backups
- files = list(filter(lambda x: all(s not in x for s in exclude_list), files))
- if len(files) > 1:
- m = "Multiple files exist with the name {0}.".format(name)
- m += " Please specify a suffix. Files are:\n\n"
- for f in files:
- m += " " + os.path.basename(f) + "\n"
- tty.die(m)
- if not files:
- tty.die("No file for '{0}' was found in {1}".format(name, path))
- path = files[0] # already confirmed only one entry in files
-
- editor(path)
- elif name:
- edit_package(name, args.repo, args.namespace)
+ paths = [locate_file(name, args.path) for name in names] if names else [args.path]
+ spack.util.editor.editor(*paths)
+ elif names:
+ if args.repo:
+ repo = spack.repo.Repo(args.repo)
+ elif args.namespace:
+ repo = spack.repo.PATH.get_repo(args.namespace)
+ else:
+ repo = spack.repo.PATH
+ paths = [locate_package(name, repo) for name in names]
+ spack.util.editor.editor(*paths)
else:
# By default open the directory where packages live
- editor(path)
+ spack.util.editor.editor(spack.paths.packages_path)
diff --git a/lib/spack/spack/test/cmd/edit.py b/lib/spack/spack/test/cmd/edit.py
new file mode 100644
index 0000000000..dc367d2537
--- /dev/null
+++ b/lib/spack/spack/test/cmd/edit.py
@@ -0,0 +1,46 @@
+# Copyright 2013-2024 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 os
+
+import spack.paths
+import spack.repo
+import spack.util.editor
+from spack.build_systems import autotools, cmake
+from spack.main import SpackCommand
+
+edit = SpackCommand("edit")
+
+
+def test_edit_packages(monkeypatch, mock_packages: spack.repo.RepoPath):
+ """Test spack edit a b"""
+ path_a = mock_packages.filename_for_package_name("a")
+ path_b = mock_packages.filename_for_package_name("b")
+ called = False
+
+ def editor(*args: str, **kwargs):
+ nonlocal called
+ called = True
+ assert args[0] == path_a
+ assert args[1] == path_b
+
+ monkeypatch.setattr(spack.util.editor, "editor", editor)
+ edit("a", "b")
+ assert called
+
+
+def test_edit_files(monkeypatch):
+ """Test spack edit --build-system autotools cmake"""
+ called = False
+
+ def editor(*args: str, **kwargs):
+ nonlocal called
+ called = True
+ assert os.path.samefile(args[0], autotools.__file__)
+ assert os.path.samefile(args[1], cmake.__file__)
+
+ monkeypatch.setattr(spack.util.editor, "editor", editor)
+ edit("--build-system", "autotools", "cmake")
+ assert called
diff --git a/share/spack/spack-completion.fish b/share/spack/spack-completion.fish
index 63abb4864e..4239faddc8 100755
--- a/share/spack/spack-completion.fish
+++ b/share/spack/spack-completion.fish
@@ -1451,7 +1451,7 @@ complete -c spack -n '__fish_spack_using_command docs' -s h -l help -d 'show thi
# spack edit
set -g __fish_spack_optspecs_spack_edit h/help b/build-system c/command d/docs t/test m/module r/repo= N/namespace=
-complete -c spack -n '__fish_spack_using_command_pos 0 edit' -f -a '(__fish_spack_packages)'
+complete -c spack -n '__fish_spack_using_command_pos_remainder 0 edit' -f -a '(__fish_spack_packages)'
complete -c spack -n '__fish_spack_using_command edit' -s h -l help -f -a help
complete -c spack -n '__fish_spack_using_command edit' -s h -l help -d 'show this help message and exit'
complete -c spack -n '__fish_spack_using_command edit' -s b -l build-system -f -a path