Add flag to automatically remove unused dependencies, like pip-autoremove
Closed this issue · 6 comments
What's the problem this feature will solve?
Remove a package and its unused dependencies. Pip cannot do this.
Describe the solution you'd like
Automatically remove no-longer-used dependencies when uninstalling a package, via a flag like -r
or --autoremove
.
This would help declutter installations when removing packages with many dependencies, like jupyter-lab or tensorflow.
And it's easy to implement. pip-autoremove does it in <100 loc.
Alternative Solutions
The alternative is manually removing packages, or nuking the installation, which is not always possible.
Additional context
Example, an old jupyter-lab install's dependencies:
jupyterlab-server==1.0.7
- jinja2 [required: >=2.10, installed: 2.11.1]
- MarkupSafe [required: >=0.23, installed: 1.1.1]
- json5 [required: Any, installed: 0.9.4]
- jsonschema [required: >=3.0.1, installed: 3.2.0]
- attrs [required: >=17.4.0, installed: 19.3.0]
- importlib-metadata [required: Any, installed: 1.6.0]
- zipp [required: >=0.5, installed: 3.1.0]
- pyrsistent [required: >=0.14.0, installed: 0.16.0]
- six [required: Any, installed: 1.14.0]
- setuptools [required: Any, installed: 46.0.0]
- six [required: >=1.11.0, installed: 1.14.0]
- notebook [required: >=4.2.0, installed: 6.0.3]
- ipykernel [required: Any, installed: 5.2.0]
- appnope [required: Any, installed: 0.1.0]
- ipython [required: >=5.0.0, installed: 7.13.0]
- appnope [required: Any, installed: 0.1.0]
- backcall [required: Any, installed: 0.1.0]
- decorator [required: Any, installed: 4.4.2]
- jedi [required: >=0.10, installed: 0.16.0]
- parso [required: >=0.5.2, installed: 0.6.2]
- pexpect [required: Any, installed: 4.8.0]
- ptyprocess [required: >=0.5, installed: 0.6.0]
- pickleshare [required: Any, installed: 0.7.5]
- prompt-toolkit [required: >=2.0.0,<3.1.0,!=3.0.1,!=3.0.0, installed: 3.0.5]
- wcwidth [required: Any, installed: 0.1.9]
- pygments [required: Any, installed: 2.6.1]
- setuptools [required: >=18.5, installed: 46.0.0]
- traitlets [required: >=4.2, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- jupyter-client [required: Any, installed: 6.1.2]
- jupyter-core [required: >=4.6.0, installed: 4.6.3]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- python-dateutil [required: >=2.1, installed: 2.8.1]
- six [required: >=1.5, installed: 1.14.0]
- pyzmq [required: >=13, installed: 19.0.0]
- tornado [required: >=4.1, installed: 6.0.4]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- tornado [required: >=4.2, installed: 6.0.4]
- traitlets [required: >=4.1.0, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- ipython-genutils [required: Any, installed: 0.2.0]
- jinja2 [required: Any, installed: 2.11.1]
- MarkupSafe [required: >=0.23, installed: 1.1.1]
- jupyter-client [required: >=5.3.4, installed: 6.1.2]
- jupyter-core [required: >=4.6.0, installed: 4.6.3]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- python-dateutil [required: >=2.1, installed: 2.8.1]
- six [required: >=1.5, installed: 1.14.0]
- pyzmq [required: >=13, installed: 19.0.0]
- tornado [required: >=4.1, installed: 6.0.4]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- jupyter-core [required: >=4.6.1, installed: 4.6.3]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- nbconvert [required: Any, installed: 5.6.1]
- bleach [required: Any, installed: 3.1.4]
- six [required: >=1.9.0, installed: 1.14.0]
- webencodings [required: Any, installed: 0.5.1]
- defusedxml [required: Any, installed: 0.6.0]
- entrypoints [required: >=0.2.2, installed: 0.3]
- jinja2 [required: >=2.4, installed: 2.11.1]
- MarkupSafe [required: >=0.23, installed: 1.1.1]
- jupyter-core [required: Any, installed: 4.6.3]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- mistune [required: >=0.8.1,<2, installed: 0.8.4]
- nbformat [required: >=4.4, installed: 5.0.5]
- ipython-genutils [required: Any, installed: 0.2.0]
- jsonschema [required: >=2.4,!=2.5.0, installed: 3.2.0]
- attrs [required: >=17.4.0, installed: 19.3.0]
- importlib-metadata [required: Any, installed: 1.6.0]
- zipp [required: >=0.5, installed: 3.1.0]
- pyrsistent [required: >=0.14.0, installed: 0.16.0]
- six [required: Any, installed: 1.14.0]
- setuptools [required: Any, installed: 46.0.0]
- six [required: >=1.11.0, installed: 1.14.0]
- jupyter-core [required: Any, installed: 4.6.3]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- traitlets [required: >=4.1, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- pandocfilters [required: >=1.4.1, installed: 1.4.2]
- pygments [required: Any, installed: 2.6.1]
- testpath [required: Any, installed: 0.4.4]
- traitlets [required: >=4.2, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- nbformat [required: Any, installed: 5.0.5]
- ipython-genutils [required: Any, installed: 0.2.0]
- jsonschema [required: >=2.4,!=2.5.0, installed: 3.2.0]
- attrs [required: >=17.4.0, installed: 19.3.0]
- importlib-metadata [required: Any, installed: 1.6.0]
- zipp [required: >=0.5, installed: 3.1.0]
- pyrsistent [required: >=0.14.0, installed: 0.16.0]
- six [required: Any, installed: 1.14.0]
- setuptools [required: Any, installed: 46.0.0]
- six [required: >=1.11.0, installed: 1.14.0]
- jupyter-core [required: Any, installed: 4.6.3]
- traitlets [required: Any, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- traitlets [required: >=4.1, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
- prometheus-client [required: Any, installed: 0.7.1]
- pyzmq [required: >=17, installed: 19.0.0]
- Send2Trash [required: Any, installed: 1.5.0]
- terminado [required: >=0.8.1, installed: 0.8.3]
- ptyprocess [required: Any, installed: 0.6.0]
- tornado [required: >=4, installed: 6.0.4]
- tornado [required: >=5.0, installed: 6.0.4]
- traitlets [required: >=4.2.1, installed: 4.3.3]
- decorator [required: Any, installed: 4.4.2]
- ipython-genutils [required: Any, installed: 0.2.0]
- six [required: Any, installed: 1.14.0]
Enjoy pruning that by hand without breaking any other packages.
Note that in order to do this correctly, pip will need to have a database of which packages are automatically installed as dependencies. E.g. A depends on B and one initially needed A and B, then perse only needs B, so pip purge A shouldn't remove B.
That would be a nice feature but it's unnecessary. pip uninstall
already lacks that distinction. "autoremove"/"recursively remove" just means "recursively uninstall everything that would be come a leaf." Tracking manual installs & whitelisting them from removal sounds like a separate feature (a hack for this is to create a dummy package that depends on all manual installs).
That would be a nice feature but it's unnecessary.
I'd argue that not randomly breaking one's environment is much more than just a nice feature.
pip uninstall
already lacks that distinction.
Because it's explicit. Whilst
"autoremove"/"recursively remove" just means "recursively uninstall everything that would be come a leaf."
is technically true, it's implicit, dangerous and unexpected (not how any package manager providing similar functionality does it). It might suit trying and removing programs/libraries in a working session (which can be done in virtual environments), yet for long-term management of end-user environments (as in where people install youtube-dl, thefuck, ect. and libraries for their local scripts), it's just asking for trouble.
I'm sure that there are many ways to track manual install, but as an user I insist that it must be done.
I understand the feature can be nice for some, but pip maintainers take the blunt when some of the users find the feature to not fit their expectation. So new feature requests need to be thought through quite extensively before they can even be considered for acceptance. Tracking manual package installation is a hard problem (which may be unexepected), and most package managers resort to doing this the other way around, requiring the user to specify what are needed and purge the rest, which may be a better solution.
The same rational has been spelled out in #2635. Although pip does have a proper resolver now, it is still lacking information required to do this properly, and may continue to lack them forever (since some of the features are arguably out of scope). I would recommend you looking into alternative project management tools instead, as mentioned in the linked issue.