Skip to content

Commit

Permalink
add --help-all
Browse files Browse the repository at this point in the history
  • Loading branch information
mjurbanski-reef committed Nov 23, 2023
1 parent 31f6878 commit 6ac76e8
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 9 deletions.
48 changes: 44 additions & 4 deletions b2/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import re
import sys
import textwrap
import unittest.mock

import arrow
from b2sdk.v2 import RetentionPeriod
Expand All @@ -30,6 +31,10 @@ class B2RawTextHelpFormatter(argparse.RawTextHelpFormatter):
It removes default "usage: " text and prints usage for all (non-hidden) subcommands.
"""

def __init__(self, *args, show_all: bool = False, **kwargs):
super().__init__(*args, **kwargs)
self.show_all = show_all

def add_usage(self, usage, actions, groups, prefix=None):
if prefix is None:
prefix = ''
Expand All @@ -39,7 +44,11 @@ def add_argument(self, action):
if isinstance(action, argparse._SubParsersAction) and action.help is not argparse.SUPPRESS:
usages = []
for choice in self._unique_choice_values(action):
if not getattr(choice, 'hidden', False):
deprecated = getattr(choice, 'deprecated', False)
if deprecated:
if self.show_all:
usages.append(f'(DEPRECATED) {choice.format_usage()}')
else:
usages.append(choice.format_usage())
self.add_text(''.join(usages))
else:
Expand All @@ -54,6 +63,14 @@ def _unique_choice_values(cls, action):
yield value


class _HelpAllAction(argparse._HelpAction):
"""Like argparse._HelpAction but prints help for all subcommands (even deprecated ones)."""

def __call__(self, parser, namespace, values, option_string=None):
parser.print_help(show_all=True)
parser.exit()


class B2ArgumentParser(argparse.ArgumentParser):
"""
CLI custom parser.
Expand All @@ -62,18 +79,32 @@ class B2ArgumentParser(argparse.ArgumentParser):
and use help message in case of error.
"""

def __init__(self, *args, for_docs: bool = False, hidden: bool = False, **kwargs):
def __init__(
self,
*args,
add_help_all: bool = True,
for_docs: bool = False,
deprecated: bool = False,
**kwargs
):
"""
:param for_docs: is this parser used for generating docs
:param hidden: should this parser be hidden from `--help`
:param deprecated: is this option deprecated
"""
self._raw_description = None
self._description = None
self._for_docs = for_docs
self.hidden = hidden
self.deprecated = deprecated
kwargs.setdefault('formatter_class', B2RawTextHelpFormatter)
super().__init__(*args, **kwargs)
if add_help_all:
self.register('action', 'help_all', _HelpAllAction)
self.add_argument(
'--help-all',
help='show help for all options, including deprecated ones',
action='help_all',
)

@property
def description(self):
Expand Down Expand Up @@ -113,6 +144,15 @@ def _get_encoding(cls):
# locales are improperly configured
return 'ascii'

def print_help(self, *args, show_all: bool = False, **kwargs):
"""
Print help message.
"""
with unittest.mock.patch.object(
self, 'formatter_class', functools.partial(B2RawTextHelpFormatter, show_all=show_all)
):
super().print_help(*args, **kwargs)


def parse_comma_separated_list(s):
"""
Expand Down
11 changes: 6 additions & 5 deletions b2/console_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ class Command(Described):
# Set to True for commands that receive sensitive information in arguments
FORBID_LOGGING_ARGUMENTS = False

hide_from_help = False
deprecated = False

# The registry for the subcommands, should be reinitialized in subclass
subcommands_registry = None
Expand Down Expand Up @@ -676,16 +676,17 @@ def create_parser(
description=description,
parents=parents,
for_docs=for_docs,
hidden=cls.hide_from_help,
deprecated=cls.deprecated,
)

if subparsers is None:
parser = B2ArgumentParser(**parser_kwargs,)
parser = B2ArgumentParser(**parser_kwargs)
else:
parser = subparsers.add_parser(
parser_kwargs.pop('prog'),
**parser_kwargs,
aliases=[alias] if alias is not None and not for_docs else (),
add_help_all=False,
)
# Register class that will handle this particular command, for both name and alias.
parser.set_defaults(command_class=cls)
Expand All @@ -694,7 +695,7 @@ def create_parser(

if cls.subcommands_registry:
if not parents:
common_parser = B2ArgumentParser(add_help=False)
common_parser = B2ArgumentParser(add_help=False, add_help_all=False)
common_parser.add_argument(
'--debugLogs', action='store_true', help=argparse.SUPPRESS
)
Expand Down Expand Up @@ -799,7 +800,7 @@ def __str__(self):


class CmdReplacedByMixin:
hide_from_help = True
deprecated = True
replaced_by_cmd: "type[Command]"

def run(self, args):
Expand Down
43 changes: 43 additions & 0 deletions test/unit/console_tool/test_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
######################################################################
#
# File: test/unit/console_tool/test_help.py
#
# Copyright 2023 Backblaze Inc. All Rights Reserved.
#
# License https://www.backblaze.com/using_b2_code.html
#
######################################################################
import pytest


@pytest.mark.parametrize(
"flag, included, excluded",
[
# --help shouldn't show deprecated commands
(
"--help",
[" b2 download-file ", "-h", "--help-all"],
[" download-file-by-name ", "(DEPRECATED)"],
),
# --help-all should show deprecated commands, but marked as deprecated
(
"--help-all",
["(DEPRECATED) b2 download-file-by-name ", "-h", "--help-all"],
[],
),
],
)
def test_help(b2_cli, flag, included, excluded, capsys):
b2_cli.run([flag], expected_stdout=None)

out = capsys.readouterr().out

found = set()
for i in included:
if i in out:
found.add(i)
for e in excluded:
if e in out:
found.add(e)
assert found.issuperset(included), f"expected {included!r} in {out!r}"
assert found.isdisjoint(excluded), f"expected {excluded!r} not in {out!r}"

0 comments on commit 6ac76e8

Please sign in to comment.