Skip to content


Fix `pip install --user` when upgrading an existing system package. #704

wants to merge 1 commit into from

4 participants


I'm on a machine with old system packages and I want to install newer versions in my user-site. Despite all the recent work on --user, no combination of --upgrade and --ignore-installed completed successfully; pip would always try to remove the system package and, naturally, fail.

This is a somewhat dumb patch that fixes the behavior for me. I can't imagine how to write a test for it, and the existing user-site tests confirm this is unsupported, but it's simple enough.

(fwiw, the existing code didn't cover this case because while check_if_exists has some use_user_site guards, they only come into play when there's a version conflict; a simple upgrade instead causes an assignment to .satisfied_by, which is later assigned to .conflicts_with and then triggers the uninstallation.)

Python Packaging Authority member

thanks for reporting. I'll look at this later tonight.

Python Packaging Authority member

yes, this scenario got overlooked (using --user and --upgrade together).
I'll probably tweak your fix and add a test tomorrow.
I assume you know you can successfully use:
pip install --user pkg==version (of course you have to go find the latest version)
thanks again.

Python Packaging Authority member

close due to fix in pull #705

@qwcode qwcode closed this

awesome, thanks!


1.2.1 works as advertised with --user --upgrade. Thanks!


I could have sworn that this worked somewhere, but I am now seeing the same error with 1.2.1:

glyph@jaina:~/tmp★ pip --version
 ↩ Fri Dec 14 12:38:32 PST 2012
pip 1.2.1 from ~/Library/Python/2.7/lib/python/site-packages (python 2.7)
 ↪ Fri Dec 14 12:38:32 PST 2012 (★ pip --version)
glyph@jaina:~/tmp★ pip install --user --upgrade --ignore-installed zope.interface
 ↩ Fri Dec 14 12:39:07 PST 2012
Downloading/unpacking zope.interface
  Running egg_info for package zope.interface

Downloading/unpacking setuptools (from zope.interface)
  Downloading setuptools-0.6c12dev-r88846.tar.gz (257kB): 257kB downloaded
  Running egg_info for package setuptools

Installing collected packages: zope.interface, setuptools
  Found existing installation: zope.interface 3.5.1
    Uninstalling zope.interface:
Traceback (most recent call last):
  File ".../pip/", line 107, in main
    status =, args)
  File ".../pip/commands/", line 261, in run
    requirement_set.install(install_options, global_options)
  File ".../pip/", line 1162, in install
  File ".../pip/", line 495, in uninstall
  File ".../pip/", line 1492, in remove
    renames(path, new_path)
  File ".../pip/", line 273, in renames
    shutil.move(old, new)
  File ".../", line 296, in move
  File ".../", line 249, in rmtree
    onerror(os.remove, fullname, sys.exc_info())
  File ".../", line 247, in rmtree
OSError: [Errno 13] Permission denied: '.../zope/'

Storing complete log in ~/Library/Logs/pip.log
 ↪ Fri Dec 14 12:39:10 PST 2012 (★ pip install --user --upgrade --ignore-installed zope.interface) (3 seconds elapsed)
[Error: 2]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Showing with 8 additions and 2 deletions.
  1. +8 −2 pip/
10 pip/
@@ -417,7 +417,7 @@ def uninstall(self, auto_confirm=False):
raise UninstallationError("Cannot uninstall requirement %s, not installed" % (,))
dist = self.satisfied_by or self.conflicts_with
- paths_to_remove = UninstallPathSet(dist)
+ paths_to_remove = UninstallPathSet(dist, use_user_site=self.use_user_site)
pip_egg_info_path = os.path.join(dist.location,
dist.egg_name()) + '.egg-info'
@@ -1405,13 +1405,14 @@ def parse_editable(editable_req, default_vcs=None):
class UninstallPathSet(object):
"""A set of file paths to be removed in the uninstallation of a
- def __init__(self, dist):
+ def __init__(self, dist, use_user_site=False):
self.paths = set()
self._refuse = set()
self.pth = {}
self.dist = dist
self.save_dir = None
self._moved_paths = []
+ self.use_user_site = use_user_site
def _permitted(self, path):
@@ -1426,6 +1427,11 @@ def _can_uninstall(self):
logger.notify("Not uninstalling %s at %s, outside environment %s"
% (self.dist.project_name, normalize_path(self.dist.location), sys.prefix))
return False
+ if self.use_user_site and not dist_in_usersite(self.dist):
+ logger.notify("Not uninstalling %s at %s, not in user site-packages"
+ % (self.dist.project_name, normalize_path(self.dist.location)))
+ return False
return True
def add(self, path):
Something went wrong with that request. Please try again.