Pip: Add 'remove' command as an alias to 'uninstall'

Created on 25 Apr 2020  路  18Comments  路  Source: pypa/pip

What's the problem this feature will solve?

As a software developer and package maintainer, I often install and remove software packages with different package managers. With pip I often run into this error:

$ pip3 remove pdfarranger
ERROR: unknown command "remove"

Describe the solution you'd like

Have a remove command that does exactly the same as uninstall.

Alternative Solutions

  • Using bash's alias command is not easily possible because it can only match the main command (I can add an alias for pip in general but not for pip remove.)
  • pip does not seem to provide a built in alias command like dnf has

Additional context

Package managers which use remove and don't know uninstall:

  1. dnf/yum (they do have erase as an alias)
  2. apt(-get)
  3. pacman

Package managers which have both:

  1. flatpak
UX UX - ecosystem research epic UX - functionality research epic feature request

Most helpful comment

I've added this issue to the UX team's research list.

Under #8516 I will conduct a command audit and find out what commands users currently struggle to remember.
Under #8515 @ei8fdb will compare pip's commands against other packaging tools.

All 18 comments

Sounds reasonable. I鈥檇 suggest not listing the alias in help to avoid clutter, but having the alias itself is fine.

How do we add an alias to a command? I am not sure about it.

IMHO it's as trivial as

diff --git a/src/pip/_internal/commands/__init__.py b/src/pip/_internal/commands/__init__.py
index 6825fa6e..defcf967 100644
--- a/src/pip/_internal/commands/__init__.py
+++ b/src/pip/_internal/commands/__init__.py
@@ -44,6 +44,10 @@ commands_dict = OrderedDict([
         'pip._internal.commands.uninstall', 'UninstallCommand',
         'Uninstall packages.',
     )),
