Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #1535 - Add a help command (alias of --help) #1544

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/1535.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`pipx help COMMAND` can now be used as an alias for `pipx COMMAND --help`,
where COMMAND is a valid pipx command, or nothing.
1 change: 1 addition & 0 deletions src/pipx/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@
"pin",
"unpin",
"upgrade_interpreters",
"help",
]
2 changes: 2 additions & 0 deletions src/pipx/commands/help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The help command only reuses existing features
# This file exists to satisfy __all__ in pipx/commands/__init__.py
24 changes: 23 additions & 1 deletion src/pipx/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar
return ExitCode(0)
elif args.command == "environment":
return commands.environment(value=args.value)
elif args.command == "help":
for i in range(2, len(sys.argv)):
sys.argv[i - 1] = sys.argv[i]
sys.argv[len(sys.argv) - 1] = "--help"
return cli()
else:
raise PipxError(f"Unknown command {args.command}")

Expand Down Expand Up @@ -928,6 +933,20 @@ def _add_environment(subparsers: argparse._SubParsersAction, shared_parser: argp
p.add_argument("--value", "-V", metavar="VARIABLE", help="Print the value of the variable.")


def _add_help(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:
p = subparsers.add_parser(
"help",
help="Print out help info for pipx or for a particular command (alias of --help).",
description="Print out help info for pipx or for a particular command (alias of --help).",
parents=[shared_parser],
)
p.add_argument(
"subcommands",
nargs="*",
help="Print out help for the specified command, if provided, using pipx help COMMAND",
)


def get_command_parser() -> Tuple[argparse.ArgumentParser, Dict[str, argparse.ArgumentParser]]:
venv_container = VenvContainer(paths.ctx.venvs)

Expand Down Expand Up @@ -973,7 +992,9 @@ def get_command_parser() -> Tuple[argparse.ArgumentParser, Dict[str, argparse.Ar
)
parser.man_short_description = PIPX_DESCRIPTION.splitlines()[1] # type: ignore[attr-defined]

subparsers = parser.add_subparsers(dest="command", description="Get help for commands with pipx COMMAND --help")
subparsers = parser.add_subparsers(
dest="command", description="Get help for commands with pipx COMMAND --help or pipx help COMMAND"
)

subparsers_with_subcommands = {}
_add_install(subparsers, shared_parser)
Expand All @@ -995,6 +1016,7 @@ def get_command_parser() -> Tuple[argparse.ArgumentParser, Dict[str, argparse.Ar
_add_runpip(subparsers, completer_venvs.use, shared_parser)
_add_ensurepath(subparsers, shared_parser)
_add_environment(subparsers, shared_parser)
_add_help(subparsers, shared_parser)

parser.add_argument("--version", action="store_true", help="Print version and exit")
subparsers.add_parser(
Expand Down
120 changes: 120 additions & 0 deletions tests/test_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from helpers import run_pipx_cli
from pipx.main import get_command_parser


def test_help(capsys):
try:
run_pipx_cli(["--help"])
except SystemExit:
flag_capture = capsys.readouterr()
assert "usage: pipx [-h]" in flag_capture.out

try:
run_pipx_cli(["help"])
except SystemExit:
command_capture = capsys.readouterr()
assert "usage: pipx [-h]" in command_capture.out

assert flag_capture == command_capture


def test_help_with_subcommands(capsys):
parser, _ = get_command_parser()
# The list of valid pipx commands was generated using the following line
# valid_commands = parser._subparsers._actions[4].choices.keys() # First four actions contain None
valid_commands = [
"install",
"install-all",
"uninject",
"inject",
"pin",
"unpin",
"upgrade",
"upgrade-all",
"upgrade-shared",
"uninstall",
"uninstall-all",
"reinstall",
"reinstall-all",
"list",
"interpreter",
"run",
"runpip",
"ensurepath",
"environment",
"help",
"completions",
]

for command in valid_commands:
try:
run_pipx_cli([command, "--help"])
except SystemExit:
flag_capture = capsys.readouterr()
assert "usage: pipx " + command in flag_capture.out

try:
run_pipx_cli(["help", command])
except SystemExit:
command_capture = capsys.readouterr()
assert "usage: pipx " + command in flag_capture.out

assert flag_capture == command_capture


def test_help_with_multiple_subcommands(capsys):
try:
run_pipx_cli(["install", "cowsaypy", "black", "--help"])
except SystemExit:
flag_capture = capsys.readouterr()
assert "usage: pipx install" in flag_capture.out

try:
run_pipx_cli(["help", "install", "cowsaypy", "black"])
except SystemExit:
command_capture = capsys.readouterr()
assert "usage: pipx install" in flag_capture.out

assert flag_capture == command_capture

try:
run_pipx_cli(["interpreter", "list", "--help"])
except SystemExit:
flag_capture = capsys.readouterr()
assert "usage: pipx interpreter list" in flag_capture.out

try:
run_pipx_cli(["help", "interpreter", "list"])
except SystemExit:
command_capture = capsys.readouterr()
assert "usage: pipx interpreter list" in flag_capture.out

assert flag_capture == command_capture

try:
run_pipx_cli(["interpreter", "prune", "--help"])
except SystemExit:
flag_capture = capsys.readouterr()
assert "usage: pipx interpreter prune" in flag_capture.out

try:
run_pipx_cli(["help", "interpreter", "prune"])
except SystemExit:
command_capture = capsys.readouterr()
assert "usage: pipx interpreter prune" in flag_capture.out

assert flag_capture == command_capture

try:
run_pipx_cli(["interpreter", "upgrade", "--help"])
except SystemExit:
flag_capture = capsys.readouterr()
assert "usage: pipx interpreter upgrade" in flag_capture.out

try:
run_pipx_cli(["help", "interpreter", "upgrade"])
except SystemExit:
command_capture = capsys.readouterr()
assert "usage: pipx interpreter upgrade" in flag_capture.out

assert flag_capture == command_capture