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

FEAT: The PIP command alias feature #12828

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
1 change: 1 addition & 0 deletions news/12828.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New command alias functionality based on existing commands.
41 changes: 31 additions & 10 deletions src/pip/_internal/cli/main_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
from pip._internal.build_env import get_runnable_pip
from pip._internal.cli import cmdoptions
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
from pip._internal.commands import commands_dict, get_similar_commands
from pip._internal.commands import (
commands_aliases,
commands_dict,
find_command_by_alias,
get_similar_commands,
)
from pip._internal.exceptions import CommandError
from pip._internal.utils.misc import get_pip_version, get_prog

Expand Down Expand Up @@ -38,10 +43,21 @@ def create_main_parser() -> ConfigOptionParser:
parser.main = True # type: ignore

# create command listing for description
description = [""] + [
f"{name:27} {command_info.summary}"
for name, command_info in commands_dict.items()
]
cmds_listings: List[str] = []
padding = 27
for name, command_info in commands_dict.items():
cmd_aliases = commands_aliases.get(name, [])
if not cmd_aliases:
cmds_listings.append(f"{name:{padding}} {command_info.summary}")
continue
display_aliases = ", ".join(cmd_aliases)
padding_adjusted = padding - len(display_aliases) - 3
cmds_listings.append(
f"{display_aliases}, {name:{padding_adjusted}} {command_info.summary}"
)

description = [""] + cmds_listings

parser.description = "\n".join(description)

return parser
Expand Down Expand Up @@ -119,13 +135,18 @@ def parse_command(args: List[str]) -> Tuple[str, List[str]]:
cmd_name = args_else[0]

if cmd_name not in commands_dict:
guess = get_similar_commands(cmd_name)
alias_command = find_command_by_alias(cmd_name)

if not alias_command:
guess = get_similar_commands(cmd_name)

msg = [f'unknown command "{cmd_name}"']
if guess:
msg.append(f'maybe you meant "{guess}"')
msg = [f'unknown command "{cmd_name}"']
if guess:
msg.append(f'maybe you meant "{guess}"')

raise CommandError(" - ".join(msg))
raise CommandError(" - ".join(msg))
cmd_name = alias_command
args[0] = cmd_name # replace with the full command name.. ex: i -> install

# all the args without the subcommand
cmd_args = args[:]
Expand Down
23 changes: 22 additions & 1 deletion src/pip/_internal/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

import importlib
from collections import namedtuple
from typing import Any, Dict, Optional
from itertools import chain
from typing import Any, Dict, List, Optional

from pip._internal.cli.base_command import Command

Expand Down Expand Up @@ -105,6 +106,21 @@
),
}

# This dict lists real command aliases from `commands_dict`
commands_aliases: Dict[str, List[str]] = {
"install": ["i", "add"],
"uninstall": ["u", "rm", "remove"],
"list": ["ls"],
}


def find_command_by_alias(alias: str) -> Optional[str]:
"""Find command name by alias"""
for command, aliases in commands_aliases.items():
if alias in aliases:
return command
return None


def create_command(name: str, **kwargs: Any) -> Command:
"""
Expand All @@ -125,8 +141,13 @@ def get_similar_commands(name: str) -> Optional[str]:
name = name.lower()

close_commands = get_close_matches(name, commands_dict.keys())
close_aliases = get_close_matches(
name, chain.from_iterable(commands_aliases.values())
)

if close_commands:
return close_commands[0]
elif close_aliases:
return close_aliases[0]
else:
return None
16 changes: 11 additions & 5 deletions src/pip/_internal/commands/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def run(self, options: Values, args: List[str]) -> int:
from pip._internal.commands import (
commands_dict,
create_command,
find_command_by_alias,
get_similar_commands,
)

Expand All @@ -27,13 +28,18 @@ def run(self, options: Values, args: List[str]) -> int:
return SUCCESS

if cmd_name not in commands_dict:
guess = get_similar_commands(cmd_name)
alias_command = find_command_by_alias(cmd_name)

msg = [f'unknown command "{cmd_name}"']
if guess:
msg.append(f'maybe you meant "{guess}"')
if not alias_command:
guess = get_similar_commands(cmd_name)

raise CommandError(" - ".join(msg))
msg = [f'unknown command "{cmd_name}"']
if guess:
msg.append(f'maybe you meant "{guess}"')

raise CommandError(" - ".join(msg))
cmd_name = alias_command
args[0] = cmd_name

command = create_command(cmd_name)
command.parser.print_help()
Expand Down
Loading