pypa/pip

uninstall doesn't remove scripts installed in 'editable' mode

bjmuld opened this issue · 11 comments

Environment

  • pip version: 18
  • Python version: 3.6
  • OS: ubuntu 18.04

Description
installed (setuputils) package with '-e' flag. uninstalled package. package scripts remain.

Expected behavior
uninstalling package should remove package scripts.

How to Reproduce

  1. install package with pip install -e which includes scripts=[] in the setup.py
  2. uninstall same package with pip uninstall
  3. observe that scripts are still on path

I can replicate this. Note that this only applies to editable requirements, and only to scripts (not entry_points.console_scripts).

(Also: You likely should use entry_points instead of scripts anyway.)


Edit: I di some digging and it seems the problem is that .egg-info does not record scripts installation at all. I am not sure whether this is intentional (to favour entry_points).

it seems the problem is that .egg-info does not record scripts installation at all.

This would mean the issue is on setuptools side ?

Kind of both, I guess? setuptools needs to record installed scripts, and pip needs to use that information during uninstallation to remove them.

Indeed, the uninstall should happen here

if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'):
for script in dist.metadata_listdir('scripts'):
if dist_in_usersite(dist):
bin_dir = bin_user
else:
bin_dir = bin_py
paths_to_remove.add(os.path.join(bin_dir, script))
if WINDOWS:
paths_to_remove.add(os.path.join(bin_dir, script) + '.bat')
🤔

(Also: You likely should use entry_points instead of scripts anyway.)

I require bash scripts... was under the impression that entry_points will only offer entry to python?

Also, I might suggest that material installed into /bin be symlinked if the install is --editable ??

Also, I might suggest that material installed into /bin be symlinked if the install is --editable ??

This would need to be raised to setuptools instead. I don’t think it would be accepted, however, with the same reasons why the library itself is not symlinked, but using egg-link.

This seems to be a problem when using console_scripts as well.

Reproduce:

git clone https://github.com/behrmann/mkosi --branch entrypoint
cd mkosi
pip install --user -e .
pip uninstall mkosi
ls ~/.local/bin/mkosi

To further specify this, the problem does not appear, when installing with the consolce_scripts entrypoint in a virtual environment, only globally (with --user though).

Strace shows us that pip tries to remove the file in the wrong location:

stat("/home/daan/.local/lib/python3.8/site-packages/bin/mkosi", 0x7ffc971ee2d0) = -1 ENOENT (No such file or directory)

It tries to go via site-packages while the script is actually in /home/daan/.local/bin

The reason for this seems to be that _script_names in _internal/req/req_uninstall.py uses bin_user from _internal/locations.py, which is bin_user = os.path.join(user_site, 'bin') on Linux and user_site is set via

 try:
     # Use getusersitepackages if this is present, as it ensures that the
     # value is initialised properly.
     user_site = site.getusersitepackages()
 except AttributeError:
     user_site = site.USER_SITE

evaluating to

>>> import site
>>> site.getusersitepackages()
'/home/user/.local/lib/python3.8/site-packages'

After systemd/mkosi@0e477de, the script gets uninstalled correctly on pip uninstall mkosi. @behrmann Can you verify this as well?