+    ('remove', CommandInfo(
+        'pip._internal.commands.uninstall', 'UninstallCommand',
+        'Uninstall packages.',
+    )),
     ('freeze', CommandInfo(
         'pip._internal.commands.freeze', 'FreezeCommand',
         'Output installed packages in requirements format.',

I'm trying to work on this because I thought it's easy but had to come here to ask about testing strategy :smile:

Thanks, @McSinyx! Please go ahead, no issue :)

As for testing, the approach I can think of is to parametrize the tests which involve uninstall command with remove command as well. IMO, this would test all the cases involve with uninstall command.

parametrize the tests which involve uninstall command with remove command as well

That's brilliant (kudos to pytest devs for making little convenient things like this possible too). I'm a bit worried about redundancy but first class command may need first class supports.

IMHO it's as trivial as

This might cause remove to be listed in pip help, which I think we want to avoid as per https://github.com/pypa/pip/issues/8130#issuecomment-619339279

$ pip help

Usage:   
  pip <command> [options]

Commands:
  install                     Install packages.
  download                    Download packages.
  uninstall                   Uninstall packages.
  remove                      Uninstall packages.
  freeze                      Output installed packages in requirements format.
  list                        List installed packages.
  show                        Show information about installed packages.
  check                       Verify installed packages have compatible dependencies.
  config                      Manage local and global configuration.
  search                      Search PyPI for packages.
  cache                       Inspect and manage pip's wheel cache.
  wheel                       Build wheels from your requirements.
  hash                        Compute hashes of package archives.
  completion                  A helper command used for command completion.
  debug                       Show information useful for debugging.
  help                        Show help for commands.

I was thinking along the lines of implementing get_alias_commands which looks for a mapping between alias and it's commands (in this case it will be remove -> install) and call it at https://github.com/pypa/pip/blob/master/src/pip/_internal/cli/main_parser.py#L87

@deveshks, thank you for the heads up, that looks like a much better idea.

Personally I on board with the alias command or some similar settings. (like what git has)

I鈥檇 suggest not listing the alias in help to avoid clutter, but having the alias itself is fine.

I think if we're adding aliases, we definitely want them to be visible to the user via help. That said, we should avoid any amount of clutter that we can. So, I agree that just adding "remove" with the same CommandInfo as uninstall is not what we want to do here.

I'd say, we should aim to have a uniform and proper way to provide aliases to users, allowing for multiple aliases for multiple commands, such that it's not difficult to add another alias for a command (similar to how adding a command is a matter of manipulating a single list).

Of course, this means we'd have to get a bit more fancy with how our command parser works, figure out how help should know about the command names and all that... but I much prefer that to something more "bolted on" / invisible to the users.

I'd imagine aliases could look like this:

$ pip help

Usage:   
  pip <command> [options]

Commands:
  i, add, install             Install packages.
  download                    Download packages.
  u, rm, remove, uninstall    Uninstall packages.
  ...

Or... we could do what npm does in their docs/help:

npm install (with no args, in package dir)
[snip, bunch more examples]

aliases: npm i, npm add
common options: [snip]

Even though I'm using them in the example above... I'm a -1 on adding a "remove" alias for uninstall, without a counterpart "add" alias for install and... I'm a -0 on adding them both together. I don't know how useful this, on it's own, would be to the general audience of pip users. I'm personally a lot more interested in adding shorthands for "most common commands", like I hinted at in the example above, which would be similar to npm i.

All that said, there's also the risk of "too many ways to do the same thing", which... is really the last thing I'd want to start messing about with pip's CLI today. I think we should wait until we move away from optparse to something else (see #4659), where it's easier to do these things properly without adding significantly more technical debt / complexity to the fairly complex/fragile CLI setup we have today.

As for testing, the approach I can think of is to parametrize the tests which involve uninstall command with remove command as well.

Doubling the number of tests (which doubles test execution times) is not a good idea. I'd much rather have a unit test that mocks out the actual implementation of the commands, and checks that various aliases hit the same Command.run block.

That's 1 extra unit test to run after adding an alias, which is significantly better than running 30+ tests w/ subprocesses and virtual environments being created for isolating them.

I'm also somewhat concerned about the precedent this sets. I'm sure there are plenty of other places where pip's commands don't quite match those of other package managers. Should we add aliases for all of those? Why aren't other package managers adding aliases to match pip's commands?

A user-defined alias mechanism like git has might be useful, but explicitly adding code to pip for each alias that someone feels would be useful isn't scalable (or IMO advisable).

Thank you @pradyunsg and @pfmoore for your thoughtful comments. Given no(t yet any) consensus on the execution, I'll mark my PR as draft until I figured out a clean way to resolve your requests al(most)together, to avoid discussion without concrete code for reference.

Edit: to add to the concerns above, personally I'm neither certain about the representation of subcommand docs, e.g. for uninstall should the usage section be

docs mockups

Usage:   
  pip uninstall [options] <package> ...
  pip uninstall [options] -r <requirements file> ...
  pip remove [options] <package> ...
  pip remove [options] -r <requirements file> ...

or

Usage:   
  pip uninstall [options] <package> ...
  pip remove [options] <package> ...
  pip uninstall [options] -r <requirements file> ...
  pip remove [options] -r <requirements file> ...

or

Usage:   
  pip {uninstall,remove} [options] <package> ...
  pip {uninstall,remove} [options] -r <requirements file> ...

Neither looks visually pleasing to me personally.

the representation of subcommand docs

Usage:   
  pip uninstall [options] <package> ...
  pip uninstall [options] -r <requirements file> ...

Aliases: pip remove

@pradyunsg, could you please see if the behavior in GH-8137 fits your vision. I'd be happy with your new suggestion though, just wanna make sure you've seen it.

Thank you all for your comments and especially @McSinyx for the PR. I like the proposed behavior and it would make my life (and probably others' too) a bit easier by not having to spend brain cycles to figure out whether you need to type uninstall or remove. Is there anything else I can do to get this merged and shipped?

I just searched for this issue after using remove and add for the umpteenth time :)

I'd like to propose making the following changes

  • install == add
  • uninstall == remove (already part of PR obviously)
  • search == find

Any other commands that people mis-remember and use?

The search/find which is undergo a UX refactor which group everything together (I think @pradyunsg may explain this better to you, he's been planning it for a while now), but the other two are included in my linked PR (GH-8137).

I've added this issue to the UX team's research list.

Under #8516 I will conduct a command audit and find out what commands users currently struggle to remember.
Under #8515 @ei8fdb will compare pip's commands against other packaging tools.

Was this page helpful?
0 / 5 - 0 ratings