Warn user borg list warns needs name, unless they meant repo-list

This commit is contained in:
John C. McCabe-Dansted 2026-05-18 19:05:39 +12:00
parent 317713378f
commit b3a573a1f0
2 changed files with 68 additions and 3 deletions

View file

@ -309,7 +309,7 @@ class Archiver(
return parser
@staticmethod
def _first_toplevel_command_index(args, parser):
def _first_positional_index(args, parser):
option_actions = {}
for action in parser._actions:
for option_string in getattr(action, "option_strings", ()):
@ -319,7 +319,7 @@ class Archiver(
while i < len(args):
token = args[i]
if token == "--":
return None
return i + 1 if i + 1 < len(args) else len(args)
if not token.startswith("-") or token == "-":
return i
@ -344,7 +344,14 @@ class Archiver(
else:
i += 2
return None
return len(args)
@staticmethod
def _first_toplevel_command_index(args, parser):
index = Archiver._first_positional_index(args, parser)
if index is None or index == len(args):
return None
return index
def _legacy_command_hint(self, args, parser):
command_index = self._first_toplevel_command_index(args, parser)
@ -422,6 +429,34 @@ class Archiver(
]
)
def _missing_list_name_hint(self, args, parser):
command_index = self._first_toplevel_command_index(args, parser)
if command_index is None or args[command_index] != "list":
return None
commands = getattr(parser, "_subcommands_action", None)
commands = commands._name_parser_map if commands else {}
list_parser = commands.get("list")
if list_parser is None:
return None
subcommand_args = args[command_index + 1 :]
positional_index = self._first_positional_index(subcommand_args, list_parser)
if positional_index is None or positional_index != len(subcommand_args):
return None
prog = self.prog or "borg"
repo_value = self._option_value(args, ("-r", "--repo")) or "REPO"
repo_list_command = shlex.join([prog, "-r", repo_value, "repo-list"])
return "\n".join(
[
"borg list NAME lists contents of an archive and needs an archive NAME.",
"If you meant to list archives in a repository, use repo-list:",
repo_list_command,
f"tip: For details of accepted options run: {prog} list --help",
]
)
def get_args(self, argv, cmd):
"""Usually just returns argv, except when dealing with an SSH forced command for borg serve."""
result = self.parse_args(argv[1:])
@ -469,6 +504,9 @@ class Archiver(
legacy_hint = self._legacy_repo_archive_hint(args, parser)
if legacy_hint:
parser.exit(EXIT_ERROR, legacy_hint + "\n")
legacy_hint = self._missing_list_name_hint(args, parser)
if legacy_hint:
parser.exit(EXIT_ERROR, legacy_hint + "\n")
args = parser.parse_args(args or ["-h"])
args = flatten_namespace(args)

View file

@ -121,6 +121,33 @@ def test_borg1_repo_archive_in_repo_shows_borg2_forms_when_repo_is_after_command
assert "borg list ::test1" in output
def test_list_without_name_suggests_repo_list(archiver):
ret, output = exec_cmd("list", archiver=archiver.archiver, fork=archiver.FORK_DEFAULT, exe=archiver.EXE)
assert ret == 2
assert "borg list NAME lists contents of an archive and needs an archive NAME." in output
assert "If you meant to list archives in a repository, use repo-list:" in output
assert "borg -r REPO repo-list" in output
assert "tip: For details of accepted options run: borg list --help" in output
def test_list_without_name_with_repo_suggests_repo_list(archiver):
ret, output = exec_cmd(
"--repo",
archiver.repository_location,
"list",
archiver=archiver.archiver,
fork=archiver.FORK_DEFAULT,
exe=archiver.EXE,
)
assert ret == 2
assert "borg list NAME lists contents of an archive and needs an archive NAME." in output
assert "If you meant to list archives in a repository, use repo-list:" in output
assert f"borg -r {archiver.repository_location} repo-list" in output
assert "tip: For details of accepted options run: borg list --help" in output
@pytest.mark.parametrize("command, parser", list(get_all_parsers().items()))
def test_help_formatting(command, parser):
if isinstance(parser.epilog, RstToTextLazy):