Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--user fixes part 4, issue #440 #574

Merged
merged 2 commits into from
Jun 11, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pip/backwardcompat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Stuff that differs in different Python versions"""

import sys
import site

__all__ = ['WindowsError']

Expand Down Expand Up @@ -84,6 +85,8 @@ def fwrite(f, s):

from distutils.sysconfig import get_python_lib, get_python_version

#site.USER_SITE was created in py2.6
user_site = getattr(site,'USER_SITE',None)

def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
Expand Down
3 changes: 2 additions & 1 deletion pip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ def run(self, options, args):
as_egg=options.as_egg,
ignore_installed=options.ignore_installed,
ignore_dependencies=options.ignore_dependencies,
force_reinstall=options.force_reinstall)
force_reinstall=options.force_reinstall,
use_user_site=options.use_user_site)
for name in args:
requirement_set.add_requirement(
InstallRequirement.from_line(name, None))
Expand Down
16 changes: 12 additions & 4 deletions pip/req.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from pip.log import logger
from pip.util import display_path, rmtree
from pip.util import ask, ask_path_exists, backup_dir
from pip.util import is_installable_dir, is_local, dist_is_local
from pip.util import is_installable_dir, is_local, dist_is_local, dist_in_usersite
from pip.util import renames, normalize_path, egg_link_path
from pip.util import make_path_relative
from pip.util import call_subprocess
Expand Down Expand Up @@ -62,6 +62,7 @@ def __init__(self, req, comes_from, source_dir=None, editable=False,
self.install_succeeded = None
# UninstallPathSet of uninstalled distribution (for possible rollback)
self.uninstalled = None
self.use_user_site = False

@classmethod
def from_editable(cls, editable_req, comes_from=None, default_vcs=None):
Expand Down Expand Up @@ -564,7 +565,7 @@ def install(self, install_options, global_options=()):
if self.editable:
self.install_editable(install_options, global_options)
return

temp_location = tempfile.mkdtemp('-record', 'pip-')
record_filename = os.path.join(temp_location, 'install-record.txt')
try:
Expand Down Expand Up @@ -680,7 +681,12 @@ def check_if_exists(self):
except pkg_resources.DistributionNotFound:
return False
except pkg_resources.VersionConflict:
self.conflicts_with = pkg_resources.get_distribution(self.req.project_name)
existing_dist = pkg_resources.get_distribution(self.req.project_name)
if self.use_user_site:
if dist_in_usersite(existing_dist):
self.conflicts_with = existing_dist
else:
self.conflicts_with = existing_dist
return True

@property
Expand Down Expand Up @@ -798,7 +804,7 @@ class RequirementSet(object):

def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
upgrade=False, ignore_installed=False, as_egg=False,
ignore_dependencies=False, force_reinstall=False):
ignore_dependencies=False, force_reinstall=False, use_user_site=False):
self.build_dir = build_dir
self.src_dir = src_dir
self.download_dir = download_dir
Expand All @@ -815,6 +821,7 @@ def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
self.successfully_installed = []
self.reqs_to_cleanup = []
self.as_egg = as_egg
self.use_user_site = use_user_site

def __str__(self):
reqs = [req for req in self.requirements.values()
Expand All @@ -825,6 +832,7 @@ def __str__(self):
def add_requirement(self, install_req):
name = install_req.name
install_req.as_egg = self.as_egg
install_req.use_user_site = self.use_user_site
if not name:
self.unnamed_requirements.append(install_req)
else:
Expand Down
12 changes: 11 additions & 1 deletion pip/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import tarfile
import subprocess
from pip.exceptions import InstallationError, BadCommand
from pip.backwardcompat import WindowsError, string_types, raw_input, console_to_str
from pip.backwardcompat import WindowsError, string_types, raw_input, console_to_str, user_site
from pip.locations import site_packages, running_under_virtualenv
from pip.log import logger

Expand Down Expand Up @@ -294,6 +294,16 @@ def dist_is_local(dist):
return is_local(dist_location(dist))


def dist_in_usersite(dist):
"""
Return True if given Distribution is installed in user site.
"""
if user_site:
return normalize_path(dist_location(dist)).startswith(normalize_path(user_site))
else:
return False

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explicit return False for the case rather than implicit return.


def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')):
"""
Return a list of installed Distribution objects.
Expand Down
72 changes: 72 additions & 0 deletions tests/test_user_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,75 @@ def test_install_user_venv_nositepkgs_fails(self):
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=True)
assert "Can not perform a '--user' install. User site-packages are not visible in this virtualenv." in result.stdout


def test_install_user_conflict_in_usersite(self):
"""
Test user install with conflict in usersite updates usersite.
"""

env = reset_env(system_site_packages=True)
result1 = run_pip('install', '--user', 'INITools==0.3')
result2 = run_pip('install', '--user', 'INITools==0.1')

#usersite has 0.1
egg_info_folder = env.user_site / 'INITools-0.1-py%s.egg-info' % pyversion
initools_v3_file = env.root_path / env.user_site / 'initools' / 'configparser.py' #file only in 0.3
assert egg_info_folder in result2.files_created, str(result2)
assert not isfile(initools_v3_file), initools_v3_file


def test_install_user_conflict_in_site(self):
"""
Test user install with conflict in site ignores site and installs to usersite
"""

#the test framework only supports testing using virtualenvs
#this test will use a --system_site_packages virtualenv to achieve the conflict scenario.

env = reset_env(system_site_packages=True)
result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', 'INITools==0.1')

#usersite has 0.1
egg_info_folder = env.user_site / 'INITools-0.1-py%s.egg-info' % pyversion
initools_folder = env.user_site / 'initools'
assert egg_info_folder in result2.files_created, str(result2)
assert initools_folder in result2.files_created, str(result2)

#site still has 0.2 (can't look in result1; have to check)
egg_info_folder = env.root_path / env.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
initools_folder = env.root_path / env.site_packages / 'initools'
assert isdir(egg_info_folder)
assert isdir(initools_folder)


def test_install_user_conflict_in_globalsite_and_usersite(self):
"""
Test user install with conflict in globalsite and usersite ignores global site and updates usersite.
"""

#the test framework only supports testing using virtualenvs
#this test will use a --system_site_packages virtualenv to achieve the conflict scenario.

env = reset_env(system_site_packages=True)

# the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv site, usersite, global site
# given this ordering you *can't* use it to simulate the scenario for this test.
# this test will add the usersite to PYTHONPATH to simulate the desired ordering
env.environ["PYTHONPATH"] = env.root_path / env.user_site

result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', 'INITools==0.3')
result3 = run_pip('install', '--user', 'INITools==0.1')

#usersite has 0.1
egg_info_folder = env.user_site / 'INITools-0.1-py%s.egg-info' % pyversion
initools_v3_file = env.root_path / env.user_site / 'initools' / 'configparser.py' #file only in 0.3
assert egg_info_folder in result3.files_created, str(result3)
assert not isfile(initools_v3_file), initools_v3_file

#site still has 0.2 (can't just look in result1; have to check)
egg_info_folder = env.root_path / env.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
initools_folder = env.root_path / env.site_packages / 'initools'
assert isdir(egg_info_folder)
assert isdir(initools_folder)