pypa / pip

The Python package installer
https://pip.pypa.io/
MIT License
9.51k stars 3.02k forks source link

Add aliases for commonly used commands #8130

Open dreua opened 4 years ago

dreua commented 4 years ago

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

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
uranusjr commented 4 years ago

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

gutsytechster commented 4 years ago

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

McSinyx commented 4 years ago

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:

gutsytechster commented 4 years ago

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.

McSinyx commented 4 years ago

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.

deveshks commented 4 years ago

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

McSinyx commented 4 years ago

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

cjc7373 commented 4 years ago

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

pradyunsg commented 4 years ago

I’d 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.

pradyunsg commented 4 years ago

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.

pfmoore commented 4 years ago

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).

McSinyx commented 4 years ago

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] ... pip uninstall [options] -r ... pip remove [options] ... pip remove [options] -r ... or Usage: pip uninstall [options] ... pip remove [options] ... pip uninstall [options] -r ... pip remove [options] -r ... or Usage: pip {uninstall,remove} [options] ... pip {uninstall,remove} [options] -r ...

Neither looks visually pleasing to me personally.

pradyunsg commented 4 years ago

the representation of subcommand docs

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

Aliases: pip remove
McSinyx commented 4 years ago

@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.

dreua commented 4 years ago

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?

wshayes commented 4 years ago

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

I'd like to propose making the following changes

Any other commands that people mis-remember and use?

McSinyx commented 4 years ago

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).

nlhkabu commented 4 years ago

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.

pradyunsg commented 2 years ago

OK, I'm on board for adding i as an alias for install and u as an alias for uninstall.

If someone wants to file a PR implementing this (based of off #8137, ideally), please feel welcome to. That isn't to say the PR would be guaranteed to merge; but it would help move us forward on this front! ^.^

luiisp commented 3 months ago

in response there is: https://github.com/pypa/pip/pull/12828#issuecomment-2248375174 (design)

I agree with the confusion about which commands are used by newer developers, but if we take this literally, no functionality would be released as one version would have access to the functionality and another would not.

but your comment is really useful and makes me think about the consequences of such a direct and visible implementation (being displayed with the --help command), taking this into account I thought we could implement this subtly in a gradual way and using just simple aliases ( i,u,ls) we would basically implement the aliases without displaying it in --help and after 4/5 years when most developers already have the version with support for aliases then yes we could display this

morotti commented 3 months ago

following the discussion, I think the most sensible way to go about this issue would be to implement a "remove" command... only to raise a useful error message that the remove command does not exist

# now in main
pip remove
ERROR: unknown command "remove"

# better
pip remove
ERROR: unknown command "pip remove", did you mean "pip uninstall"?

I think the issue is very specific to the uninstall command because it's only used once in a while. I myself can never remember if it's pip remove or pip delete, I try both and it's neither :D

pip install does not need any assistance IMO