Updated to upstream to make pull request build

This commit is contained in:
Ben Rosser 2012-10-19 01:54:42 -04:00
commit b4a3133ef8
67 changed files with 1618 additions and 280 deletions

2
.gitignore vendored
View File

@ -21,3 +21,5 @@ build/*
pip-log.txt
pip.log
*.~
.tox

View File

@ -5,6 +5,7 @@ python:
- 2.7
- 3.1
- 3.2
- pypy
before_install:
- sudo apt-get install subversion bzr mercurial
- echo -e "[web]\ncacerts = /etc/ssl/certs/ca-certificates.crt" >> ~/.hgrc
@ -15,5 +16,7 @@ notifications:
branches:
only:
- develop
matrix:
allow_failures:
env:
- PIP_USE_MIRRORS=true

View File

@ -1,15 +1,20 @@
Alex Grönholm
Alex Morega
Alexandre Conrad
Andrey Bulgakov
Antti Kaihola
Armin Ronacher
Aziz Köksal
Ben Rosser
Brian Rosner
Carl Meyer
Chris McDonough
Christian Oudard
Clay McClure
Cody Soyland
Daniel Holth
Dave Abrahams
Donald Stufft
Francesco
Hugo Lopes Tavares
Ian Bicking
@ -36,12 +41,15 @@ Nowell Strite
Oliver Tonnhofer
Olivier Girardot
Patrick Jenkins
Paul Moore
Paul Nasrat
Paul Oswald
Paul van der Linden
Peter Waller
Phil Whelan
Piet Delport
Qiangning Hong
Rafael Caricio
Rene Dudfield
Ronny Pfannschmidt
Rory McCann

View File

@ -1,4 +1,4 @@
Copyright (c) 2008-2011 The pip developers (see AUTHORS.txt file)
Copyright (c) 2008-2012 The pip developers (see AUTHORS.txt file)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

7
README.rst Normal file
View File

@ -0,0 +1,7 @@
pip
===
.. image:: https://secure.travis-ci.org/pypa/pip.png?branch=develop
:target: http://travis-ci.org/pypa/pip
For documentation, see http://www.pip-installer.org

View File

@ -11,12 +11,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
sys.path.insert(0, os.path.abspath(os.pardir))
#sys.path.append(os.path.join(os.path.dirname(__file__), '../'))
# -- General configuration -----------------------------------------------------
@ -40,15 +41,21 @@ master_doc = 'index'
# General information about the project.
project = 'pip'
copyright = '2008-2011, The pip developers'
copyright = '2008-2012, The pip developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
release = "1.1.post2"
version = '.'.join(release.split('.')[:2])
try:
from pip import __version__
# The short X.Y version.
version = '.'.join(__version__.split('.')[:2])
# The full version, including alpha/beta/rc tags.
release = __version__
except ImportError:
version = release = 'dev'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -60,10 +60,10 @@ Before sending us a pull request, please, be sure all tests pass.
Supported Python versions
-------------------------
Pip supports Python versions 2.4, 2.5, 2.6, 2.7, 3.1, and 3.2, from a single
Pip supports Python versions 2.5, 2.6, 2.7, 3.1, and 3.2, from a single
codebase (without use of 2to3 translation). Untested contributions frequently
break Python 2.4 or 3.x compatibility. Please run the tests on at least 2.4 and
3.2 and report your results when sending a pull request.
break Python 3.x compatibility. Please run the tests on at least 3.2 and
report your results when sending a pull request.
Continuous Integration server
-----------------------------

View File

@ -5,13 +5,49 @@ News
Changelog
=========
Next release (1.2) schedule
Next release (1.3) schedule
---------------------------
Beta and final releases planned for the second half of 2012.
Beta and final releases planned for the end of 2012.
develop (unreleased)
-------------------
--------------------
* --user/--upgrade install options now work together; thanks eevee.
* Added check in ``install --download`` to prevent re-downloading if the target
file already exists. Thanks Andrey Bulgakov.
* Added support for bare paths (including relative paths) as argument to
`--find-links`. Thanks Paul Moore for draft patch.
* Added support for --no-index in requirements files.
* Added "pip show" command to get information about an installed
package. Fixes #131. Thanks Kelsey Hightower and Rafael Caricio.
1.2.1 (2012-09-06)
------------------
* Fixed a regression introduced in 1.2 about raising an exception when
not finding any files to uninstall in the current environment. Thanks for
the fix, Marcus Smith.
1.2 (2012-09-01)
----------------
* **Dropped support for Python 2.4** The minimum supported Python version is
now Python 2.5.
* Fixed issue #605 - pypi mirror support broken on some DNS responses. Thanks
philwhin.
* Fixed issue #355 - pip uninstall removes files it didn't install. Thanks
pjdelport.
* Fixed issues #493, #494, #440, and #573 related to improving support for the
user installation scheme. Thanks Marcus Smith.
* Write failure log to temp file if default location is not writable. Thanks
andreigc.
@ -38,9 +74,6 @@ develop (unreleased)
* Fixed issue #504 - allow package URLS to have querystrings. Thanks W.
Trevor King.
* Dropped support for Python 2.4. The minimum supported Python version is
now Python 2.5.
* Fixed issue #58 - pip freeze now falls back to non-editable format rather
than blowing up if it can't determine the origin repository of an editable.
Thanks Rory McCann.
@ -57,6 +90,21 @@ develop (unreleased)
* Fixed issue #427 - clearer error message on a malformed VCS url. Thanks
Thomas Fenzl.
* Added support for using any of the built in guaranteed algorithms in
``hashlib`` as a checksum hash.
* Fixed issue #321 - Raise an exception if current working directory can't be
found or accessed.
* Fixed issue #82 - Removed special casing of the user directory and use the
Python default instead.
* Fixed #436 - Only warn about version conflicts if there is actually one.
This re-enables using ``==dev`` in requirements files.
* Moved tests to be run on Travis CI: http://travis-ci.org/pypa/pip
* Added a better help formatter.
1.1 (2012-02-16)
----------------

View File

@ -26,8 +26,8 @@ local or remote::
.. _setuptools extras: http://peak.telecommunity.com/DevCenter/setuptools#declaring-extras-optional-features-with-their-own-dependencies
Edit mode
*********
Editable mode
*************
Packages normally_ install under ``site-packages``, but when you're
making changes, it makes more sense to run the package straight from the
@ -106,6 +106,16 @@ within a :ref:`requirements file <requirements-files>` in addition to on the
command line directly.
Package Checksum Hashes
'''''''''''''''''''''''
:term:`PyPI` provides a md5 hash of a package by having the link to the
package include a #md5=<hash>. pip supports this, as well as any of the
guaranteed hashlib algorithms (sha1, sha224, sha384, sha256, sha512, md5).
The hash fragment is case sensitive (i.e. sha1 not SHA1).
Uninstall packages
------------------
@ -137,9 +147,23 @@ packages. With the ``--index`` option you can search in a different
repository.
Checking installed package status
---------------------------------
To get info about an installed package, including its location and included
files, run ``pip show ProjectName``.
Bundles
-------
.. note::
Pip bundles are poorly supported, poorly tested, not widely used, and
unnecessary (the same goals can be achieved by distributing a directory of
sdists and using `--find-links` to reference it). The feature will likely be
removed in a future version of pip.
Another way to distribute a set of libraries is a bundle format (specific to
pip). This format is not stable at this time (there simply hasn't been
any feedback, nor a great deal of thought). A bundle file contains all the

View File

@ -10,8 +10,12 @@ from pip.basecommand import command_dict, load_command, load_all_commands, comma
from pip.baseparser import parser
from pip.exceptions import InstallationError
from pip.log import logger
from pip.util import get_installed_distributions
from pip.vcs import git, mercurial, subversion, bazaar
from pip.util import get_installed_distributions, get_prog
from pip.vcs import git, mercurial, subversion, bazaar # noqa
# The version as used in the setup.py and the docs conf.py
__version__ = "1.2.1.post1"
def autocomplete():
@ -26,7 +30,7 @@ def autocomplete():
cwords = os.environ['COMP_WORDS'].split()[1:]
cword = int(os.environ['COMP_CWORD'])
try:
current = cwords[cword-1]
current = cwords[cword - 1]
except IndexError:
current = ''
load_all_commands()
@ -59,7 +63,7 @@ def autocomplete():
for opt in subcommand.parser.option_list
if opt.help != optparse.SUPPRESS_HELP]
# filter out previously specified options from available options
prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]]
prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]]
options = [(x, v) for (x, v) in options if x not in prev_opts]
# filter options by current input
options = [(k, v) for k, v in options if k.startswith(current)]
@ -87,7 +91,8 @@ def main(initial_args=None):
if options.help and not args:
args = ['help']
if not args:
parser.error('You must give a command (use "pip help" to see a list of commands)')
parser.error('You must give a command '
'(use "%s help" to see a list of commands)' % get_prog())
command = args[0].lower()
load_command(command)
if command not in command_dict:
@ -184,7 +189,7 @@ class FrozenRequirement(object):
req = self.req
if self.editable:
req = '-e %s' % req
return '\n'.join(list(self.comments)+[str(req)])+'\n'
return '\n'.join(list(self.comments) + [str(req)]) + '\n'
if __name__ == '__main__':

View File

@ -1,7 +1,7 @@
if __name__ == '__main__':
import sys
from . import main
import sys
from .runner import run
exit = main()
if __name__ == '__main__':
exit = run()
if exit:
sys.exit(exit)

View File

@ -1,10 +1,14 @@
"""Stuff that differs in different Python versions"""
import os
import imp
import sys
import site
__all__ = ['WindowsError']
uses_pycache = hasattr(imp,'cache_from_source')
try:
WindowsError = WindowsError
except NameError:
@ -12,6 +16,7 @@ except NameError:
"""this exception should never be raised"""
WindowsError = NeverUsedException
console_encoding = sys.__stdout__.encoding
if sys.version_info >= (3,):
@ -97,3 +102,11 @@ def product(*args, **kwds):
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
def home_lib(home):
"""Return the lib dir under the 'home' installation scheme"""
if hasattr(sys, 'pypy_version_info'):
lib = 'site-packages'
else:
lib = os.path.join('lib', 'python')
return os.path.join(home, lib)

View File

@ -4,6 +4,7 @@ import os
from pkgutil import walk_packages
import socket
import sys
import tempfile
import traceback
import time
@ -15,6 +16,7 @@ from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
CommandError)
from pip.backwardcompat import StringIO
from pip.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND
from pip.util import get_prog
__all__ = ['command_dict', 'Command', 'load_all_commands',
@ -35,7 +37,7 @@ class Command(object):
assert self.name
self.parser = ConfigOptionParser(
usage=self.usage,
prog='%s %s' % (sys.argv[0], self.name),
prog='%s %s' % (get_prog(), self.name),
version=parser.version,
formatter=UpdatingDefaultsHelpFormatter(),
name=self.name)
@ -144,7 +146,7 @@ class Command(object):
temp = tempfile.NamedTemporaryFile(delete=False)
log_fn = temp.name
log_fp = open_logfile(log_fn, 'w')
logger.fatal('Storing complete log in %s' % log_fn)
logger.fatal('Storing complete log in %s' % log_fn)
log_fp.write(text)
log_fp.close()
return exit

View File

@ -7,22 +7,17 @@ import os
from distutils.util import strtobool
from pip.backwardcompat import ConfigParser, string_types
from pip.locations import default_config_file, default_log_file
from pip.util import get_terminal_size, get_prog
class PipPrettyHelpFormatter(optparse.IndentedHelpFormatter):
class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
"""A prettier/less verbose help formatter for optparse."""
def __init__(self, *args, **kw):
kw['max_help_position'] = 23
kw['indent_increment'] = 1
# do as argparse does
try:
kw['width'] = int(os.environ['COLUMNS']) - 2
except:
kw['width'] = 78
optparse.IndentedHelpFormatter.__init__(self, *args, **kw)
def __init__(self, *args, **kwargs):
kwargs['max_help_position'] = 30
kwargs['indent_increment'] = 1
kwargs['width'] = get_terminal_size()[0] - 2
optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs)
def format_option_strings(self, option):
return self._format_option_strings(option, ' <%s>', ', ')
@ -35,31 +30,34 @@ class PipPrettyHelpFormatter(optparse.IndentedHelpFormatter):
:param mvarfmt: metavar format string - evaluated as mvarfmt % metavar
:param optsep: separator
"""
opts = []
if option._short_opts: opts.append(option._short_opts[0])
if option._long_opts: opts.append(option._long_opts[0])
if len(opts) > 1: opts.insert(1, optsep)
if option._short_opts:
opts.append(option._short_opts[0])
if option._long_opts:
opts.append(option._long_opts[0])
if len(opts) > 1:
opts.insert(1, optsep)
if option.takes_value():
metavar = option.metavar or option.dest.lower()
opts.append(mvarfmt % metavar)
opts.append(mvarfmt % metavar.upper())
return ''.join(opts)
def format_heading(self, heading):
if heading == 'Options': return ''
if heading == 'Options':
return ''
return heading + ':\n'
def format_usage(self, usage):
# ensure there is only one newline between usage and the first heading
# if there is no description
"""
ensure there is only one newline between usage and the first heading
if there is no description
"""
msg = 'Usage: %s' % usage
if self.parser.description:
msg += '\n'
return msg
def format_description(self, description):
@ -77,7 +75,7 @@ class PipPrettyHelpFormatter(optparse.IndentedHelpFormatter):
return ''
class UpdatingDefaultsHelpFormatter(PipPrettyHelpFormatter):
class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
"""Custom help formatter for use in ConfigOptionParser that updates
the defaults before expanding them, allowing them to show up correctly
in the help listing"""
@ -148,7 +146,7 @@ class ConfigOptionParser(optparse.OptionParser):
for key, val in items:
key = key.replace('_', '-')
if not key.startswith('--'):
key = '--%s' % key # only prefer long opts
key = '--%s' % key # only prefer long opts
normalized[key] = val
return normalized
@ -171,7 +169,7 @@ class ConfigOptionParser(optparse.OptionParser):
# Old, pre-Optik 1.5 behaviour.
return optparse.Values(self.defaults)
defaults = self.update_defaults(self.defaults.copy()) # ours
defaults = self.update_defaults(self.defaults.copy()) # ours
for option in self._get_all_options():
default = defaults.get(option.dest)
if isinstance(default, string_types):
@ -179,20 +177,27 @@ class ConfigOptionParser(optparse.OptionParser):
defaults[option.dest] = option.check_value(opt_str, default)
return optparse.Values(defaults)
def error(self, msg):
self.print_usage(sys.stderr)
self.exit(2, "%s\n" % msg)
try:
pip_dist = pkg_resources.get_distribution('pip')
version = '%s from %s (python %s)' % (
pip_dist, pip_dist.location, sys.version[:3])
except pkg_resources.DistributionNotFound:
# when running pip.py without installing
version=None
version = None
parser = ConfigOptionParser(
usage='%prog COMMAND [OPTIONS]',
version=version,
add_help_option=False,
formatter=UpdatingDefaultsHelpFormatter(),
name='global')
name='global',
prog=get_prog())
parser.add_option(
'-h', '--help',
@ -253,7 +258,7 @@ parser.add_option(
default='',
help="Specify a proxy in the form user:passwd@proxy.server:port. "
"Note that the user:password@ is optional and required only if you "
"are behind an authenticated proxy. If you provide "
"are behind an authenticated proxy. If you provide "
"user@proxy.server:port then you will be prompted for a password.")
parser.add_option(
'--timeout', '--default-timeout',
@ -285,10 +290,10 @@ parser.add_option(
choices=['s', 'i', 'w', 'b'],
default=[],
action='append',
help="Default action when a path already exists."
"Use this option more then one time to specify "
help="Default action when a path already exists. "
"Use this option more than one time to specify "
"another action if a certain option is not "
"available, choices: "
"available. Choices: "
"(s)witch, (i)gnore, (w)ipe, (b)ackup")
parser.disable_interspersed_args()

View File

@ -9,6 +9,7 @@ from pip.locations import build_prefix, src_prefix, virtualenv_no_global
from pip.basecommand import Command
from pip.index import PackageFinder
from pip.exceptions import InstallationError, CommandError
from pip.backwardcompat import home_lib
class InstallCommand(Command):
@ -36,7 +37,7 @@ class InstallCommand(Command):
action='append',
default=[],
metavar='FILENAME',
help='Install all the packages listed in the given requirements file. '
help='Install all the packages listed in the given requirements file. '
'This option can be used multiple times.')
self.parser.add_option(
'-f', '--find-links',
@ -148,15 +149,15 @@ class InstallCommand(Command):
dest='install_options',
action='append',
help="Extra arguments to be supplied to the setup.py install "
"command (use like --install-option=\"--install-scripts=/usr/local/bin\"). "
"Use multiple --install-option options to pass multiple options to setup.py install. "
"command (use like --install-option=\"--install-scripts=/usr/local/bin\"). "
"Use multiple --install-option options to pass multiple options to setup.py install. "
"If you are using an option with a directory path, be sure to use absolute path.")
self.parser.add_option(
'--global-option',
dest='global_options',
action='append',
help="Extra global options to be supplied to the setup.py"
help="Extra global options to be supplied to the setup.py "
"call before the install command")
self.parser.add_option(
@ -276,7 +277,7 @@ class InstallCommand(Command):
if options.target_dir:
if not os.path.exists(options.target_dir):
os.makedirs(options.target_dir)
lib_dir = os.path.join(temp_target_dir, "lib/python/")
lib_dir = home_lib(temp_target_dir)
for item in os.listdir(lib_dir):
shutil.move(
os.path.join(lib_dir, item),

78
pip/commands/show.py Normal file
View File

@ -0,0 +1,78 @@
import os
import pkg_resources
from pip.basecommand import Command
from pip.log import logger
class ShowCommand(Command):
name = 'show'
usage = '%prog QUERY'
summary = 'Output installed distributions (exact versions, files) to stdout'
def __init__(self):
super(ShowCommand, self).__init__()
self.parser.add_option(
'-f', '--files',
dest='files',
action='store_true',
default=False,
help='Show the full list of installed files for each package')
def run(self, options, args):
if not args:
logger.warn('ERROR: Please provide a project name or names.')
return
query = args
results = search_packages_info(query)
print_results(results, options.files)
def search_packages_info(query):
"""
Gather details from installed distributions. Print distribution name,
version, location, and installed files. Installed files requires a
pip generated 'installed-files.txt' in the distributions '.egg-info'
directory.
"""
installed_packages = dict(
[(p.project_name.lower(), p) for p in pkg_resources.working_set])
for name in query:
normalized_name = name.lower()
if normalized_name in installed_packages:
dist = installed_packages[normalized_name]
package = {
'name': dist.project_name,
'version': dist.version,
'location': dist.location,
'requires': [dep.project_name for dep in dist.requires()],
}
filelist = os.path.join(
dist.location,
dist.egg_name() + '.egg-info',
'installed-files.txt')
if os.path.isfile(filelist):
package['files'] = filelist
yield package
def print_results(distributions, list_all_files):
"""
Print the informations from installed distributions found.
"""
for dist in distributions:
logger.notify("---")
logger.notify("Name: %s" % dist['name'])
logger.notify("Version: %s" % dist['version'])
logger.notify("Location: %s" % dist['location'])
logger.notify("Requires: %s" % ', '.join(dist['requires']))
if list_all_files:
logger.notify("Files:")
if 'files' in dist:
for line in open(dist['files']):
logger.notify(" %s" % line.strip())
else:
logger.notify("Cannot locate installed-files.txt")
ShowCommand()

View File

@ -231,7 +231,7 @@ class ZipCommand(Command):
def add_filename_to_pth(self, filename):
path = os.path.dirname(filename)
dest = os.path.join(path, filename + '.pth')
dest = filename + '.pth'
if path not in self.paths():
logger.warn('Adding .pth file %s, but it is not on sys.path' % display_path(dest))
if not self.simulate:

View File

@ -1,6 +1,6 @@
import cgi
import getpass
from hashlib import md5
import hashlib
import mimetypes
import os
import re
@ -322,16 +322,24 @@ def is_file_url(link):
return link.url.lower().startswith('file:')
def _check_md5(download_hash, link):
download_hash = download_hash.hexdigest()
if download_hash != link.md5_hash:
logger.fatal("MD5 hash of the package %s (%s) doesn't match the expected hash %s!"
% (link, download_hash, link.md5_hash))
raise InstallationError('Bad MD5 hash for package %s' % link)
def _check_hash(download_hash, link):
if download_hash.digest_size != hashlib.new(link.hash_name).digest_size:
logger.fatal("Hash digest size of the package %d (%s) doesn't match the expected hash name %s!"
% (download_hash.digest_size, link, link.hash_name))
raise InstallationError('Hash name mismatch for package %s' % link)
if download_hash.hexdigest() != link.hash:
logger.fatal("Hash of the package %s (%s) doesn't match the expected hash %s!"
% (link, download_hash, link.hash))
raise InstallationError('Bad %s hash for package %s' % (link.hash_name, link))
def _get_md5_from_file(target_file, link):
download_hash = md5()
def _get_hash_from_file(target_file, link):
try:
download_hash = hashlib.new(link.hash_name)
except (ValueError, TypeError):
logger.warn("Unsupported hash name %s for package %s" % (link.hash_name, link))
return None
fp = open(target_file, 'rb')
while True:
chunk = fp.read(4096)
@ -345,8 +353,11 @@ def _get_md5_from_file(target_file, link):
def _download_url(resp, link, temp_location):
fp = open(temp_location, 'wb')
download_hash = None
if link.md5_hash:
download_hash = md5()
if link.hash and link.hash_name:
try:
download_hash = hashlib.new(link.hash_name)
except ValueError:
logger.warn("Unsupported hash name %s for package %s" % (link.hash_name, link))
try:
total_length = int(resp.info()['content-length'])
except (ValueError, KeyError, TypeError):
@ -363,7 +374,7 @@ def _download_url(resp, link, temp_location):
logger.start_progress('Downloading %s (unknown size): ' % show_url)
else:
logger.notify('Downloading %s' % show_url)
logger.debug('Downloading from URL %s' % link)
logger.info('Downloading from URL %s' % link)
while True:
chunk = resp.read(4096)
@ -375,7 +386,7 @@ def _download_url(resp, link, temp_location):
logger.show_progress('%s' % format_size(downloaded))
else:
logger.show_progress('%3i%% %s' % (100*downloaded/total_length, format_size(downloaded)))
if link.md5_hash:
if download_hash is not None:
download_hash.update(chunk)
fp.write(chunk)
fp.close()
@ -418,16 +429,29 @@ def unpack_http_url(link, location, download_cache, download_dir=None):
urllib.quote(target_url, ''))
if not os.path.isdir(download_cache):
create_download_cache_folder(download_cache)
already_downloaded = None
if download_dir:
already_downloaded = os.path.join(download_dir, link.filename)
if not os.path.exists(already_downloaded):
already_downloaded = None
if (target_file
and os.path.exists(target_file)
and os.path.exists(target_file + '.content-type')):
fp = open(target_file+'.content-type')
content_type = fp.read().strip()
fp.close()
if link.md5_hash:
download_hash = _get_md5_from_file(target_file, link)
if link.hash and link.hash_name:
download_hash = _get_hash_from_file(target_file, link)
temp_location = target_file
logger.notify('Using download cache from %s' % target_file)
elif already_downloaded:
temp_location = already_downloaded
content_type = mimetypes.guess_type(already_downloaded)
if link.hash:
download_hash = _get_hash_from_file(temp_location, link)
logger.notify('File was already downloaded %s' % already_downloaded)
else:
resp = _get_response_from_url(target_url, link)
content_type = resp.info()['content-type']
@ -450,14 +474,14 @@ def unpack_http_url(link, location, download_cache, download_dir=None):
filename += ext
temp_location = os.path.join(temp_dir, filename)
download_hash = _download_url(resp, link, temp_location)
if link.md5_hash:
_check_md5(download_hash, link)
if download_dir:
if link.hash and link.hash_name:
_check_hash(download_hash, link)
if download_dir and not already_downloaded:
_copy_file(temp_location, download_dir, content_type, link)
unpack_file(temp_location, location, content_type, link)
if target_file and target_file != temp_location:
cache_download(target_file, temp_location, content_type)
if target_file is None:
if target_file is None and not already_downloaded:
os.unlink(temp_location)
os.rmdir(temp_dir)

View File

@ -20,7 +20,7 @@ from pip.util import Inf
from pip.util import normalize_name, splitext
from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled
from pip.backwardcompat import (WindowsError, BytesIO,
Queue, httplib, urlparse,
Queue, urlparse,
URLError, HTTPError, u,
product, url2pathname)
from pip.backwardcompat import Empty as QueueEmpty
@ -29,7 +29,7 @@ from pip.download import urlopen, path_to_url2, url_to_path, geturl, Urllib2Head
__all__ = ['PackageFinder']
DEFAULT_MIRROR_URL = "last.pypi.python.org"
DEFAULT_MIRROR_HOSTNAME = "last.pypi.python.org"
class PackageFinder(object):
@ -60,8 +60,7 @@ class PackageFinder(object):
## FIXME: also, we should track comes_from (i.e., use Link)
self.dependency_links.extend(links)
@staticmethod
def _sort_locations(locations):
def _sort_locations(self, locations):
"""
Sort locations into "files" (archives) and "urls", and return
a pair of lists (files,urls)
@ -69,8 +68,7 @@ class PackageFinder(object):
files = []
urls = []
# puts the url for the given file path into the appropriate
# list
# puts the url for the given file path into the appropriate list
def sort_path(path):
url = path_to_url2(path)
if mimetypes.guess_type(url, strict=False)[0] == 'text/html':
@ -79,34 +77,30 @@ class PackageFinder(object):
files.append(url)
for url in locations:
if url.startswith('file:'):
path = url_to_path(url)
if os.path.isdir(path):
is_local_path = os.path.exists(url)
is_file_url = url.startswith('file:')
is_find_link = url in self.find_links
if is_local_path or is_file_url:
if is_local_path:
path = url
else:
path = url_to_path(url)
if is_find_link and os.path.isdir(path):
path = os.path.realpath(path)
for item in os.listdir(path):
sort_path(os.path.join(path, item))
elif is_file_url and os.path.isdir(path):
urls.append(url)
elif os.path.isfile(path):
sort_path(path)
else:
urls.append(url)
return files, urls
def find_requirement(self, req, upgrade):
url_name = req.url_name
# Only check main index if index URL is given:
main_index_url = None
if self.index_urls:
# Check that we have the url_name correctly spelled:
main_index_url = Link(posixpath.join(self.index_urls[0], url_name))
# This will also cache the page, so it's okay that we get it again later:
page = self._get_page(main_index_url, req)
if page is None:
url_name = self._find_url_name(Link(self.index_urls[0]), url_name, req) or req.url_name
# Combine index URLs with mirror URLs here to allow
# adding more index URLs from requirements files
all_index_urls = self.index_urls + self.mirror_urls
def mkurl_pypi_url(url):
loc = posixpath.join(url, url_name)
# For maximum compatibility with easy_install, ensure the path
@ -116,6 +110,22 @@ class PackageFinder(object):
if not loc.endswith('/'):
loc = loc + '/'
return loc
url_name = req.url_name
# Only check main index if index URL is given:
main_index_url = None
if self.index_urls:
# Check that we have the url_name correctly spelled:
main_index_url = Link(mkurl_pypi_url(self.index_urls[0]))
# This will also cache the page, so it's okay that we get it again later:
page = self._get_page(main_index_url, req)
if page is None:
url_name = self._find_url_name(Link(self.index_urls[0]), url_name, req) or req.url_name
# Combine index URLs with mirror URLs here to allow
# adding more index URLs from requirements files
all_index_urls = self.index_urls + self.mirror_urls
if url_name is not None:
locations = [
mkurl_pypi_url(url)
@ -156,7 +166,7 @@ class PackageFinder(object):
logger.fatal('Could not find any downloads that satisfy the requirement %s' % req)
raise DistributionNotFound('No distributions at all found for %s' % req)
if req.satisfied_by is not None:
found_versions.append((req.satisfied_by.parsed_version, Inf, req.satisfied_by.version))
found_versions.append((req.satisfied_by.parsed_version, InfLink, req.satisfied_by.version))
if file_versions:
file_versions.sort(reverse=True)
logger.info('Local files found: %s' % ', '.join([url_to_path(link.url) for parsed, link, version in file_versions]))
@ -168,31 +178,31 @@ class PackageFinder(object):
logger.info("Ignoring link %s, version %s doesn't match %s"
% (link, version, ','.join([''.join(s) for s in req.req.specs])))
continue
applicable_versions.append((link, version))
applicable_versions = sorted(applicable_versions, key=lambda v: pkg_resources.parse_version(v[1]), reverse=True)
existing_applicable = bool([link for link, version in applicable_versions if link is Inf])
applicable_versions.append((parsed_version, link, version))
applicable_versions = sorted(applicable_versions, reverse=True)
existing_applicable = bool([link for parsed_version, link, version in applicable_versions if link is InfLink])
if not upgrade and existing_applicable:
if applicable_versions[0][1] is Inf:
if applicable_versions[0][1] is InfLink:
logger.info('Existing installed version (%s) is most up-to-date and satisfies requirement'
% req.satisfied_by.version)
raise BestVersionAlreadyInstalled
else:
logger.info('Existing installed version (%s) satisfies requirement (most up-to-date version is %s)'
% (req.satisfied_by.version, applicable_versions[0][1]))
% (req.satisfied_by.version, applicable_versions[0][2]))
return None
if not applicable_versions:
logger.fatal('Could not find a version that satisfies the requirement %s (from versions: %s)'
% (req, ', '.join([version for parsed_version, link, version in found_versions])))
raise DistributionNotFound('No distributions matching the version for %s' % req)
if applicable_versions[0][0] is Inf:
if applicable_versions[0][1] is InfLink:
# We have an existing version, and its the best version
logger.info('Installed version (%s) is most up-to-date (past versions: %s)'
% (req.satisfied_by.version, ', '.join([version for link, version in applicable_versions[1:]]) or 'none'))
% (req.satisfied_by.version, ', '.join([version for parsed_version, link, version in applicable_versions[1:]]) or 'none'))
raise BestVersionAlreadyInstalled
if len(applicable_versions) > 1:
logger.info('Using version %s (newest of versions: %s)' %
(applicable_versions[0][1], ', '.join([version for link, version in applicable_versions])))
return applicable_versions[0][0]
(applicable_versions[0][2], ', '.join([version for parsed_version, link, version in applicable_versions])))
return applicable_versions[0][1]
def _find_url_name(self, index_url, url_name, req):
"""Finds the true URL name of a package, when the given name isn't quite correct.
@ -586,7 +596,7 @@ class Link(object):
if self.comes_from:
return '%s (from %s)' % (self.url, self.comes_from)
else:
return self.url
return str(self.url)
def __repr__(self):
return '<Link %s>' % self
@ -594,6 +604,21 @@ class Link(object):
def __eq__(self, other):
return self.url == other.url
def __ne__(self, other):
return self.url != other.url
def __lt__(self, other):
return self.url < other.url
def __le__(self, other):
return self.url <= other.url
def __gt__(self, other):
return self.url > other.url
def __ge__(self, other):
return self.url >= other.url
def __hash__(self):
return hash(self.url)
@ -629,11 +654,18 @@ class Link(object):
return None
return match.group(1)
_md5_re = re.compile(r'md5=([a-f0-9]+)')
_hash_re = re.compile(r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)')
@property
def md5_hash(self):
match = self._md5_re.search(self.url)
def hash(self):
match = self._hash_re.search(self.url)
if match:
return match.group(2)
return None
@property
def hash_name(self):
match = self._hash_re.search(self.url)
if match:
return match.group(1)
return None
@ -642,6 +674,9 @@ class Link(object):
def show_url(self):
return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0])
#An "Infinite Link" that compares greater than other links
InfLink = Link(Inf)
def get_requirement_from_url(url):
"""Get a requirement from the URL, if possible. This looks for #egg
@ -680,14 +715,17 @@ def get_mirrors(hostname=None):
Originally written for the distutils2 project by Alexis Metaireau.
"""
if hostname is None:
hostname = DEFAULT_MIRROR_URL
hostname = DEFAULT_MIRROR_HOSTNAME
# return the last mirror registered on PyPI.
last_mirror_hostname = None
try:
hostname = socket.gethostbyname_ex(hostname)[0]
last_mirror_hostname = socket.gethostbyname_ex(hostname)[0]
except socket.gaierror:
return []
end_letter = hostname.split(".", 1)
if not last_mirror_hostname or last_mirror_hostname == DEFAULT_MIRROR_HOSTNAME:
last_mirror_hostname = "z.pypi.python.org"
end_letter = last_mirror_hostname.split(".", 1)
# determine the list from the last one.
return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])]

View File

@ -15,26 +15,26 @@ def running_under_virtualenv():
"""
return hasattr(sys, 'real_prefix')
def virtualenv_no_global():
"""
Return True if in a venv and no system site packages.
"""
#this mirrors the logic in virtualenv.py for locating the no-global-site-packages.txt file
site_mod_dir = os.path.dirname(os.path.abspath(site.__file__))
no_global_file = os.path.join(site_mod_dir,'no-global-site-packages.txt')
no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt')
if running_under_virtualenv() and os.path.isfile(no_global_file):
return True
if running_under_virtualenv():
## FIXME: is build/ a good name?
build_prefix = os.path.join(sys.prefix, 'build')
src_prefix = os.path.join(sys.prefix, 'src')
else:
# Use tempfile to create a temporary folder for build
# Note: we are NOT using mkdtemp so we can have a consistent build dir
build_prefix = os.path.join(tempfile.gettempdir(), 'pip-build')
## FIXME: keep src in cwd for now (it is not a temporary folder)
try:
src_prefix = os.path.join(os.getcwd(), 'src')

View File

@ -1,5 +1,6 @@
from email.parser import FeedParser
import os
import imp
import pkg_resources
import re
import sys
@ -16,10 +17,10 @@ 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, dist_in_usersite
from pip.util import renames, normalize_path, egg_link_path
from pip.util import renames, normalize_path, egg_link_path, dist_in_site_packages
from pip.util import make_path_relative
from pip.util import call_subprocess
from pip.backwardcompat import (urlparse, urllib,
from pip.backwardcompat import (urlparse, urllib, uses_pycache,
ConfigParser, string_types, HTTPError,
get_python_version, b)
from pip.index import Link
@ -94,7 +95,7 @@ class InstallRequirement(object):
link = Link(name)
elif os.path.isdir(path) and (os.path.sep in name or name.startswith('.')):
if not is_installable_dir(path):
raise InstallationError("Directory %r is not installable. File 'setup.py' not found.", name)
raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % name)
link = Link(path_to_url(name))
elif is_archive_file(path):
if not os.path.isfile(path):
@ -245,14 +246,16 @@ class InstallRequirement(object):
_run_setup_py = """
__file__ = __SETUP_PY__
from setuptools.command import egg_info
import pkg_resources
import os
def replacement_run(self):
self.mkpath(self.egg_info)
installer = self.distribution.fetch_build_egg
for ep in egg_info.iter_entry_points('egg_info.writers'):
for ep in pkg_resources.iter_entry_points('egg_info.writers'):
# require=False is the change we're making:
writer = ep.load(require=False)
if writer:
writer(self, ep.name, egg_info.os.path.join(self.egg_info,ep.name))
writer(self, ep.name, os.path.join(self.egg_info,ep.name))
self.find_sources()
egg_info.egg_info.run = replacement_run
exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
@ -365,18 +368,9 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
def assert_source_matches_version(self):
assert self.source_dir
if self.comes_from is None:
# We don't check the versions of things explicitly installed.
# This makes, e.g., "pip Package==dev" possible
return
version = self.installed_version
if version not in self.req:
logger.fatal(
'Source in %s has the version %s, which does not match the requirement %s'
% (display_path(self.source_dir), version, self))
raise InstallationError(
'Source in %s has version %s that conflicts with %s'
% (display_path(self.source_dir), version, self))
logger.warn('Requested %s, but installing version %s' % (self, self.installed_version))
else:
logger.debug('Source in %s has version %s, which satisfies requirement %s'
% (display_path(self.source_dir), version, self))
@ -446,7 +440,9 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
for installed_file in dist.get_metadata('installed-files.txt').splitlines():
path = os.path.normpath(os.path.join(egg_info_path, installed_file))
paths_to_remove.add(path)
if dist.has_metadata('top_level.txt'):
#FIXME: need a test for this elif block
#occurs with --single-version-externally-managed/--record outside of pip
elif dist.has_metadata('top_level.txt'):
if dist.has_metadata('namespace_packages.txt'):
namespaces = dist.get_metadata('namespace_packages.txt')
else:
@ -466,7 +462,7 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
'easy-install.pth')
paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg)
elif os.path.isfile(develop_egg_link):
elif develop_egg_link:
# develop egg
fh = open(develop_egg_link, 'r')
link_pointer = os.path.normcase(fh.readline().strip())
@ -685,6 +681,9 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
if self.use_user_site:
if dist_in_usersite(existing_dist):
self.conflicts_with = existing_dist
elif running_under_virtualenv() and dist_in_site_packages(existing_dist):
raise InstallationError("Will not install to the user site because it will lack sys.path precedence to %s in %s"
%(existing_dist.project_name, existing_dist.location))
else:
self.conflicts_with = existing_dist
return True
@ -890,7 +889,7 @@ class RequirementSet(object):
req.commit_uninstall()
def locate_files(self):
## FIXME: duplicates code from install_files; relevant code should
## FIXME: duplicates code from prepare_files; relevant code should
## probably be factored out into a separate method
unnamed = list(self.unnamed_requirements)
reqs = list(self.requirements.values())
@ -904,7 +903,9 @@ class RequirementSet(object):
req_to_install.check_if_exists()
if req_to_install.satisfied_by:
if self.upgrade:
req_to_install.conflicts_with = req_to_install.satisfied_by
#don't uninstall conflict if user install and and conflict is not user install
if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
req_to_install.conflicts_with = req_to_install.satisfied_by
req_to_install.satisfied_by = None
else:
install_needed = False
@ -956,7 +957,9 @@ class RequirementSet(object):
req_to_install.url = url.url
if not best_installed:
req_to_install.conflicts_with = req_to_install.satisfied_by
#don't uninstall conflict if user install and conflict is not user install
if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
req_to_install.conflicts_with = req_to_install.satisfied_by
req_to_install.satisfied_by = None
else:
install = False
@ -998,7 +1001,9 @@ class RequirementSet(object):
##occurs when the script attempts to unpack the
##build directory
# NB: This call can result in the creation of a temporary build directory
location = req_to_install.build_location(self.build_dir, not self.is_download)
## FIXME: is the existance of the checkout good enough to use it? I don't think so.
unpack = True
url = None
@ -1053,7 +1058,9 @@ class RequirementSet(object):
req_to_install.check_if_exists()
if req_to_install.satisfied_by:
if self.upgrade or self.ignore_installed:
req_to_install.conflicts_with = req_to_install.satisfied_by
#don't uninstall conflict if user install and and conflict is not user install
if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
req_to_install.conflicts_with = req_to_install.satisfied_by
req_to_install.satisfied_by = None
else:
install = False
@ -1079,7 +1086,7 @@ class RequirementSet(object):
self.add_requirement(subreq)
if req_to_install.name not in self.requirements:
self.requirements[req_to_install.name] = req_to_install
if self.is_download:
if self.is_download or req_to_install._temp_build_dir is not None:
self.reqs_to_cleanup.append(req_to_install)
else:
self.reqs_to_cleanup.append(req_to_install)
@ -1133,7 +1140,8 @@ class RequirementSet(object):
loc = location
if is_vcs_url(link):
return unpack_vcs_link(link, loc, only_download)
elif is_file_url(link):
# a local file:// index could have links with hashes
elif not link.hash and is_file_url(link):
return unpack_file_url(link, loc)
else:
if self.download_cache:
@ -1269,9 +1277,10 @@ _scheme_re = re.compile(r'^(http|https|file):', re.I)
def parse_requirements(filename, finder=None, comes_from=None, options=None):
skip_match = None
skip_regex = options.skip_requirements_regex
skip_regex = options.skip_requirements_regex if options else None
if skip_regex:
skip_match = re.compile(skip_regex)
reqs_file_dir = os.path.dirname(os.path.abspath(filename))
filename, content = get_file_content(filename, comes_from=comes_from)
for line_number, line in enumerate(content.splitlines()):
line_number += 1
@ -1303,6 +1312,10 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None):
line = line[len('--find-links'):].strip().lstrip('=')
## FIXME: it would be nice to keep track of the source of
## the find_links:
# support a find-links local path relative to a requirements file
relative_to_reqs_file = os.path.join(reqs_file_dir, line)
if os.path.exists(relative_to_reqs_file):
line = relative_to_reqs_file
if finder:
finder.find_links.append(line)
elif line.startswith('-i') or line.startswith('--index-url'):
@ -1316,6 +1329,8 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None):
line = line[len('--extra-index-url'):].strip().lstrip('=')
if finder:
finder.index_urls.append(line)
elif line.startswith('--no-index'):
finder.index_urls = []
else:
comes_from = '-r %s (line %s)' % (filename, line_number)
if line.startswith('-e') or line.startswith('--editable'):
@ -1347,7 +1362,7 @@ def parse_editable(editable_req, default_vcs=None):
if os.path.isdir(url_no_extras):
if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
raise InstallationError("Directory %r is not installable. File 'setup.py' not found.", url_no_extras)
raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % url_no_extras)
# Treating it as code that has already been checked out
url_no_extras = path_to_url(url_no_extras)
@ -1428,6 +1443,11 @@ class UninstallPathSet(object):
else:
self._refuse.add(path)
# __pycache__ files can show up after 'installed-files.txt' is created, due to imports
if os.path.splitext(path)[1] == '.py' and uses_pycache:
self.add(imp.cache_from_source(path))
def add_pth(self, pth_file, entry):
pth_file = normalize_path(pth_file)
if self._permitted(pth_file):
@ -1459,6 +1479,9 @@ class UninstallPathSet(object):
``auto_confirm`` is True)."""
if not self._can_uninstall():
return
if not self.paths:
logger.notify("Can't uninstall '%s'. No files were found to uninstall." % self.dist.project_name)
return
logger.notify('Uninstalling %s:' % self.dist.project_name)
logger.indent += 2
paths = sorted(self.compact(self.paths))

View File

@ -10,7 +10,7 @@ import tarfile
import subprocess
from pip.exceptions import InstallationError, BadCommand
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.locations import site_packages, running_under_virtualenv, virtualenv_no_global
from pip.log import logger
__all__ = ['rmtree', 'display_path', 'backup_dir',
@ -20,11 +20,20 @@ __all__ = ['rmtree', 'display_path', 'backup_dir',
'is_svn_page', 'file_contents',
'split_leading_dir', 'has_leading_dir',
'make_path_relative', 'normalize_path',
'renames', 'get_terminal_size',
'renames', 'get_terminal_size', 'get_prog',
'unzip_file', 'untar_file', 'create_download_cache_folder',
'cache_download', 'unpack_file', 'call_subprocess']
def get_prog():
try:
if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'):
return "%s -m pip" % sys.executable
except (AttributeError, TypeError, IndexError):
pass
return 'pip'
def rmtree(dir, ignore_errors=False):
shutil.rmtree(dir, ignore_errors=ignore_errors,
onerror=rmtree_errorhandler)
@ -78,7 +87,7 @@ def find_command(cmd, paths=None, pathext=None):
# check if there are funny path extensions for executables, e.g. Windows
if pathext is None:
pathext = get_pathext()
pathext = [ext for ext in pathext.lower().split(os.pathsep)]
pathext = [ext for ext in pathext.lower().split(os.pathsep) if len(ext)]
# don't use extensions if the command ends with one of them
if os.path.splitext(cmd)[1].lower() in pathext:
pathext = ['']
@ -127,10 +136,27 @@ def ask(message, options):
class _Inf(object):
"""I am bigger than everything!"""
def __cmp__(self, a):
if self is a:
return 0
return 1
def __eq__(self, other):
if self is other:
return True
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
def __lt__(self, other):
return False
def __le__(self, other):
return False
def __gt__(self, other):
return True
def __ge__(self, other):
return True
def __repr__(self):
return 'Inf'
@ -303,6 +329,12 @@ def dist_in_usersite(dist):
else:
return False
def dist_in_site_packages(dist):
"""
Return True if given Distribution is installed in distutils.sysconfig.get_python_lib().
"""
return normalize_path(dist_location(dist)).startswith(normalize_path(site_packages))
def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')):
"""
@ -325,16 +357,36 @@ def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'pyt
def egg_link_path(dist):
"""
Return the path where we'd expect to find a .egg-link file for
this distribution. (There doesn't seem to be any metadata in the
Distribution object for a develop egg that points back to its
.egg-link and easy-install.pth files).
Return the path for the .egg-link file if it exists, otherwise, None.
This won't find a globally-installed develop egg if we're in a
virtualenv.
There's 3 scenarios:
1) not in a virtualenv
try to find in site.USER_SITE, then site_packages
2) in a no-global virtualenv
try to find in site_packages
3) in a yes-global virtualenv
try to find in site_packages, then site.USER_SITE (don't look in global location)
For #1 and #3, there could be odd cases, where there's an egg-link in 2 locations.
This method will just return the first one found.
"""
return os.path.join(site_packages, dist.project_name) + '.egg-link'
sites = []
if running_under_virtualenv():
if virtualenv_no_global():
sites.append(site_packages)
else:
sites.append(site_packages)
if user_site:
sites.append(user_site)
else:
if user_site:
sites.append(user_site)
sites.append(site_packages)
for site in sites:
egglink = os.path.join(site, dist.project_name) + '.egg-link'
if os.path.isfile(egglink):
return egglink
def dist_location(dist):
@ -346,7 +398,7 @@ def dist_location(dist):
"""
egg_link = egg_link_path(dist)
if os.path.exists(egg_link):
if egg_link:
return egg_link
return dist.location

View File

@ -19,7 +19,7 @@ class VcsSupport(object):
def __init__(self):
# Register more schemes with urlparse for various version control systems
urlparse.uses_netloc.extend(self.schemes)
# Python 3.3 doesn't have uses_fragment
# Python >= 2.7.4, 3.3 doesn't have uses_fragment
if getattr(urlparse, 'uses_fragment', None):
urlparse.uses_fragment.extend(self.schemes)
super(VcsSupport, self).__init__()

View File

@ -19,8 +19,11 @@ class Bazaar(VersionControl):
def __init__(self, url=None, *args, **kwargs):
super(Bazaar, self).__init__(url, *args, **kwargs)
urlparse.non_hierarchical.extend(['lp'])
urlparse.uses_fragment.extend(['lp'])
# Python >= 2.7.4, 3.3 doesn't have uses_fragment or non_hierarchical
# Register lp but do not expose as a scheme to support bzr+lp.
if getattr(urlparse, 'uses_fragment', None):
urlparse.uses_fragment.extend(['lp'])
urlparse.non_hierarchical.extend(['lp'])
def parse_vcs_bundle_file(self, content):
url = rev = None

View File

@ -214,8 +214,7 @@ class Git(VersionControl):
def update_submodules(self, location):
if not os.path.exists(os.path.join(location, '.gitmodules')):
return
call_subprocess([self.cmd, 'submodule', 'init', '-q'], cwd=location)
call_subprocess([self.cmd, 'submodule', 'update', '--recursive', '-q'],
call_subprocess([self.cmd, 'submodule', 'update', '--init', '--recursive', '-q'],
cwd=location)
vcs.register(Git)

View File

@ -1,2 +1,5 @@
[nosetests]
where=tests
[aliases]
dev = develop easy_install pip[testing]

View File

@ -1,32 +1,39 @@
import sys
import codecs
import os
import re
import sys
from setuptools import setup
# If you change this version, change it also in docs/conf.py
version = "1.1.post2"
doc_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "docs")
index_filename = os.path.join(doc_dir, "index.txt")
news_filename = os.path.join(doc_dir, "news.txt")
def read(*parts):
return codecs.open(os.path.join(os.path.abspath(os.path.dirname(__file__)), *parts), 'r').read()
def find_version(*file_paths):
version_file = read(*file_paths)
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
long_description = """
The main website for pip is `www.pip-installer.org
<http://www.pip-installer.org>`_. You can also install
<http://www.pip-installer.org>`_. You can also install
the `in-development version <https://github.com/pypa/pip/tarball/develop#egg=pip-dev>`_
of pip with ``easy_install pip==dev``.
"""
f = open(index_filename)
# remove the toctree from sphinx index, as it breaks long_description
parts = f.read().split("split here", 2)
long_description = parts[0] + long_description + parts[2]
f.close()
f = open(news_filename)
long_description += "\n\n" + f.read()
f.close()
parts = read("docs", "index.txt").split("split here", 2)
long_description = (parts[0] + long_description + parts[2] +
"\n\n" + read("docs", "news.txt"))
tests_require = ['nose', 'virtualenv>=1.7', 'scripttest>=1.1.1', 'mock']
setup(name="pip",
version=version,
version=find_version('pip', '__init__.py'),
description="pip installs packages. Python packages. An easy_install replacement",
long_description=long_description,
classifiers=[
@ -35,7 +42,6 @@ setup(name="pip",
'License :: OSI Approved :: MIT License',
'Topic :: Software Development :: Build Tools',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.4',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
@ -51,5 +57,9 @@ setup(name="pip",
packages=['pip', 'pip.commands', 'pip.vcs'],
entry_points=dict(console_scripts=['pip=pip:main', 'pip-%s=pip:main' % sys.version[:3]]),
test_suite='nose.collector',
tests_require=['nose', 'virtualenv>=1.7', 'scripttest>=1.1.1', 'mock'],
zip_safe=False)
tests_require=tests_require,
zip_safe=False,
extras_require = {
'testing':tests_require,
},
)

View File

@ -44,19 +44,22 @@ def _create_test_package_with_submodule(env):
packages=find_packages(),
)
'''), version_pkg_path)
env.run('git', 'init', cwd=version_pkg_path)
env.run('git', 'add', '.', cwd=version_pkg_path)
env.run('git', 'init', cwd=version_pkg_path, expect_error=True)
env.run('git', 'add', '.', cwd=version_pkg_path, expect_error=True)
env.run('git', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'initial version', cwd=version_pkg_path)
'-am', 'initial version', cwd=version_pkg_path,
expect_error=True)
submodule_path = _create_test_package_submodule(env)
env.run('git', 'submodule', 'add', submodule_path, 'testpkg/static', cwd=version_pkg_path)
env.run('git', 'submodule', 'add', submodule_path, 'testpkg/static', cwd=version_pkg_path,
expect_error=True)
env.run('git', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'initial version w submodule', cwd=version_pkg_path)
'-am', 'initial version w submodule', cwd=version_pkg_path,
expect_error=True)
return version_pkg_path, submodule_path

View File

@ -1,3 +0,0 @@
<html><head><title>Links for FSPkg</title></head><body><h1>Links for FSPkg</h1><a href="./FSPkg-0.1dev.tar.gz#md5=ba6e46bed32c5b6d20f974d7d889bdb2">FSPkg-0.1dev.tar.gz</a><br/>
<a href="file://../../packages/FSPkg">Source</a><br/>
</body></html>

15
tests/indexes/README.txt Normal file
View File

@ -0,0 +1,15 @@
Details on Test Indexes
=======================
empty_with_pkg
--------------
empty index, but there's a package in the dir
in dex
------
for testing url quoting with indexes
simple
------
contains index page for "simple" pkg

View File

Binary file not shown.

View File

@ -0,0 +1,5 @@
<html>
<body>
<a href="../../../packages/simple-1.0.tar.gz#md5=4bdf78ebb7911f215c1972cf71b378f0">simple-1.0.tar.gz</a>
</body>
</html>

View File

@ -0,0 +1,5 @@
<html>
<body>
<a href="../../../packages/simple-1.0.tar.gz#md5=4bdf78ebb7911f215c1972cf71b378f0">simple-1.0.tar.gz</a>
</body>
</html>

View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from setuptools import setup
from setuptools.command import egg_info as orig_egg_info
class egg_info (orig_egg_info.egg_info):
def run(self):
orig_egg_info.egg_info.run(self)
setup(
name = "hackedegginfo",
version = '0.0.0',
cmdclass = {'egg_info':egg_info },
zip_safe = False,
)

View File

@ -2,12 +2,12 @@ import os
from setuptools import setup, find_packages
HERE = os.path.dirname(__file__)
INDEX = os.path.join(HERE, '..', '..', 'in dex', 'FSPkg')
INDEX = "file://" + os.path.join(HERE, '..', '..', 'indexes', 'simple', 'simple')
setup(
name='LocalExtras',
version='0.0.1',
packages=find_packages(),
extras_require={ 'bar': ['FSPkg'] },
dependency_links=['file://' + INDEX]
extras_require={ 'bar': ['simple'] },
dependency_links=[INDEX]
)

View File

@ -1,6 +1,70 @@
This package exists for testing uninstall-rollback.
Details on Test Packages
========================
broken-0.1.tar.gz
-----------------
This package exists for testing uninstall-rollback.
broken-0.2broken.tar.gz
-----------------------
Version 0.2broken has a setup.py crafted to fail on install (and only on
install). If any earlier step would fail (i.e. egg-info-generation), the
already-installed version would never be uninstalled, so uninstall-rollback
would not come into play.
BrokenEmitsUTF8
---------------
for generating unicode error in py3.x
duplicate-1.0.tar.gz
--------------------
for testing finding dupes across multiple find-links
FSPkg
-----
for installing from the file system
gmpy-1.15.tar.gz
----------------
hash testing (altough this pkg isn't needed explicitly)
gmpy2-2.0.tar.gz
----------------
for testing finder logic when name *contains* the name of the package specified
HackedEggInfo
-------------
has it's own egg_info class
LineEndings
-----------
contains DOS line endings
LocalExtras
-----------
has an extra in a local file:// dependency link
parent/child-0.1.tar.gz
-----------------------
The parent-0.1.tar.gz and child-0.1.tar.gz packages are used by
test_uninstall:test_uninstall_overlapping_package.
paxpkg.tar.bz2
--------------
tar with pax headers
pkgwithmpkg-1.0.tar.gz; pkgwithmpkg-1.0-py2.7-macosx10.7.mpkg.zip
-----------------------------------------------------------------
used for osx test case (tests.test_finder:test_no_mpkg)
simple-[123].0.tar.gz
---------------------
contains "simple" package; good for basic testing and version logic.

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@ -5,7 +5,6 @@ import textwrap
import sys
from os.path import abspath, join, curdir, pardir
from nose import SkipTest
from nose.tools import assert_raises
from mock import patch
@ -376,6 +375,16 @@ def test_install_with_pax_header():
run_pip('install', 'paxpkg.tar.bz2', cwd=run_from)
def test_install_with_hacked_egg_info():
"""
test installing a package which defines its own egg_info class
"""
reset_env()
run_from = abspath(join(here, 'packages', 'HackedEggInfo'))
result = run_pip('install', '.', cwd=run_from)
assert 'Successfully installed hackedegginfo\n' in result.stdout
def test_install_using_install_option_and_editable():
"""
Test installing a tool using -e and --install-option

View File

@ -17,6 +17,7 @@ def test_cleanup_after_install_from_pypi():
src = env.scratch_path/"src"
assert not exists(build), "build/ dir still exists: %s" % build
assert not exists(src), "unexpected src/ dir exists: %s" % src
env.assert_no_temp()
def test_cleanup_after_install_editable_from_hg():
@ -34,6 +35,7 @@ def test_cleanup_after_install_editable_from_hg():
src = env.venv_path/'src'
assert not exists(build), "build/ dir still exists: %s" % build
assert exists(src), "expected src/ dir doesn't exist: %s" % src
env.assert_no_temp()
def test_cleanup_after_install_from_local_directory():
@ -48,6 +50,7 @@ def test_cleanup_after_install_from_local_directory():
src = env.venv_path/'src'
assert not exists(build), "unexpected build/ dir exists: %s" % build
assert not exists(src), "unexpected src/ dir exist: %s" % src
env.assert_no_temp()
def test_cleanup_after_create_bundle():
@ -79,6 +82,7 @@ def test_cleanup_after_create_bundle():
src_bundle = env.scratch_path/"src-bundle"
assert not exists(build_bundle), "build-bundle/ dir still exists: %s" % build_bundle
assert not exists(src_bundle), "src-bundle/ dir still exists: %s" % src_bundle
env.assert_no_temp()
# Make sure previously created src/ from editable still exists
assert exists(src), "expected src dir doesn't exist: %s" % src
@ -96,6 +100,25 @@ def test_no_install_and_download_should_not_leave_build_dir():
assert not os.path.exists(env.venv_path/'/build'), "build/ dir should be deleted"
def test_cleanup_req_satisifed_no_name():
"""
Test cleanup when req is already satisfied, and req has no 'name'
"""
#this test confirms Issue #420 is fixed
#reqs with no 'name' that were already satisfied were leaving behind tmp build dirs
#2 examples of reqs that would do this
# 1) https://bitbucket.org/ianb/initools/get/tip.zip
# 2) parent-0.1.tar.gz
dist = abspath(join(here, 'packages', 'parent-0.1.tar.gz'))
env = reset_env()
result = run_pip('install', dist)
result = run_pip('install', dist)
build = env.venv_path/'build'
assert not exists(build), "unexpected build/ dir exists: %s" % build
env.assert_no_temp()
def test_download_should_not_delete_existing_build_dir():
"""
It should not delete build/ if existing before run the command

View File

@ -45,7 +45,7 @@ def test_completion_for_unknown_shell():
Test getting completion for an unknown shell
"""
reset_env()
error_msg = 'error: no such option: --myfooshell'
error_msg = 'no such option: --myfooshell'
result = run_pip('completion', '--myfooshell', expect_error=True)
assert error_msg in result.stderr, 'tests for an unknown shell failed'

View File

@ -39,3 +39,32 @@ def test_download_should_download_dependencies():
openid_tarball_prefix = str(Path('scratch')/ 'python-openid-')
assert any(path.startswith(openid_tarball_prefix) for path in result.files_created)
assert env.site_packages/ 'openid' not in result.files_created
def test_download_should_skip_existing_files():
"""
It should not download files already existing in the scratch dir
"""
env = reset_env()
write_file('test-req.txt', textwrap.dedent("""
INITools==0.1
"""))
result = run_pip('install', '-r', env.scratch_path/ 'test-req.txt', '-d', '.', expect_error=True)
assert Path('scratch')/ 'INITools-0.1.tar.gz' in result.files_created
assert env.site_packages/ 'initools' not in result.files_created
# adding second package to test-req.txt
write_file('test-req.txt', textwrap.dedent("""
INITools==0.1
python-openid==2.2.5
"""))
# only the second package should be downloaded
result = run_pip('install', '-r', env.scratch_path/ 'test-req.txt', '-d', '.', expect_error=True)
openid_tarball_prefix = str(Path('scratch')/ 'python-openid-')
assert any(path.startswith(openid_tarball_prefix) for path in result.files_created)
assert Path('scratch')/ 'INITools-0.1.tar.gz' not in result.files_created
assert env.site_packages/ 'initools' not in result.files_created
assert env.site_packages/ 'openid' not in result.files_created

View File

@ -1,16 +0,0 @@
from pip.backwardcompat import urllib
from tests.test_pip import here, reset_env, run_pip, pyversion
from tests.path import Path
index_url = 'file://' + urllib.quote(str(Path(here).abspath/'in dex').replace('\\', '/'))
def test_install():
"""
Test installing from a local index.
"""
env = reset_env()
result = run_pip('install', '-vvv', '--index-url', index_url, 'FSPkg', expect_error=False)
assert (env.site_packages/'fspkg') in result.files_created, str(result.stdout)
assert (env.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion) in result.files_created, str(result)

38
tests/test_find_links.py Normal file
View File

@ -0,0 +1,38 @@
import textwrap
from tests.test_pip import reset_env, run_pip, pyversion, here, write_file
def test_find_links_relative_path():
"""Test find-links as a relative path."""
e = reset_env()
result = run_pip(
'install',
'parent==0.1',
'--no-index',
'--find-links',
'packages/',
cwd=here)
egg_info_folder = e.site_packages / 'parent-0.1-py%s.egg-info' % pyversion
initools_folder = e.site_packages / 'parent'
assert egg_info_folder in result.files_created, str(result)
assert initools_folder in result.files_created, str(result)
def test_find_links_requirements_file_relative_path():
"""Test find-links as a relative path to a reqs file."""
e = reset_env()
write_file('test-req.txt', textwrap.dedent("""
--no-index
--find-links=../../../packages/
parent==0.1
"""))
result = run_pip(
'install',
'-r',
e.scratch_path / "test-req.txt",
cwd=here)
egg_info_folder = e.site_packages / 'parent-0.1-py%s.egg-info' % pyversion
initools_folder = e.site_packages / 'parent'
assert egg_info_folder in result.files_created, str(result)
assert initools_folder in result.files_created, str(result)

View File

@ -1,12 +1,15 @@
from pkg_resources import parse_version
from pip.backwardcompat import urllib
from pip.req import InstallRequirement
from pip.index import PackageFinder
from pip.exceptions import BestVersionAlreadyInstalled
from tests.path import Path
from tests.test_pip import here
from nose.tools import assert_raises
from mock import Mock
find_links = 'file://' + urllib.quote(str(Path(here).abspath/'packages').replace('\\', '/'))
find_links2 = 'file://' + urllib.quote(str(Path(here).abspath/'packages2').replace('\\', '/'))
def test_no_mpkg():
@ -25,3 +28,50 @@ def test_no_partial_name_match():
found = finder.find_requirement(req, False)
assert found.url.endswith("gmpy-1.15.tar.gz"), found
def test_duplicates_sort_ok():
"""Finder successfully finds one of a set of duplicates in different
locations"""
finder = PackageFinder([find_links, find_links2], [])
req = InstallRequirement.from_line("duplicate")
found = finder.find_requirement(req, False)
assert found.url.endswith("duplicate-1.0.tar.gz"), found
def test_finder_detects_latest_find_links():
"""Test PackageFinder detects latest using find-links"""
req = InstallRequirement.from_line('simple', None)
finder = PackageFinder([find_links], [])
link = finder.find_requirement(req, False)
assert link.url.endswith("simple-3.0.tar.gz")
def test_finder_detects_latest_already_satisfied_find_links():
"""Test PackageFinder detects latest already satisified using find-links"""
req = InstallRequirement.from_line('simple', None)
#the latest simple in local pkgs is 3.0
latest_version = "3.0"
satisfied_by = Mock(
location = "/path",
parsed_version = parse_version(latest_version),
version = latest_version
)
req.satisfied_by = satisfied_by
finder = PackageFinder([find_links], [])
assert_raises(BestVersionAlreadyInstalled, finder.find_requirement, req, True)
def test_finder_detects_latest_already_satisfied_pypi_links():
"""Test PackageFinder detects latest already satisified using pypi links"""
req = InstallRequirement.from_line('initools', None)
#the latest initools on pypi is 0.3.1
latest_version = "0.3.1"
satisfied_by = Mock(
location = "/path",
parsed_version = parse_version(latest_version),
version = latest_version
)
req.satisfied_by = satisfied_by
finder = PackageFinder([], ["http://pypi.python.org/simple"])
assert_raises(BestVersionAlreadyInstalled, finder.find_requirement, req, True)

View File

@ -91,7 +91,7 @@ def test_freeze_git_clone():
expected = textwrap.dedent("""\
Script result: ...pip freeze
-- stdout: --------------------
-e %s@...#egg=pip_test_package-...
...-e %s@...#egg=pip_test_package-...
...""" % local_checkout('git+http://github.com/pypa/pip-test-package.git'))
_check_output(result, expected)
@ -101,7 +101,7 @@ def test_freeze_git_clone():
expected = textwrap.dedent("""\
Script result: pip freeze -f %(repo)s#egg=pip_test_package
-- stdout: --------------------
-f %(repo)s#egg=pip_test_package
-f %(repo)s#egg=pip_test_package...
-e %(repo)s@...#egg=pip_test_package-dev
...""" % {'repo': local_checkout('git+http://github.com/pypa/pip-test-package.git')})
_check_output(result, expected)
@ -124,7 +124,7 @@ def test_freeze_mercurial_clone():
expected = textwrap.dedent("""\
Script result: ...pip freeze
-- stdout: --------------------
-e %s@...#egg=django_authority-...
...-e %s@...#egg=django_authority-...
...""" % local_checkout('hg+http://bitbucket.org/jezdez/django-authority'))
_check_output(result, expected)
@ -135,7 +135,7 @@ def test_freeze_mercurial_clone():
Script result: ...pip freeze -f %(repo)s#egg=django_authority
-- stdout: --------------------
-f %(repo)s#egg=django_authority
-e %(repo)s@...#egg=django_authority-dev
...-e %(repo)s@...#egg=django_authority-dev
...""" % {'repo': local_checkout('hg+http://bitbucket.org/jezdez/django-authority')})
_check_output(result, expected)
@ -156,7 +156,7 @@ def test_freeze_bazaar_clone():
expected = textwrap.dedent("""\
Script result: ...pip freeze
-- stdout: --------------------
-e %s@...#egg=django_wikiapp-...
...-e %s@...#egg=django_wikiapp-...
...""" % local_checkout('bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1'))
_check_output(result, expected)
@ -168,7 +168,7 @@ def test_freeze_bazaar_clone():
Script result: ...pip freeze -f %(repo)s/#egg=django-wikiapp
-- stdout: --------------------
-f %(repo)s/#egg=django-wikiapp
-e %(repo)s@...#egg=django_wikiapp-...
...-e %(repo)s@...#egg=django_wikiapp-...
...""" % {'repo':
local_checkout('bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1')})
_check_output(result, expected)

194
tests/test_hashes.py Normal file
View File

@ -0,0 +1,194 @@
import os
from nose.tools import assert_raises
from pip.download import _get_hash_from_file, _check_hash
from pip.exceptions import InstallationError
from pip.index import Link
def test_get_hash_from_file_md5():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash.digest_size == 16
assert download_hash.hexdigest() == "d41d8cd98f00b204e9800998ecf8427e"
def test_get_hash_from_file_sha1():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash.digest_size == 20
assert download_hash.hexdigest() == "da39a3ee5e6b4b0d3255bfef95601890afd80709"
def test_get_hash_from_file_sha224():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha224=d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash.digest_size == 28
assert download_hash.hexdigest() == "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"
def test_get_hash_from_file_sha384():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash.digest_size == 48
assert download_hash.hexdigest() == "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"
def test_get_hash_from_file_sha256():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash.digest_size == 32
assert download_hash.hexdigest() == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
def test_get_hash_from_file_sha512():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash.digest_size == 64
assert download_hash.hexdigest() == "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
def test_get_hash_from_file_unknown():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#unknown_hash=d41d8cd98f00b204e9800998ecf8427e")
download_hash = _get_hash_from_file(file_path, file_link)
assert download_hash is None
def test_check_hash_md5_valid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e")
download_hash = _get_hash_from_file(file_path, file_link)
_check_hash(download_hash, file_link)
def test_check_hash_md5_invalid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=deadbeef")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, file_link)
def test_check_hash_sha1_valid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709")
download_hash = _get_hash_from_file(file_path, file_link)
_check_hash(download_hash, file_link)
def test_check_hash_sha1_invalid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha1=deadbeef")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, file_link)
def test_check_hash_sha224_valid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha224=d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f'")
download_hash = _get_hash_from_file(file_path, file_link)
_check_hash(download_hash, file_link)
def test_check_hash_sha224_invalid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha224=deadbeef")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, file_link)
def test_check_hash_sha384_valid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")
download_hash = _get_hash_from_file(file_path, file_link)
_check_hash(download_hash, file_link)
def test_check_hash_sha384_invalid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha384=deadbeef")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, file_link)
def test_check_hash_sha256_valid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
download_hash = _get_hash_from_file(file_path, file_link)
_check_hash(download_hash, file_link)
def test_check_hash_sha256_invalid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=deadbeef")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, file_link)
def test_check_hash_sha512_valid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")
download_hash = _get_hash_from_file(file_path, file_link)
_check_hash(download_hash, file_link)
def test_check_hash_sha512_invalid():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha512=deadbeef")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, file_link)
def test_check_hasher_mismsatch():
file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "packages", "gmpy-1.15.tar.gz")
file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e")
other_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
download_hash = _get_hash_from_file(file_path, file_link)
assert_raises(InstallationError, _check_hash, download_hash, other_link)

View File

@ -1,4 +1,11 @@
from pip.index import package_to_requirement, HTMLPage
import os
from pip.backwardcompat import urllib
from tests.path import Path
from pip.index import package_to_requirement, HTMLPage, get_mirrors, DEFAULT_MIRROR_HOSTNAME
from pip.index import PackageFinder, Link, InfLink
from tests.test_pip import reset_env, run_pip, pyversion, here
from string import ascii_lowercase
from mock import patch
def test_package_name_should_be_converted_to_requirement():
@ -26,3 +33,75 @@ def test_html_page_should_be_able_to_scrap_rel_links():
assert len(links) == 1
assert links[0].url == 'http://supervisord.org/'
@patch('socket.gethostbyname_ex')
def test_get_mirrors(mock_gethostbyname_ex):
# Test when the expected result comes back
# from socket.gethostbyname_ex
mock_gethostbyname_ex.return_value = ('g.pypi.python.org', [DEFAULT_MIRROR_HOSTNAME], ['129.21.171.98'])
mirrors = get_mirrors()
# Expect [a-g].pypi.python.org, since last mirror
# is returned as g.pypi.python.org
assert len(mirrors) == 7
for c in "abcdefg":
assert c + ".pypi.python.org" in mirrors
@patch('socket.gethostbyname_ex')
def test_get_mirrors_no_cname(mock_gethostbyname_ex):
# Test when the UNexpected result comes back
# from socket.gethostbyname_ex
# (seeing this in Japan and was resulting in 216k
# invalid mirrors and a hot CPU)
mock_gethostbyname_ex.return_value = (DEFAULT_MIRROR_HOSTNAME, [DEFAULT_MIRROR_HOSTNAME], ['129.21.171.98'])
mirrors = get_mirrors()
# Falls back to [a-z].pypi.python.org
assert len(mirrors) == 26
for c in ascii_lowercase:
assert c + ".pypi.python.org" in mirrors
def test_sort_locations_file_find_link():
"""
Test that a file:// find-link dir gets listdir run
"""
find_links_url = 'file://' + os.path.join(here, 'packages')
find_links = [find_links_url]
finder = PackageFinder(find_links, [])
files, urls = finder._sort_locations(find_links)
assert files and not urls, "files and not urls should have been found at find-links url: %s" % find_links_url
def test_sort_locations_file_not_find_link():
"""
Test that a file:// url dir that's not a find-link, doesn't get a listdir run
"""
index_url = 'file://' + os.path.join(here, 'indexes', 'empty_with_pkg')
finder = PackageFinder([], [])
files, urls = finder._sort_locations([index_url])
assert urls and not files, "urls, but not files should have been found"
def test_install_from_file_index_hash_link():
"""
Test that a pkg can be installed from a file:// index using a link with a hash
"""
env = reset_env()
index_url = 'file://' + os.path.join(here, 'indexes', 'simple')
result = run_pip('install', '-i', index_url, 'simple==1.0')
egg_info_folder = env.site_packages / 'simple-1.0-py%s.egg-info' % pyversion
assert egg_info_folder in result.files_created, str(result)
def test_file_index_url_quoting():
"""
Test url quoting of file index url with a space
"""
index_url = 'file://' + urllib.quote(str(Path(here).abspath/'indexes'/'in dex').replace('\\', '/'))
env = reset_env()
result = run_pip('install', '-vvv', '--index-url', index_url, 'simple', expect_error=False)
assert (env.site_packages/'simple') in result.files_created, str(result.stdout)
assert (env.site_packages/'simple-1.0-py%s.egg-info' % pyversion) in result.files_created, str(result)
def test_inflink_greater():
"""Test InfLink compares greater."""
assert InfLink > Link(object())

View File

@ -314,12 +314,18 @@ class TestPipEnvironment(TestFileEnvironment):
assert self.venv_path == virtualenv_paths[0] # sanity check
for id, path in zip(('venv', 'lib', 'include', 'bin'), virtualenv_paths):
#fix for virtualenv issue #306
if hasattr(sys, "pypy_version_info") and id == 'lib':
path = os.path.join(self.venv_path, 'lib-python', pyversion)
setattr(self, id+'_path', Path(path))
setattr(self, id, relpath(self.root_path, path))
assert self.venv == TestPipEnvironment.venv # sanity check
self.site_packages = self.lib/'site-packages'
if hasattr(sys, "pypy_version_info"):
self.site_packages = self.venv/'site-packages'
else:
self.site_packages = self.lib/'site-packages'
self.user_base_path = self.venv_path/'user'
self.user_site_path = self.venv_path/'user'/site_packages_suffix
@ -362,6 +368,10 @@ class TestPipEnvironment(TestFileEnvironment):
if sitecustomize:
self._add_to_sitecustomize(sitecustomize)
# Ensure that $TMPDIR exists (because we use start_clear=False, it's not created for us)
if self.temp_path and not os.path.exists(self.temp_path):
os.makedirs(self.temp_path)
def _ignore_file(self, fn):
if fn.endswith('__pycache__') or fn.endswith(".pyc"):
result = True
@ -444,12 +454,18 @@ class FastTestPipEnvironment(TestPipEnvironment):
virtualenv_paths = virtualenv.path_locations(self.venv_path)
for id, path in zip(('venv', 'lib', 'include', 'bin'), virtualenv_paths):
#fix for virtualenv issue #306
if hasattr(sys, "pypy_version_info") and id == 'lib':
path = os.path.join(self.venv_path, 'lib-python', pyversion)
setattr(self, id+'_path', Path(path))
setattr(self, id, relpath(self.root_path, path))
assert self.venv == TestPipEnvironment.venv # sanity check
self.site_packages = self.lib/'site-packages'
if hasattr(sys, "pypy_version_info"):
self.site_packages = self.venv/'site-packages'
else:
self.site_packages = self.lib/'site-packages'
self.user_base_path = self.venv_path/'user'
self.user_site_path = self.venv_path/'user'/'lib'/self.lib.name/'site-packages'
@ -510,6 +526,10 @@ class FastTestPipEnvironment(TestPipEnvironment):
assert self.root_path.exists
# Ensure that $TMPDIR exists (because we use start_clear=False, it's not created for us)
if self.temp_path and not os.path.exists(self.temp_path):
os.makedirs(self.temp_path)
def __del__(self):
pass # shutil.rmtree(str(self.root_path), ignore_errors=True)
@ -663,5 +683,5 @@ def _change_test_package_version(env, version_pkg_path):
if __name__ == '__main__':
sys.stderr.write("Run pip's tests using nosetests. Requires virtualenv, ScriptTest, and nose.\n")
sys.stderr.write("Run pip's tests using nosetests. Requires virtualenv, ScriptTest, mock, and nose.\n")
sys.exit(1)

View File

@ -80,20 +80,23 @@ def test_multiple_requirements_files():
def test_respect_order_in_requirements_file():
env = reset_env()
write_file('frameworks-req.txt', textwrap.dedent("""\
bidict
ordereddict
initools
parent
child
simple
"""))
result = run_pip('install', '-r', env.scratch_path / 'frameworks-req.txt')
find_links = 'file://' + os.path.join(here, 'packages')
result = run_pip('install', '--no-index', '-f', find_links, '-r', env.scratch_path / 'frameworks-req.txt')
downloaded = [line for line in result.stdout.split('\n')
if 'Downloading/unpacking' in line]
assert 'bidict' in downloaded[0], 'First download should ' \
'be "bidict" but was "%s"' % downloaded[0]
assert 'ordereddict' in downloaded[1], 'Second download should ' \
'be "ordereddict" but was "%s"' % downloaded[1]
assert 'initools' in downloaded[2], 'Third download should ' \
'be "initools" but was "%s"' % downloaded[2]
assert 'parent' in downloaded[0], 'First download should ' \
'be "parent" but was "%s"' % downloaded[0]
assert 'child' in downloaded[1], 'Second download should ' \
'be "child" but was "%s"' % downloaded[1]
assert 'simple' in downloaded[2], 'Third download should ' \
'be "simple" but was "%s"' % downloaded[2]
def test_requirements_data_structure_keeps_order():
@ -121,19 +124,22 @@ def test_requirements_data_structure_implements__contains__():
assert 'pip' in requirements
assert 'nose' not in requirements
@patch('os.path.normcase')
@patch('pip.req.os.getcwd')
@patch('pip.req.os.path.exists')
@patch('pip.req.os.path.isdir')
def test_parse_editable_local(isdir_mock, exists_mock, getcwd_mock):
def test_parse_editable_local(isdir_mock, exists_mock, getcwd_mock, normcase_mock):
exists_mock.return_value = isdir_mock.return_value = True
getcwd_mock.return_value = "/some/path"
# mocks needed to support path operations on windows tests
normcase_mock.return_value = getcwd_mock.return_value = "/some/path"
assert_equal(
parse_editable('.', 'git'),
(None, 'file:///some/path', None)
)
normcase_mock.return_value = "/some/path/foo"
assert_equal(
parse_editable('foo', 'git'),
(None, 'file://' + os.path.join("/some/path", 'foo'), None)
(None, 'file:///some/path/foo', None)
)
def test_parse_editable_default_vcs():
@ -154,19 +160,21 @@ def test_parse_editable_vcs_extras():
('foo[extras]', 'svn+https://foo#egg=foo[extras]', None)
)
@patch('os.path.normcase')
@patch('pip.req.os.getcwd')
@patch('pip.req.os.path.exists')
@patch('pip.req.os.path.isdir')
def test_parse_editable_local_extras(isdir_mock, exists_mock, getcwd_mock):
def test_parse_editable_local_extras(isdir_mock, exists_mock, getcwd_mock, normcase_mock):
exists_mock.return_value = isdir_mock.return_value = True
getcwd_mock.return_value = "/some/path"
normcase_mock.return_value = getcwd_mock.return_value = "/some/path"
assert_equal(
parse_editable('.[extras]', 'git'),
(None, 'file://' + "/some/path", ('extras',))
)
normcase_mock.return_value = "/some/path/foo"
assert_equal(
parse_editable('foo[bar,baz]', 'git'),
(None, 'file://' + os.path.join("/some/path", 'foo'), ('bar', 'baz'))
(None, 'file:///some/path/foo', ('bar', 'baz'))
)
def test_install_local_editable_with_extras():
@ -175,4 +183,4 @@ def test_install_local_editable_with_extras():
res = run_pip('install', '-e', to_install + '[bar]', expect_error=False)
assert env.site_packages/'easy-install.pth' in res.files_updated
assert env.site_packages/'LocalExtras.egg-link' in res.files_created
assert env.site_packages/'fspkg' in res.files_created
assert env.site_packages/'simple' in res.files_created

88
tests/test_show.py Normal file
View File

@ -0,0 +1,88 @@
import re
from pip import __version__
from pip.commands.show import search_packages_info
from tests.test_pip import reset_env, run_pip
def test_show():
"""
Test end to end test for show command.
"""
reset_env()
result = run_pip('show', 'pip')
lines = result.stdout.split('\n')
assert len(lines) == 6
assert lines[0] == '---', lines[0]
assert lines[1] == 'Name: pip', lines[1]
assert lines[2] == 'Version: %s' % __version__, lines[2]
assert lines[3].startswith('Location: '), lines[3]
assert lines[4] == 'Requires: '
def test_show_with_files_not_found():
"""
Test for show command with installed files listing enabled and
installed-files.txt not found.
"""
reset_env()
result = run_pip('show', '-f', 'pip')
lines = result.stdout.split('\n')
assert len(lines) == 8
assert lines[0] == '---', lines[0]
assert lines[1] == 'Name: pip', lines[1]
assert lines[2] == 'Version: %s' % __version__, lines[2]
assert lines[3].startswith('Location: '), lines[3]
assert lines[4] == 'Requires: '
assert lines[5] == 'Files:', lines[4]
assert lines[6] == 'Cannot locate installed-files.txt', lines[5]
def test_show_with_all_files():
"""
Test listing all files in the show command.
"""
reset_env()
result = run_pip('install', 'initools==0.2')
result = run_pip('show', '--files', 'initools')
assert re.search(r"Files:\n( .+\n)+", result.stdout)
def test_missing_argument():
"""
Test show command with no arguments.
"""
reset_env()
result = run_pip('show')
assert 'ERROR: Please provide a project name or names.' in result.stdout
def test_find_package_not_found():
"""
Test trying to get info about a nonexistent package.
"""
result = search_packages_info(['abcd3'])
assert len(list(result)) == 0
def test_search_any_case():
"""
Search for a package in any case.
"""
result = list(search_packages_info(['PIP']))
assert len(result) == 1
assert 'pip' == result[0]['name']
def test_more_than_one_package():
"""
Search for more than one package.
"""
result = list(search_packages_info(['Pip', 'Nose', 'Virtualenv']))
assert len(result) == 3

View File

@ -66,3 +66,24 @@ def test_sitecustomize_not_growing_in_fast_environment():
size2 = os.stat(sc2).st_size
assert size1==size2, "size before, %d != size after, %d" %(size1, size2)
def test_tmp_dir_exists_in_env():
"""
Test that $TMPDIR == env.temp_path and path exists, and env.assert_no_temp() passes
"""
#need these tests to ensure the assert_no_temp feature of scripttest is working
env = reset_env(use_distribute=True)
env.assert_no_temp() #this fails if env.tmp_path doesn't exist
assert env.environ['TMPDIR'] == env.temp_path
assert isdir(env.temp_path)
def test_tmp_dir_exists_in_fast_env():
"""
Test that $TMPDIR == env.temp_path and path exists and env.assert_no_temp() passes (in fast env)
"""
#need these tests to ensure the assert_no_temp feature of scripttest is working
env = reset_env()
env.assert_no_temp() #this fails if env.tmp_path doesn't exist
assert env.environ['TMPDIR'] == env.temp_path
assert isdir(env.temp_path)

View File

@ -20,6 +20,6 @@ def test_install_package_that_emits_unicode():
env = reset_env()
to_install = os.path.abspath(os.path.join(here, 'packages', 'BrokenEmitsUTF8'))
result = run_pip('install', to_install, expect_error=True)
assert '__main__.FakeError: this package designed to fail on install' in result.stdout
result = run_pip('install', to_install, expect_error=True, expect_temp=True, quiet=True)
assert 'FakeError: this package designed to fail on install' in result.stdout
assert 'UnicodeDecodeError' not in result.stdout

View File

@ -1,7 +1,10 @@
from __future__ import with_statement
import textwrap
import sys
from os.path import join, abspath
from os.path import join, abspath, normpath
from tempfile import mkdtemp
from mock import patch
from tests.test_pip import here, reset_env, run_pip, assert_all_changes, write_file, pyversion
from tests.local_repos import local_repo, local_checkout
@ -16,6 +19,8 @@ def test_simple_uninstall():
env = reset_env()
result = run_pip('install', 'INITools==0.2')
assert join(env.site_packages, 'initools') in result.files_created, sorted(result.files_created.keys())
#the import forces the generation of __pycache__ if the version of python supports it
env.run('python', '-c', "import initools")
result2 = run_pip('uninstall', 'INITools', '-y')
assert_all_changes(result, result2, [env.venv/'build', 'cache'])
@ -34,6 +39,19 @@ def test_uninstall_with_scripts():
assert_all_changes(result, result2, [env.venv/'build', 'cache'])
def test_uninstall_easy_install_after_import():
"""
Uninstall an easy_installed package after it's been imported
"""
env = reset_env()
result = env.run('easy_install', 'INITools==0.2', expect_stderr=True)
#the import forces the generation of __pycache__ if the version of python supports it
env.run('python', '-c', "import initools")
result2 = run_pip('uninstall', 'INITools', '-y')
assert_all_changes(result, result2, [env.venv/'build', 'cache'])
def test_uninstall_namespace_package():
"""
Uninstall a distribution with a namespace package without clobbering
@ -48,6 +66,33 @@ def test_uninstall_namespace_package():
assert join(env.site_packages, 'pd', 'find') in result2.files_deleted, sorted(result2.files_deleted.keys())
def test_uninstall_overlapping_package():
"""
Uninstalling a distribution that adds modules to a pre-existing package
should only remove those added modules, not the rest of the existing
package.
See: GitHub issue #355 (pip uninstall removes things it didn't install)
"""
parent_pkg = abspath(join(here, 'packages', 'parent-0.1.tar.gz'))
child_pkg = abspath(join(here, 'packages', 'child-0.1.tar.gz'))
env = reset_env()
result1 = run_pip('install', parent_pkg, expect_error=False)
assert join(env.site_packages, 'parent') in result1.files_created, sorted(result1.files_created.keys())
result2 = run_pip('install', child_pkg, expect_error=False)
assert join(env.site_packages, 'child') in result2.files_created, sorted(result2.files_created.keys())
assert normpath(join(env.site_packages, 'parent/plugins/child_plugin.py')) in result2.files_created, sorted(result2.files_created.keys())
#the import forces the generation of __pycache__ if the version of python supports it
env.run('python', '-c', "import parent.plugins.child_plugin, child")
result3 = run_pip('uninstall', '-y', 'child', expect_error=False)
assert join(env.site_packages, 'child') in result3.files_deleted, sorted(result3.files_created.keys())
assert normpath(join(env.site_packages, 'parent/plugins/child_plugin.py')) in result3.files_deleted, sorted(result3.files_deleted.keys())
assert join(env.site_packages, 'parent') not in result3.files_deleted, sorted(result3.files_deleted.keys())
# Additional check: uninstalling 'child' should return things to the
# previous state, without unintended side effects.
assert_all_changes(result2, result3, [])
def test_uninstall_console_scripts():
"""
Test uninstalling a package with more files (console_script entry points, extra directories).
@ -155,3 +200,42 @@ def test_uninstall_as_egg():
result2 = run_pip('uninstall', 'FSPkg', '-y', expect_error=True)
assert_all_changes(result, result2, [env.venv/'build', 'cache'])
@patch('pip.req.logger')
def test_uninstallpathset_no_paths(mock_logger):
"""
Test UninstallPathSet logs notification when there are no paths to uninstall
"""
from pip.req import UninstallPathSet
from pkg_resources import get_distribution
test_dist = get_distribution('pip')
# ensure that the distribution is "local"
with patch("pip.req.dist_is_local") as mock_dist_is_local:
mock_dist_is_local.return_value = True
uninstall_set = UninstallPathSet(test_dist)
uninstall_set.remove() #with no files added to set
mock_logger.notify.assert_any_call("Can't uninstall 'pip'. No files were found to uninstall.")
@patch('pip.req.logger')
def test_uninstallpathset_non_local(mock_logger):
"""
Test UninstallPathSet logs notification and returns (with no exception) when dist is non-local
"""
from pip.req import UninstallPathSet
from pkg_resources import get_distribution
test_dist = get_distribution('pip')
test_dist.location = "/NON_LOCAL"
# ensure that the distribution is "non-local"
# setting location isn't enough, due to egg-link file checking for
# develop-installs
with patch("pip.req.dist_is_local") as mock_dist_is_local:
mock_dist_is_local.return_value = False
uninstall_set = UninstallPathSet(test_dist)
uninstall_set.remove() #with no files added to set; which is the case when trying to remove non-local dists
mock_logger.notify.assert_any_call("Not uninstalling pip at /NON_LOCAL, outside environment %s" % sys.prefix)

View File

@ -49,11 +49,12 @@ def test_upgrade_with_newest_already_installed():
not be reinstalled and the user should be informed.
"""
find_links = 'file://' + join(here, 'packages')
env = reset_env()
run_pip('install', 'INITools')
result = run_pip('install', '--upgrade', 'INITools')
assert not result.files_created, 'pip install --upgrade INITools upgraded when it should not have'
assert 'already up-to-date' in result.stdout
run_pip('install', '-f', find_links, '--no-index', 'simple')
result = run_pip('install', '--upgrade', '-f', find_links, '--no-index', 'simple')
assert not result.files_created, 'simple upgraded when it should not have'
assert 'already up-to-date' in result.stdout, result.stdout
def test_upgrade_force_reinstall_newest():
@ -207,8 +208,8 @@ def test_upgrade_vcs_req_with_no_dists_found():
def test_upgrade_vcs_req_with_dist_found():
"""It can upgrade a VCS requirement that has distributions on the index."""
reset_env()
req = "%s#egg=virtualenv" % local_checkout(
"git+git://github.com/pypa/virtualenv@c21fef2c2d53cf19f49bcc37f9c058a33fb50499")
# TODO(pnasrat) Using local_checkout fails on windows - oddness with the test path urls/git.
req = "%s#egg=virtualenv" % "git+git://github.com/pypa/virtualenv@c21fef2c2d53cf19f49bcc37f9c058a33fb50499"
run_pip("install", req)
result = run_pip("install", "-U", req)
assert not "pypi.python.org" in result.stdout, result.stdout

View File

@ -6,7 +6,15 @@ import sys
from os.path import abspath, join, curdir, isdir, isfile
from nose import SkipTest
from tests.local_repos import local_checkout
from tests.test_pip import here, reset_env, run_pip, pyversion
from tests.test_pip import here, reset_env, run_pip, pyversion, assert_all_changes
patch_dist_in_site_packages = """
def dist_in_site_packages(dist):
return False
import pip
pip.util.dist_in_site_packages=dist_in_site_packages
"""
def test_install_curdir_usersite_fails_in_old_python():
@ -27,6 +35,10 @@ class Tests_UserSite:
# --user only works on 2.6 or higher
if sys.version_info < (2, 6):
raise SkipTest()
# --user option is broken in pypy
if hasattr(sys, "pypy_version_info"):
raise SkipTest()
def test_reset_env_system_site_packages_usersite(self):
"""
@ -58,9 +70,6 @@ class Tests_UserSite:
"""
Test installing current directory ('.') into usersite after installing distribute
"""
# FIXME distutils --user option seems to be broken in pypy
if hasattr(sys, "pypy_version_info"):
raise SkipTest()
env = reset_env(use_distribute=True, system_site_packages=True)
result = run_pip('install', '--user', '-e',
'%s#egg=initools-dev' %
@ -72,9 +81,6 @@ class Tests_UserSite:
"""
Test installing current directory ('.') into usersite
"""
# FIXME distutils --user option seems to be broken in pypy
if hasattr(sys, "pypy_version_info"):
raise SkipTest()
env = reset_env(use_distribute=True, system_site_packages=True)
run_from = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=False)
@ -111,15 +117,21 @@ class Tests_UserSite:
assert not isfile(initools_v3_file), initools_v3_file
def test_install_user_conflict_in_site(self):
def test_install_user_conflict_in_globalsite(self):
"""
Test user install with conflict in site ignores site and installs to usersite
Test user install with conflict in global 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.
# the test framework only supports testing using virtualenvs
# the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site
# this test will use 2 modifications to simulate the user-site/global-site relationship
# 1) a monkey patch which will make it appear INITools==0.2 is not in in the virtualenv site
# if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..."
# 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence over the virtualenv site
env = reset_env(system_site_packages=True, sitecustomize=patch_dist_in_site_packages)
env.environ["PYTHONPATH"] = env.root_path / env.user_site
env = reset_env(system_site_packages=True)
result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', 'INITools==0.1')
@ -136,19 +148,50 @@ class Tests_UserSite:
assert isdir(initools_folder)
def test_upgrade_user_conflict_in_globalsite(self):
"""
Test user install/upgrade with conflict in global site ignores site and installs to usersite
"""
# the test framework only supports testing using virtualenvs
# the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site
# this test will use 2 modifications to simulate the user-site/global-site relationship
# 1) a monkey patch which will make it appear INITools==0.2 is not in in the virtualenv site
# if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..."
# 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence over the virtualenv site
env = reset_env(system_site_packages=True, sitecustomize=patch_dist_in_site_packages)
env.environ["PYTHONPATH"] = env.root_path / env.user_site
result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', '--upgrade', 'INITools')
#usersite has 0.3.1
egg_info_folder = env.user_site / 'INITools-0.3.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), result2.stdout
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.
# the test framework only supports testing using virtualenvs.
# the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site.
# this test will use 2 modifications to simulate the user-site/global-site relationship
# 1) a monkey patch which will make it appear INITools==0.2 is not in in the virtualenv site
# if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..."
# 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence over the virtualenv site
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 = reset_env(system_site_packages=True, sitecustomize=patch_dist_in_site_packages)
env.environ["PYTHONPATH"] = env.root_path / env.user_site
result1 = run_pip('install', 'INITools==0.2')
@ -166,3 +209,48 @@ class Tests_UserSite:
initools_folder = env.root_path / env.site_packages / 'initools'
assert isdir(egg_info_folder)
assert isdir(initools_folder)
def test_install_user_in_global_virtualenv_with_conflict_fails(self):
"""
Test user install in --system-site-packages virtualenv with conflict in site fails.
"""
env = reset_env(system_site_packages=True)
result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', 'INITools==0.1', expect_error=True)
resultp = env.run('python', '-c', "import pkg_resources; print(pkg_resources.get_distribution('initools').location)")
dist_location = resultp.stdout.strip()
assert result2.stdout.startswith("Will not install to the user site because it will lack sys.path precedence to %s in %s"
%('INITools', dist_location)), result2.stdout
def test_uninstall_from_usersite(self):
"""
Test uninstall from usersite
"""
env = reset_env(system_site_packages=True)
result1 = run_pip('install', '--user', 'INITools==0.3')
result2 = run_pip('uninstall', '-y', 'INITools')
assert_all_changes(result1, result2, [env.venv/'build', 'cache'])
def test_uninstall_editable_from_usersite(self):
"""
Test uninstall editable local user install
"""
env = reset_env(use_distribute=True, system_site_packages=True)
#install
to_install = abspath(join(here, 'packages', 'FSPkg'))
result1 = run_pip('install', '--user', '-e', to_install, expect_error=False)
egg_link = env.user_site/'FSPkg.egg-link'
assert egg_link in result1.files_created, str(result1.stdout)
#uninstall
result2 = run_pip('uninstall', '-y', 'FSPkg')
assert not isfile(env.root_path / egg_link)
assert_all_changes(result1, result2,
[env.venv/'build', 'cache', env.user_site/'easy-install.pth'])

148
tests/test_util.py Normal file
View File

@ -0,0 +1,148 @@
"""
util tests
"""
import os
import pkg_resources
from mock import Mock
from nose.tools import eq_
from tests.path import Path
from pip.util import egg_link_path
from pip.util import Inf
class Tests_EgglinkPath:
"util.egg_link_path() tests"
def setup(self):
project = 'foo'
self.mock_dist = Mock(project_name=project)
self.site_packages = 'SITE_PACKAGES'
self.user_site = 'USER_SITE'
self.user_site_egglink = os.path.join(self.user_site,'%s.egg-link' % project)
self.site_packages_egglink = os.path.join(self.site_packages,'%s.egg-link' % project)
#patches
from pip import util
self.old_site_packages = util.site_packages
self.mock_site_packages = util.site_packages = 'SITE_PACKAGES'
self.old_running_under_virtualenv = util.running_under_virtualenv
self.mock_running_under_virtualenv = util.running_under_virtualenv = Mock()
self.old_virtualenv_no_global = util.virtualenv_no_global
self.mock_virtualenv_no_global = util.virtualenv_no_global = Mock()
self.old_user_site = util.user_site
self.mock_user_site = util.user_site = self.user_site
from os import path
self.old_isfile = path.isfile
self.mock_isfile = path.isfile = Mock()
def teardown(self):
from pip import util
util.site_packages = self.old_site_packages
util.running_under_virtualenv = self.old_running_under_virtualenv
util.virtualenv_no_global = self.old_virtualenv_no_global
util.user_site = self.old_user_site
from os import path
path.isfile = self.old_isfile
def eggLinkInUserSite(self,egglink):
return egglink==self.user_site_egglink
def eggLinkInSitePackages(self,egglink):
return egglink==self.site_packages_egglink
#########################
## egglink in usersite ##
#########################
def test_egglink_in_usersite_notvenv(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.side_effect = self.eggLinkInUserSite
eq_(egg_link_path(self.mock_dist), self.user_site_egglink)
def test_egglink_in_usersite_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInUserSite
eq_(egg_link_path(self.mock_dist), None)
def test_egglink_in_usersite_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInUserSite
eq_(egg_link_path(self.mock_dist), self.user_site_egglink)
#########################
## egglink in sitepkgs ##
#########################
def test_egglink_in_sitepkgs_notvenv(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.side_effect = self.eggLinkInSitePackages
eq_(egg_link_path(self.mock_dist), self.site_packages_egglink)
def test_egglink_in_sitepkgs_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInSitePackages
eq_(egg_link_path(self.mock_dist), self.site_packages_egglink)
def test_egglink_in_sitepkgs_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInSitePackages
eq_(egg_link_path(self.mock_dist), self.site_packages_egglink)
####################################
## egglink in usersite & sitepkgs ##
####################################
def test_egglink_in_both_notvenv(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.return_value = True
eq_(egg_link_path(self.mock_dist), self.user_site_egglink)
def test_egglink_in_both_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = True
eq_(egg_link_path(self.mock_dist), self.site_packages_egglink)
def test_egglink_in_both_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = True
eq_(egg_link_path(self.mock_dist), self.site_packages_egglink)
################
## no egglink ##
################
def test_noegglink_in_sitepkgs_notvenv(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.return_value = False
eq_(egg_link_path(self.mock_dist), None)
def test_noegglink_in_sitepkgs_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = False
eq_(egg_link_path(self.mock_dist), None)
def test_noegglink_in_sitepkgs_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = False
eq_(egg_link_path(self.mock_dist), None)
def test_Inf_greater():
"""Test Inf compares greater."""
assert Inf > object()
def test_Inf_equals_Inf():
"""Test Inf compares greater."""
assert Inf == Inf

View File

@ -1,4 +1,6 @@
import sys
from mock import patch
from nose import SkipTest
from pip.vcs.git import Git
from tests.test_pip import (reset_env, run_pip,
_create_test_package,)
@ -87,6 +89,9 @@ def test_check_submodule_addition():
Submodules are pulled in on install and updated on upgrade.
"""
# TODO(pnasrat) fix all helpers to do right things with paths on windows.
if sys.platform == 'win32':
raise SkipTest()
env = reset_env()
module_path, submodule_path = _create_test_package_with_submodule(env)

8
tox.ini Normal file
View File

@ -0,0 +1,8 @@
[tox]
envlist =
py25,py26,py27,py32,py33,pypy
[testenv]
commands =
python setup.py dev
python setup.py nosetests