1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Merged with upstream

This commit is contained in:
Your Name 2012-06-19 19:06:27 -04:00
commit 18f2e2cbe5
40 changed files with 857 additions and 884 deletions

View file

@ -5,10 +5,13 @@ python:
- 2.7
- 3.1
- 3.2
before_install: sudo apt-get install subversion bzr mercurial
before_install:
- sudo apt-get install subversion bzr mercurial
- echo -e "[web]\ncacerts = /etc/ssl/certs/ca-certificates.crt" >> ~/.hgrc
install: pip install nose virtualenv scripttest mock
script: nosetests
notifications:
irc: "irc.freenode.org#pip"
branches:
only:
- develop

View file

@ -20,6 +20,7 @@ Jay Graves
John-Scott Atlakson
Jon Parise
Josh Bronson
Kamal Bin Mustafa
Kelsey Hightower
Kenneth Belitzky
Kumar McMillan
@ -27,7 +28,9 @@ Luke Macken
Masklinn
Marc Abramowitz
Marcus Smith
Markus Hametner
Matt Maker
Nick Stenning
Nowell Strite
Oliver Tonnhofer
Olivier Girardot
@ -49,3 +52,4 @@ Vinay Sajip
Vitaly Babiy
W Trevor King
Wil Tan
Hsiaoming Yang

View file

@ -47,7 +47,7 @@ copyright = '2008-2011, The pip developers'
# built documents.
#
# The short X.Y version.
release = "1.1.post1"
release = "1.1.post2"
version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation

View file

@ -13,6 +13,21 @@ Beta and final releases planned for the second half of 2012.
develop (unreleased)
-------------------
* Write failure log to temp file if default location is not writable. Thanks
andreigc.
* Pull in submodules for git editable checkouts. Fixes #289 and #421. Thanks
Hsiaoming Yang and Markus Hametner.
* Use a temporary directory as the default build location outside of a
virtualenv. Fixes issues #339 and #381. Thanks TC01.
* Added support for specifying extras with local editables. Thanks Nick
Stenning.
* Added ``--egg`` flag to request egg-style rather than flat installation. Refs
issue #3. Thanks Kamal Bin Mustafa.
* Fixed issue #510 - prevent e.g. ``gmpy2-2.0.tar.gz`` from matching a request
to ``pip install gmpy``; sdist filename must begin with full project name
followed by a dash. Thanks casevh for the report.

View file

@ -36,9 +36,6 @@ pip doesn't do everything that easy_install does. Specifically:
future it would be good if it could install binaries from Windows ``.exe``
or ``.msi`` -- binary install on other platforms is not a priority.)
* It doesn't understand Setuptools extras (like ``package[test]``). This should
be added eventually.
* It is incompatible with some packages that extensively customize distutils
or setuptools in their ``setup.py`` files.

View file

@ -100,6 +100,8 @@ installed. For example::
tells pip to install the 3.0 version of MyPackage.
A line beginning with ``#`` is treated as a comment and ignored.
You can also request `extras`_ in the requirements file::
MyPackage==3.0 [PDF]

View file

@ -2,17 +2,16 @@
import os
import optparse
import subprocess
import sys
import re
import difflib
from pip.backwardcompat import walk_packages, console_to_str
from pip.basecommand import command_dict, load_command, load_all_commands, command_names
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
def autocomplete():
@ -80,19 +79,10 @@ def autocomplete():
sys.exit(1)
def version_control():
# Import all the version control support modules:
from pip import vcs
for importer, modname, ispkg in \
walk_packages(path=vcs.__path__, prefix=vcs.__name__+'.'):
__import__(modname)
def main(initial_args=None):
if initial_args is None:
initial_args = sys.argv[1:]
autocomplete()
version_control()
options, args = parser.parse_args(initial_args)
if options.help and not args:
args = ['help']
@ -196,76 +186,6 @@ class FrozenRequirement(object):
req = '-e %s' % req
return '\n'.join(list(self.comments)+[str(req)])+'\n'
############################################################
## Requirement files
def call_subprocess(cmd, show_stdout=True,
filter_stdout=None, cwd=None,
raise_on_returncode=True,
command_level=logger.DEBUG, command_desc=None,
extra_environ=None):
if command_desc is None:
cmd_parts = []
for part in cmd:
if ' ' in part or '\n' in part or '"' in part or "'" in part:
part = '"%s"' % part.replace('"', '\\"')
cmd_parts.append(part)
command_desc = ' '.join(cmd_parts)
if show_stdout:
stdout = None
else:
stdout = subprocess.PIPE
logger.log(command_level, "Running command %s" % command_desc)
env = os.environ.copy()
if extra_environ:
env.update(extra_environ)
try:
proc = subprocess.Popen(
cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
cwd=cwd, env=env)
except Exception:
e = sys.exc_info()[1]
logger.fatal(
"Error %s while executing command %s" % (e, command_desc))
raise
all_output = []
if stdout is not None:
stdout = proc.stdout
while 1:
line = console_to_str(stdout.readline())
if not line:
break
line = line.rstrip()
all_output.append(line + '\n')
if filter_stdout:
level = filter_stdout(line)
if isinstance(level, tuple):
level, line = level
logger.log(level, line)
if not logger.stdout_level_matches(level):
logger.show_progress()
else:
logger.info(line)
else:
returned_stdout, returned_stderr = proc.communicate()
all_output = [returned_stdout or '']
proc.wait()
if proc.returncode:
if raise_on_returncode:
if all_output:
logger.notify('Complete output from command %s:' % command_desc)
logger.notify('\n'.join(all_output) + '\n----------------------------------------')
raise InstallationError(
"Command %s failed with error code %s in %s"
% (command_desc, proc.returncode, cwd))
else:
logger.warn(
"Command %s had error code %s in %s"
% (command_desc, proc.returncode, cwd))
if stdout is not None:
return ''.join(all_output)
if __name__ == '__main__':
exit = main()

View file

@ -1,592 +0,0 @@
"""Utilities to support packages."""
# NOTE: This module must remain compatible with Python 2.3, as it is shared
# by setuptools for distribution with Python 2.3 and up.
import os
import sys
import imp
import os.path
from types import ModuleType
__all__ = [
'get_importer', 'iter_importers', 'get_loader', 'find_loader',
'walk_packages', 'iter_modules',
'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
]
def read_code(stream):
# This helper is needed in order for the PEP 302 emulation to
# correctly handle compiled files
import marshal
magic = stream.read(4)
if magic != imp.get_magic():
return None
stream.read(4) # Skip timestamp
return marshal.load(stream)
def simplegeneric(func):
"""Make a trivial single-dispatch generic function"""
registry = {}
def wrapper(*args, **kw):
ob = args[0]
try:
cls = ob.__class__
except AttributeError:
cls = type(ob)
try:
mro = cls.__mro__
except AttributeError:
try:
class cls(cls, object):
pass
mro = cls.__mro__[1:]
except TypeError:
mro = object, # must be an ExtensionClass or some such :(
for t in mro:
if t in registry:
return registry[t](*args, **kw)
else:
return func(*args, **kw)
try:
wrapper.__name__ = func.__name__
except (TypeError, AttributeError):
pass # Python 2.3 doesn't allow functions to be renamed
def register(typ, func=None):
if func is None:
return lambda f: register(typ, f)
registry[typ] = func
return func
wrapper.__dict__ = func.__dict__
wrapper.__doc__ = func.__doc__
wrapper.register = register
return wrapper
def walk_packages(path=None, prefix='', onerror=None):
"""Yields (module_loader, name, ispkg) for all modules recursively
on path, or, if path is None, all accessible modules.
'path' should be either None or a list of paths to look for
modules in.
'prefix' is a string to output on the front of every module name
on output.
Note that this function must import all *packages* (NOT all
modules!) on the given path, in order to access the __path__
attribute to find submodules.
'onerror' is a function which gets called with one argument (the
name of the package which was being imported) if any exception
occurs while trying to import a package. If no onerror function is
supplied, ImportErrors are caught and ignored, while all other
exceptions are propagated, terminating the search.
Examples:
# list all modules python can access
walk_packages()
# list all submodules of ctypes
walk_packages(ctypes.__path__, ctypes.__name__+'.')
"""
def seen(p, m={}):
if p in m:
return True
m[p] = True
for importer, name, ispkg in iter_modules(path, prefix):
yield importer, name, ispkg
if ispkg:
try:
__import__(name)
except ImportError:
if onerror is not None:
onerror(name)
except Exception:
if onerror is not None:
onerror(name)
else:
raise
else:
path = getattr(sys.modules[name], '__path__', None) or []
# don't traverse path items we've seen before
path = [p for p in path if not seen(p)]
for item in walk_packages(path, name+'.', onerror):
yield item
def iter_modules(path=None, prefix=''):
"""Yields (module_loader, name, ispkg) for all submodules on path,
or, if path is None, all top-level modules on sys.path.
'path' should be either None or a list of paths to look for
modules in.
'prefix' is a string to output on the front of every module name
on output.
"""
if path is None:
importers = iter_importers()
else:
importers = map(get_importer, path)
yielded = {}
for i in importers:
for name, ispkg in iter_importer_modules(i, prefix):
if name not in yielded:
yielded[name] = 1
yield i, name, ispkg
#@simplegeneric
def iter_importer_modules(importer, prefix=''):
if not hasattr(importer, 'iter_modules'):
return []
return importer.iter_modules(prefix)
iter_importer_modules = simplegeneric(iter_importer_modules)
class ImpImporter:
"""PEP 302 Importer that wraps Python's "classic" import algorithm
ImpImporter(dirname) produces a PEP 302 importer that searches that
directory. ImpImporter(None) produces a PEP 302 importer that searches
the current sys.path, plus any modules that are frozen or built-in.
Note that ImpImporter does not currently support being used by placement
on sys.meta_path.
"""
def __init__(self, path=None):
self.path = path
def find_module(self, fullname, path=None):
# Note: we ignore 'path' argument since it is only used via meta_path
subname = fullname.split(".")[-1]
if subname != fullname and self.path is None:
return None
if self.path is None:
path = None
else:
path = [os.path.realpath(self.path)]
try:
file, filename, etc = imp.find_module(subname, path)
except ImportError:
return None
return ImpLoader(fullname, file, filename, etc)
def iter_modules(self, prefix=''):
if self.path is None or not os.path.isdir(self.path):
return
yielded = {}
import inspect
filenames = os.listdir(self.path)
filenames.sort() # handle packages before same-named modules
for fn in filenames:
modname = inspect.getmodulename(fn)
if modname=='__init__' or modname in yielded:
continue
path = os.path.join(self.path, fn)
ispkg = False
if not modname and os.path.isdir(path) and '.' not in fn:
modname = fn
for fn in os.listdir(path):
subname = inspect.getmodulename(fn)
if subname=='__init__':
ispkg = True
break
else:
continue # not a package
if modname and '.' not in modname:
yielded[modname] = 1
yield prefix + modname, ispkg
class ImpLoader:
"""PEP 302 Loader that wraps Python's "classic" import algorithm
"""
code = source = None
def __init__(self, fullname, file, filename, etc):
self.file = file
self.filename = filename
self.fullname = fullname
self.etc = etc
def load_module(self, fullname):
self._reopen()
try:
mod = imp.load_module(fullname, self.file, self.filename, self.etc)
finally:
if self.file:
self.file.close()
# Note: we don't set __loader__ because we want the module to look
# normal; i.e. this is just a wrapper for standard import machinery
return mod
def get_data(self, pathname):
return open(pathname, "rb").read()
def _reopen(self):
if self.file and self.file.closed:
mod_type = self.etc[2]
if mod_type==imp.PY_SOURCE:
self.file = open(self.filename, 'rU')
elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
self.file = open(self.filename, 'rb')
def _fix_name(self, fullname):
if fullname is None:
fullname = self.fullname
elif fullname != self.fullname:
raise ImportError("Loader for module %s cannot handle "
"module %s" % (self.fullname, fullname))
return fullname
def is_package(self, fullname):
fullname = self._fix_name(fullname)
return self.etc[2]==imp.PKG_DIRECTORY
def get_code(self, fullname=None):
fullname = self._fix_name(fullname)
if self.code is None:
mod_type = self.etc[2]
if mod_type==imp.PY_SOURCE:
source = self.get_source(fullname)
self.code = compile(source, self.filename, 'exec')
elif mod_type==imp.PY_COMPILED:
self._reopen()
try:
self.code = read_code(self.file)
finally:
self.file.close()
elif mod_type==imp.PKG_DIRECTORY:
self.code = self._get_delegate().get_code()
return self.code
def get_source(self, fullname=None):
fullname = self._fix_name(fullname)
if self.source is None:
mod_type = self.etc[2]
if mod_type==imp.PY_SOURCE:
self._reopen()
try:
self.source = self.file.read()
finally:
self.file.close()
elif mod_type==imp.PY_COMPILED:
if os.path.exists(self.filename[:-1]):
f = open(self.filename[:-1], 'rU')
self.source = f.read()
f.close()
elif mod_type==imp.PKG_DIRECTORY:
self.source = self._get_delegate().get_source()
return self.source
def _get_delegate(self):
return ImpImporter(self.filename).find_module('__init__')
def get_filename(self, fullname=None):
fullname = self._fix_name(fullname)
mod_type = self.etc[2]
if self.etc[2]==imp.PKG_DIRECTORY:
return self._get_delegate().get_filename()
elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
return self.filename
return None
try:
import zipimport
from zipimport import zipimporter
def iter_zipimport_modules(importer, prefix=''):
dirlist = list(zipimport._zip_directory_cache[importer.archive].keys())
dirlist.sort()
_prefix = importer.prefix
plen = len(_prefix)
yielded = {}
import inspect
for fn in dirlist:
if not fn.startswith(_prefix):
continue
fn = fn[plen:].split(os.sep)
if len(fn)==2 and fn[1].startswith('__init__.py'):
if fn[0] not in yielded:
yielded[fn[0]] = 1
yield fn[0], True
if len(fn)!=1:
continue
modname = inspect.getmodulename(fn[0])
if modname=='__init__':
continue
if modname and '.' not in modname and modname not in yielded:
yielded[modname] = 1
yield prefix + modname, False
iter_importer_modules.register(zipimporter, iter_zipimport_modules)
except ImportError:
pass
def get_importer(path_item):
"""Retrieve a PEP 302 importer for the given path item
The returned importer is cached in sys.path_importer_cache
if it was newly created by a path hook.
If there is no importer, a wrapper around the basic import
machinery is returned. This wrapper is never inserted into
the importer cache (None is inserted instead).
The cache (or part of it) can be cleared manually if a
rescan of sys.path_hooks is necessary.
"""
try:
importer = sys.path_importer_cache[path_item]
except KeyError:
for path_hook in sys.path_hooks:
try:
importer = path_hook(path_item)
break
except ImportError:
pass
else:
importer = None
sys.path_importer_cache.setdefault(path_item, importer)
if importer is None:
try:
importer = ImpImporter(path_item)
except ImportError:
importer = None
return importer
def iter_importers(fullname=""):
"""Yield PEP 302 importers for the given module name
If fullname contains a '.', the importers will be for the package
containing fullname, otherwise they will be importers for sys.meta_path,
sys.path, and Python's "classic" import machinery, in that order. If
the named module is in a package, that package is imported as a side
effect of invoking this function.
Non PEP 302 mechanisms (e.g. the Windows registry) used by the
standard import machinery to find files in alternative locations
are partially supported, but are searched AFTER sys.path. Normally,
these locations are searched BEFORE sys.path, preventing sys.path
entries from shadowing them.
For this to cause a visible difference in behaviour, there must
be a module or package name that is accessible via both sys.path
and one of the non PEP 302 file system mechanisms. In this case,
the emulation will find the former version, while the builtin
import mechanism will find the latter.
Items of the following types can be affected by this discrepancy:
imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY
"""
if fullname.startswith('.'):
raise ImportError("Relative module names not supported")
if '.' in fullname:
# Get the containing package's __path__
pkg = '.'.join(fullname.split('.')[:-1])
if pkg not in sys.modules:
__import__(pkg)
path = getattr(sys.modules[pkg], '__path__', None) or []
else:
for importer in sys.meta_path:
yield importer
path = sys.path
for item in path:
yield get_importer(item)
if '.' not in fullname:
yield ImpImporter()
def get_loader(module_or_name):
"""Get a PEP 302 "loader" object for module_or_name
If the module or package is accessible via the normal import
mechanism, a wrapper around the relevant part of that machinery
is returned. Returns None if the module cannot be found or imported.
If the named module is not already imported, its containing package
(if any) is imported, in order to establish the package __path__.
This function uses iter_importers(), and is thus subject to the same
limitations regarding platform-specific special import locations such
as the Windows registry.
"""
if module_or_name in sys.modules:
module_or_name = sys.modules[module_or_name]
if isinstance(module_or_name, ModuleType):
module = module_or_name
loader = getattr(module, '__loader__', None)
if loader is not None:
return loader
fullname = module.__name__
else:
fullname = module_or_name
return find_loader(fullname)
def find_loader(fullname):
"""Find a PEP 302 "loader" object for fullname
If fullname contains dots, path must be the containing package's __path__.
Returns None if the module cannot be found or imported. This function uses
iter_importers(), and is thus subject to the same limitations regarding
platform-specific special import locations such as the Windows registry.
"""
for importer in iter_importers(fullname):
loader = importer.find_module(fullname)
if loader is not None:
return loader
return None
def extend_path(path, name):
"""Extend a package's path.
Intended use is to place the following code in a package's __init__.py:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
This will add to the package's __path__ all subdirectories of
directories on sys.path named after the package. This is useful
if one wants to distribute different parts of a single logical
package as multiple directories.
It also looks for *.pkg files beginning where * matches the name
argument. This feature is similar to *.pth files (see site.py),
except that it doesn't special-case lines starting with 'import'.
A *.pkg file is trusted at face value: apart from checking for
duplicates, all entries found in a *.pkg file are added to the
path, regardless of whether they are exist the filesystem. (This
is a feature.)
If the input path is not a list (as is the case for frozen
packages) it is returned unchanged. The input path is not
modified; an extended copy is returned. Items are only appended
to the copy at the end.
It is assumed that sys.path is a sequence. Items of sys.path that
are not (unicode or 8-bit) strings referring to existing
directories are ignored. Unicode items of sys.path that cause
errors when used as filenames may cause this function to raise an
exception (in line with os.path.isdir() behavior).
"""
if not isinstance(path, list):
# This could happen e.g. when this is called from inside a
# frozen package. Return the path unchanged in that case.
return path
pname = os.path.join(*name.split('.')) # Reconstitute as relative path
# Just in case os.extsep != '.'
sname = os.extsep.join(name.split('.'))
sname_pkg = sname + os.extsep + "pkg"
init_py = "__init__" + os.extsep + "py"
path = path[:] # Start with a copy of the existing path
from pip.backwardcompat import string_types
for dir in sys.path:
if not isinstance(dir, string_types) or not os.path.isdir(dir):
continue
subdir = os.path.join(dir, pname)
# XXX This may still add duplicate entries to path on
# case-insensitive filesystems
initfile = os.path.join(subdir, init_py)
if subdir not in path and os.path.isfile(initfile):
path.append(subdir)
# XXX Is this the right thing for subpackages like zope.app?
# It looks for a file named "zope.app.pkg"
pkgfile = os.path.join(dir, sname_pkg)
if os.path.isfile(pkgfile):
try:
f = open(pkgfile)
except IOError:
msg = sys.exc_info()[1]
sys.stderr.write("Can't open %s: %s\n" %
(pkgfile, msg))
else:
for line in f:
line = line.rstrip('\n')
if not line or line.startswith('#'):
continue
path.append(line) # Don't check for existence!
f.close()
return path
def get_data(package, resource):
"""Get a resource from a package.
This is a wrapper round the PEP 302 loader get_data API. The package
argument should be the name of a package, in standard module format
(foo.bar). The resource argument should be in the form of a relative
filename, using '/' as the path separator. The parent directory name '..'
is not allowed, and nor is a rooted name (starting with a '/').
The function returns a binary string, which is the contents of the
specified resource.
For packages located in the filesystem, which have already been imported,
this is the rough equivalent of
d = os.path.dirname(sys.modules[package].__file__)
data = open(os.path.join(d, resource), 'rb').read()
If the package cannot be located or loaded, or it uses a PEP 302 loader
which does not support get_data(), then None is returned.
"""
loader = get_loader(package)
if loader is None or not hasattr(loader, 'get_data'):
return None
mod = sys.modules.get(package) or loader.load_module(package)
if mod is None or not hasattr(mod, '__file__'):
return None
# Modify the resource name to be compatible with the loader.get_data
# signature - an os.path format "filename" starting with the dirname of
# the package's __file__
parts = resource.split('/')
parts.insert(0, os.path.dirname(mod.__file__))
resource_name = os.path.join(*parts)
return loader.get_data(resource_name)

View file

@ -1,10 +1,9 @@
"""Stuff that differs in different Python versions"""
import sys
import os
import shutil
import site
__all__ = ['any', 'WindowsError', 'md5', 'copytree']
__all__ = ['WindowsError']
try:
WindowsError = WindowsError
@ -12,27 +11,6 @@ except NameError:
class NeverUsedException(Exception):
"""this exception should never be raised"""
WindowsError = NeverUsedException
try:
from hashlib import md5
except ImportError:
import md5 as md5_module
md5 = md5_module.new
try:
from pkgutil import walk_packages
except ImportError:
# let's fall back as long as we can
from pip._pkgutil import walk_packages
try:
any = any
except NameError:
def any(seq):
for item in seq:
if item:
return True
return False
console_encoding = sys.__stdout__.encoding
@ -104,25 +82,11 @@ else:
raw_input = raw_input
BytesIO = StringIO
try:
from email.parser import FeedParser
except ImportError:
# python lesser than 2.5
from email.FeedParser import FeedParser
from distutils.sysconfig import get_python_lib, get_python_version
def copytree(src, dst):
if sys.version_info < (2, 5):
before_last_dir = os.path.dirname(dst)
if not os.path.exists(before_last_dir):
os.makedirs(before_last_dir)
shutil.copytree(src, dst)
shutil.copymode(src, dst)
else:
shutil.copytree(src, dst)
#site.USER_SITE was created in py2.6
user_site = getattr(site,'USER_SITE',None)
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy

View file

@ -1,6 +1,7 @@
"""Base Command class, and related routines"""
import os
from pkgutil import walk_packages
import socket
import sys
import traceback
@ -12,7 +13,7 @@ from pip.baseparser import parser, ConfigOptionParser, UpdatingDefaultsHelpForma
from pip.download import urlopen
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
CommandError)
from pip.backwardcompat import StringIO, walk_packages
from pip.backwardcompat import StringIO
from pip.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND
@ -137,8 +138,13 @@ class Command(object):
if store_log:
log_fn = options.log_file
text = '\n'.join(complete_log)
logger.fatal('Storing complete log in %s' % log_fn)
log_fp = open_logfile(log_fn, 'w')
try:
log_fp = open_logfile(log_fn, 'w')
except IOError:
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)
log_fp.write(text)
log_fp.close()
return exit

View file

@ -9,7 +9,75 @@ from pip.backwardcompat import ConfigParser, string_types
from pip.locations import default_config_file, default_log_file
class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter):
class PipPrettyHelpFormatter(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 format_option_strings(self, option):
return self._format_option_strings(option, ' <%s>', ', ')
def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '):
"""
Return a comma-separated list of option strings and metavars.
:param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
: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.takes_value():
metavar = option.metavar or option.dest.lower()
opts.append(mvarfmt % metavar)
return ''.join(opts)
def format_heading(self, heading):
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
msg = 'Usage: %s' % usage
if self.parser.description:
msg += '\n'
return msg
def format_description(self, description):
# leave full control over description to us
if description:
return description
else:
return ''
def format_epilog(self, epilog):
# leave full control over epilog to us
if epilog:
return epilog
else:
return ''
class UpdatingDefaultsHelpFormatter(PipPrettyHelpFormatter):
"""Custom help formatter for use in ConfigOptionParser that updates
the defaults before expanding them, allowing them to show up correctly
in the help listing"""

View file

@ -5,7 +5,7 @@ import shutil
from pip.req import InstallRequirement, RequirementSet
from pip.req import parse_requirements
from pip.log import logger
from pip.locations import build_prefix, src_prefix
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
@ -165,6 +165,12 @@ class InstallCommand(Command):
action='store_true',
help='Install to user-site')
self.parser.add_option(
'--egg',
dest='as_egg',
action='store_true',
help="Install as self contained egg file, like easy_install does.")
def _build_package_finder(self, options, index_urls):
"""
Create a package finder appropriate to this install command.
@ -184,6 +190,8 @@ class InstallCommand(Command):
options.src_dir = os.path.abspath(options.src_dir)
install_options = options.install_options or []
if options.use_user_site:
if virtualenv_no_global():
raise InstallationError("Can not perform a '--user' install. User site-packages are not visible in this virtualenv.")
install_options.append('--user')
if options.target_dir:
options.ignore_installed = True
@ -206,9 +214,11 @@ class InstallCommand(Command):
download_dir=options.download_dir,
download_cache=options.download_cache,
upgrade=options.upgrade,
as_egg=options.as_egg,
ignore_installed=options.ignore_installed,
ignore_dependencies=options.ignore_dependencies,
force_reinstall=options.force_reinstall)
force_reinstall=options.force_reinstall,
use_user_site=options.use_user_site)
for name in args:
requirement_set.add_requirement(
InstallRequirement.from_line(name, None))

View file

@ -1,16 +1,17 @@
import cgi
import getpass
from hashlib import md5
import mimetypes
import os
import re
import shutil
import sys
import tempfile
from pip.backwardcompat import (md5, copytree, xmlrpclib, urllib, urllib2,
urlparse, string_types, HTTPError)
from pip.backwardcompat import (xmlrpclib, urllib, urllib2,
urlparse, string_types)
from pip.exceptions import InstallationError
from pip.util import (splitext, rmtree, format_size, display_path,
backup_dir, ask, ask_path_exists, unpack_file,
backup_dir, ask_path_exists, unpack_file,
create_download_cache_folder, cache_download)
from pip.vcs import vcs
from pip.log import logger
@ -301,7 +302,7 @@ def unpack_file_url(link, location):
# delete the location since shutil will create it again :(
if os.path.isdir(location):
rmtree(location)
copytree(source, location)
shutil.copytree(source, location)
else:
unpack_file(source, location, content_type, link)

View file

@ -1,11 +1,15 @@
"""Exceptions used throughout package"""
class InstallationError(Exception):
class PipError(Exception):
"""Base pip exception"""
class InstallationError(PipError):
"""General exception during installation"""
class UninstallationError(Exception):
class UninstallationError(PipError):
"""General exception during uninstallation"""
@ -13,15 +17,14 @@ class DistributionNotFound(InstallationError):
"""Raised when a distribution cannot be found to satisfy a requirement"""
class BestVersionAlreadyInstalled(Exception):
class BestVersionAlreadyInstalled(PipError):
"""Raised when the most up-to-date version of a package is already
installed.
"""
installed. """
class BadCommand(Exception):
class BadCommand(PipError):
"""Raised when virtualenv or a command is not found"""
class CommandError(Exception):
class CommandError(PipError):
"""Raised when there is an error in command-line arguments"""

View file

@ -1,8 +1,8 @@
"""Locations where we look for configs, install stuff, etc"""
import sys
import site
import os
import shutil
import tempfile
from pip.backwardcompat import get_python_lib
@ -14,14 +14,28 @@ 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')
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:
## FIXME: this isn't a very good default
build_prefix = os.path.join(os.getcwd(), 'build')
src_prefix = os.path.join(os.getcwd(), 'src')
except OSError:
# In case the current working directory has been renamed or deleted
@ -41,7 +55,6 @@ if sys.platform == 'win32':
# buildout uses 'bin' on Windows too?
if not os.path.exists(bin_py):
bin_py = os.path.join(sys.prefix, 'bin')
user_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming
default_storage_dir = os.path.join(user_dir, 'pip')
default_config_file = os.path.join(default_storage_dir, 'pip.ini')
default_log_file = os.path.join(default_storage_dir, 'pip.log')

View file

@ -1,10 +1,12 @@
import sys
from email.parser import FeedParser
import os
import shutil
import re
import zipfile
import pkg_resources
import re
import sys
import shutil
import tempfile
import zipfile
from pip.locations import bin_py, running_under_virtualenv
from pip.exceptions import (InstallationError, UninstallationError,
BestVersionAlreadyInstalled,
@ -13,14 +15,13 @@ from pip.vcs import vcs
from pip.log import logger
from pip.util import display_path, rmtree
from pip.util import ask, ask_path_exists, backup_dir
from pip.util import is_installable_dir, is_local, dist_is_local
from pip.util import is_installable_dir, is_local, dist_is_local, dist_in_usersite
from pip.util import renames, normalize_path, egg_link_path
from pip.util import make_path_relative
from pip import call_subprocess
from pip.backwardcompat import (any, copytree, urlparse, urllib,
from pip.util import call_subprocess
from pip.backwardcompat import (urlparse, urllib,
ConfigParser, string_types, HTTPError,
FeedParser, get_python_version,
b)
get_python_version, b)
from pip.index import Link
from pip.locations import build_prefix
from pip.download import (get_file_content, is_url, url_to_path,
@ -35,7 +36,7 @@ PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt'
class InstallRequirement(object):
def __init__(self, req, comes_from, source_dir=None, editable=False,
url=None, update=True):
url=None, as_egg=False, update=True):
self.extras = ()
if isinstance(req, string_types):
req = pkg_resources.Requirement.parse(req)
@ -45,6 +46,7 @@ class InstallRequirement(object):
self.source_dir = source_dir
self.editable = editable
self.url = url
self.as_egg = as_egg
self._egg_info_path = None
# This holds the pkg_resources.Distribution object if this requirement
# is already available:
@ -60,15 +62,22 @@ class InstallRequirement(object):
self.install_succeeded = None
# UninstallPathSet of uninstalled distribution (for possible rollback)
self.uninstalled = None
self.use_user_site = False
@classmethod
def from_editable(cls, editable_req, comes_from=None, default_vcs=None):
name, url = parse_editable(editable_req, default_vcs)
name, url, extras_override = parse_editable(editable_req, default_vcs)
if url.startswith('file:'):
source_dir = url_to_path(url)
else:
source_dir = None
return cls(name, comes_from, source_dir=source_dir, editable=True, url=url)
res = cls(name, comes_from, source_dir=source_dir, editable=True, url=url)
if extras_override is not None:
res.extras = extras_override
return res
@classmethod
def from_line(cls, name, comes_from=None):
@ -276,7 +285,10 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
for dir in vcs.dirnames:
if dir in dirs:
dirs.remove(dir)
for dir in dirs:
# Iterate over a copy of ``dirs``, since mutating
# a list while iterating over it can cause trouble.
# (See https://github.com/pypa/pip/pull/462.)
for dir in list(dirs):
# Don't search in anything that looks like a virtualenv environment
if (os.path.exists(os.path.join(root, dir, 'bin', 'python'))
or os.path.exists(os.path.join(root, dir, 'Scripts', 'Python.exe'))):
@ -553,6 +565,7 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
if self.editable:
self.install_editable(install_options, global_options)
return
temp_location = tempfile.mkdtemp('-record', 'pip-')
record_filename = os.path.join(temp_location, 'install-record.txt')
try:
@ -562,9 +575,11 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
"exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" % self.setup_py] +\
list(global_options) + [
'install',
'--single-version-externally-managed',
'--record', record_filename]
if not self.as_egg:
install_args += ['--single-version-externally-managed']
if running_under_virtualenv():
## FIXME: I'm not sure if this is a reasonable location; probably not
## but we can't put it in the default location, as that is a virtualenv symlink that isn't writable
@ -582,6 +597,11 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
logger.notify('Record file %s not found' % record_filename)
return
self.install_succeeded = True
if self.as_egg:
# there's no --always-unzip option we can pass to install command
# so we unable to save the installed-files.txt
return
f = open(record_filename)
for line in f:
line = line.strip()
@ -661,7 +681,12 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
except pkg_resources.DistributionNotFound:
return False
except pkg_resources.VersionConflict:
self.conflicts_with = pkg_resources.get_distribution(self.req.project_name)
existing_dist = pkg_resources.get_distribution(self.req.project_name)
if self.use_user_site:
if dist_in_usersite(existing_dist):
self.conflicts_with = existing_dist
else:
self.conflicts_with = existing_dist
return True
@property
@ -757,10 +782,7 @@ class Requirements(object):
return self._keys
def values(self):
values_list = []
for key in self._keys:
values_list.append(self._dict[key])
return values_list
return [self._dict[key] for key in self._keys]
def __contains__(self, item):
return item in self._keys
@ -781,8 +803,8 @@ class Requirements(object):
class RequirementSet(object):
def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
upgrade=False, ignore_installed=False,
ignore_dependencies=False, force_reinstall=False):
upgrade=False, ignore_installed=False, as_egg=False,
ignore_dependencies=False, force_reinstall=False, use_user_site=False):
self.build_dir = build_dir
self.src_dir = src_dir
self.download_dir = download_dir
@ -798,6 +820,8 @@ class RequirementSet(object):
self.successfully_downloaded = []
self.successfully_installed = []
self.reqs_to_cleanup = []
self.as_egg = as_egg
self.use_user_site = use_user_site
def __str__(self):
reqs = [req for req in self.requirements.values()
@ -807,6 +831,8 @@ class RequirementSet(object):
def add_requirement(self, install_req):
name = install_req.name
install_req.as_egg = self.as_egg
install_req.use_user_site = self.use_user_site
if not name:
self.unnamed_requirements.append(install_req)
else:
@ -1096,7 +1122,7 @@ class RequirementSet(object):
target_dir = req_to_install.editable and self.src_dir or self.build_dir
logger.info("Copying %s to %s" % (req_to_install.name, target_dir))
dest = os.path.join(target_dir, req_to_install.name)
copytree(req_to_install.source_dir, dest)
shutil.copytree(req_to_install.source_dir, dest)
call_subprocess(["python", "%s/setup.py" % dest, "clean"], cwd=dest,
command_desc='python setup.py clean')
@ -1307,12 +1333,30 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None):
def parse_editable(editable_req, default_vcs=None):
"""Parses svn+http://blahblah@rev#egg=Foobar into a requirement
(Foobar) and a URL"""
url = editable_req
if os.path.isdir(url) and os.path.exists(os.path.join(url, 'setup.py')):
extras = None
# If a file path is specified with extras, strip off the extras.
m = re.match(r'^(.+)(\[[^\]]+\])$', url)
if m:
url_no_extras = m.group(1)
extras = m.group(2)
else:
url_no_extras = url
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)
# Treating it as code that has already been checked out
url = path_to_url(url)
if url.lower().startswith('file:'):
return None, url
url_no_extras = path_to_url(url_no_extras)
if url_no_extras.lower().startswith('file:'):
if extras:
return None, url_no_extras, pkg_resources.Requirement.parse('__placeholder__' + extras).extras
else:
return None, url_no_extras, None
for version_control in vcs:
if url.lower().startswith('%s:' % version_control):
url = '%s+%s' % (version_control, url)
@ -1324,8 +1368,10 @@ def parse_editable(editable_req, default_vcs=None):
'--editable=%s should be formatted with svn+URL, git+URL, hg+URL or bzr+URL' % editable_req)
vc_type = url.split('+', 1)[0].lower()
if not vcs.get_backend(vc_type):
raise InstallationError(
'For --editable=%s only svn (svn+URL), Git (git+URL), Mercurial (hg+URL) and Bazaar (bzr+URL) is currently supported' % editable_req)
error_message = 'For --editable=%s only ' % editable_req + \
', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
' is currently supported'
raise InstallationError(error_message)
match = re.search(r'(?:#|#.*?&)egg=([^&]*)', editable_req)
if (not match or not match.group(1)) and vcs.get_backend(vc_type):
parts = [p for p in editable_req.split('#', 1)[0].split('/') if p]
@ -1344,7 +1390,7 @@ def parse_editable(editable_req, default_vcs=None):
if match:
# Strip off -dev, -0.2, etc.
req = match.group(1)
return req, url
return req, url, None
class UninstallPathSet(object):

View file

@ -7,8 +7,9 @@ import posixpath
import pkg_resources
import zipfile
import tarfile
import subprocess
from pip.exceptions import InstallationError, BadCommand
from pip.backwardcompat import WindowsError, string_types, raw_input
from pip.backwardcompat import WindowsError, string_types, raw_input, console_to_str, user_site
from pip.locations import site_packages, running_under_virtualenv
from pip.log import logger
@ -21,7 +22,7 @@ __all__ = ['rmtree', 'display_path', 'backup_dir',
'make_path_relative', 'normalize_path',
'renames', 'get_terminal_size',
'unzip_file', 'untar_file', 'create_download_cache_folder',
'cache_download', 'unpack_file']
'cache_download', 'unpack_file', 'call_subprocess']
def rmtree(dir, ignore_errors=False):
@ -149,9 +150,9 @@ def format_size(bytes):
if bytes > 1000*1000:
return '%.1fMB' % (bytes/1000.0/1000)
elif bytes > 10*1000:
return '%iKB' % (bytes/1000)
return '%ikB' % (bytes/1000)
elif bytes > 1000:
return '%.1fKB' % (bytes/1000.0)
return '%.1fkB' % (bytes/1000.0)
else:
return '%ibytes' % bytes
@ -293,6 +294,16 @@ def dist_is_local(dist):
return is_local(dist_location(dist))
def dist_in_usersite(dist):
"""
Return True if given Distribution is installed in user site.
"""
if user_site:
return normalize_path(dist_location(dist)).startswith(normalize_path(user_site))
else:
return False
def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')):
"""
Return a list of installed Distribution objects.
@ -505,4 +516,68 @@ def unpack_file(filename, location, content_type, link):
raise InstallationError('Cannot determine archive format of %s' % location)
def call_subprocess(cmd, show_stdout=True,
filter_stdout=None, cwd=None,
raise_on_returncode=True,
command_level=logger.DEBUG, command_desc=None,
extra_environ=None):
if command_desc is None:
cmd_parts = []
for part in cmd:
if ' ' in part or '\n' in part or '"' in part or "'" in part:
part = '"%s"' % part.replace('"', '\\"')
cmd_parts.append(part)
command_desc = ' '.join(cmd_parts)
if show_stdout:
stdout = None
else:
stdout = subprocess.PIPE
logger.log(command_level, "Running command %s" % command_desc)
env = os.environ.copy()
if extra_environ:
env.update(extra_environ)
try:
proc = subprocess.Popen(
cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
cwd=cwd, env=env)
except Exception:
e = sys.exc_info()[1]
logger.fatal(
"Error %s while executing command %s" % (e, command_desc))
raise
all_output = []
if stdout is not None:
stdout = proc.stdout
while 1:
line = console_to_str(stdout.readline())
if not line:
break
line = line.rstrip()
all_output.append(line + '\n')
if filter_stdout:
level = filter_stdout(line)
if isinstance(level, tuple):
level, line = level
logger.log(level, line)
if not logger.stdout_level_matches(level):
logger.show_progress()
else:
logger.info(line)
else:
returned_stdout, returned_stderr = proc.communicate()
all_output = [returned_stdout or '']
proc.wait()
if proc.returncode:
if raise_on_returncode:
if all_output:
logger.notify('Complete output from command %s:' % command_desc)
logger.notify('\n'.join(all_output) + '\n----------------------------------------')
raise InstallationError(
"Command %s failed with error code %s in %s"
% (command_desc, proc.returncode, cwd))
else:
logger.warn(
"Command %s had error code %s in %s"
% (command_desc, proc.returncode, cwd))
if stdout is not None:
return ''.join(all_output)

View file

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

View file

@ -1,10 +1,9 @@
import os
import tempfile
import re
from pip import call_subprocess
from pip.backwardcompat import urlparse
from pip.log import logger
from pip.util import rmtree, display_path
from pip.util import rmtree, display_path, call_subprocess
from pip.vcs import vcs, VersionControl
from pip.download import path_to_url2

View file

@ -1,6 +1,7 @@
import tempfile
import re
from pip import call_subprocess
import os.path
from pip.util import call_subprocess
from pip.util import display_path, rmtree
from pip.vcs import vcs, VersionControl
from pip.log import logger
@ -28,8 +29,8 @@ class Git(VersionControl):
initial_slashes = path[:-len(path.lstrip('/'))]
newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/')
url = urlunsplit((scheme, netloc, newpath, query, fragment))
after_plus = scheme.find('+')+1
url = scheme[:after_plus]+ urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment))
after_plus = scheme.find('+') + 1
url = scheme[:after_plus] + urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment))
super(Git, self).__init__(url, *args, **kwargs)
@ -86,6 +87,8 @@ class Git(VersionControl):
call_subprocess(
[self.cmd, 'checkout', '-q'] + rev_options, cwd=dest)
self.update_submodules(dest)
def update(self, dest, rev_options):
# First fetch changes from the default remote
call_subprocess([self.cmd, 'fetch', '-q'], cwd=dest)
@ -93,6 +96,8 @@ class Git(VersionControl):
if rev_options:
rev_options = self.check_rev_options(rev_options[0], dest, rev_options)
call_subprocess([self.cmd, 'reset', '--hard', '-q'] + rev_options, cwd=dest)
#: update submodules
self.update_submodules(dest)
def obtain(self, dest):
url, rev = self.get_url_rev()
@ -105,6 +110,8 @@ class Git(VersionControl):
if self.check_destination(dest, url, rev_options, rev_display):
logger.notify('Cloning %s%s to %s' % (url, rev_display, display_path(dest)))
call_subprocess([self.cmd, 'clone', '-q', url, dest])
#: repo may contain submodules
self.update_submodules(dest)
if rev:
rev_options = self.check_rev_options(rev, dest, rev_options)
# Only do a checkout if rev_options differs from HEAD
@ -161,8 +168,10 @@ class Git(VersionControl):
elif (current_rev in branch_revs and
branch_revs[current_rev] != 'origin/master'):
# It's the head of a branch
full_egg_name = '%s-%s' % (egg_project_name,
branch_revs[current_rev].replace('origin/', ''))
full_egg_name = '%s-%s' % (
egg_project_name,
branch_revs[current_rev].replace('origin/', '')
)
else:
full_egg_name = '%s-dev' % egg_project_name
@ -202,5 +211,11 @@ class Git(VersionControl):
return call_subprocess([self.cmd, 'rev-parse', name],
show_stdout=False, cwd=location)
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'],
cwd=location)
vcs.register(Git)

View file

@ -2,7 +2,7 @@ import os
import tempfile
import re
import sys
from pip import call_subprocess
from pip.util import call_subprocess
from pip.util import display_path, rmtree
from pip.log import logger
from pip.vcs import vcs, VersionControl

View file

@ -1,9 +1,9 @@
import os
import re
from pip.backwardcompat import urlparse
from pip import call_subprocess, InstallationError
from pip import InstallationError
from pip.index import Link
from pip.util import rmtree, display_path
from pip.util import rmtree, display_path, call_subprocess
from pip.log import logger
from pip.vcs import vcs, VersionControl

View file

@ -3,7 +3,7 @@ import os
from setuptools import setup
# If you change this version, change it also in docs/conf.py
version = "1.1.post1"
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")

View file

@ -0,0 +1,62 @@
import textwrap
from tests.test_pip import (mkdir, write_file,)
def _create_test_package_submodule(env):
mkdir('version_pkg_submodule')
submodule_path = env.scratch_path/'version_pkg_submodule'
env.run('touch', 'testfile', cwd=submodule_path)
env.run('git', 'init', cwd=submodule_path)
env.run('git', 'add', '.', cwd=submodule_path)
env.run('git', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'initial version / submodule', cwd=submodule_path)
return submodule_path
def _change_test_package_submodule(env, submodule_path):
write_file(submodule_path/'testfile', 'this is a changed file')
write_file(submodule_path/'testfile2', 'this is an added file')
env.run('git', 'add', '.', cwd=submodule_path)
env.run('git', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'submodule change', cwd=submodule_path)
def _pull_in_submodule_changes_to_module(env, module_path):
env.run(cwd=module_path/'testpkg/static/', *('git pull -q origin master'.split(' ')))
env.run('git', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'submodule change', cwd=module_path)
def _create_test_package_with_submodule(env):
mkdir('version_pkg')
version_pkg_path = env.scratch_path/'version_pkg'
mkdir(version_pkg_path/'testpkg')
pkg_path = version_pkg_path/'testpkg'
write_file('__init__.py', '# hello there', pkg_path)
write_file('version_pkg.py', textwrap.dedent('''\
def main():
print('0.1')
'''), pkg_path)
write_file('setup.py', textwrap.dedent('''\
from setuptools import setup, find_packages
setup(name='version_pkg',
version='0.1',
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', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'initial version', cwd=version_pkg_path)
submodule_path = _create_test_package_submodule(env)
env.run('git', 'submodule', 'add', submodule_path, 'testpkg/static', cwd=version_pkg_path)
env.run('git', 'commit', '-q',
'--author', 'Pip <python-virtualenv@googlegroups.com>',
'-am', 'initial version w submodule', cwd=version_pkg_path)
return version_pkg_path, submodule_path

1
tests/packages/LocalExtras/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/LocalExtras.egg-info

View file

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

View file

@ -41,7 +41,7 @@ def test_correct_pip_version():
# primary resources other than .py files, this code will need
# maintenance
mismatch_py = [x for x in diffs.left_only + diffs.right_only + diffs.diff_files if x.endswith('.py')]
assert not mismatch_py, 'mismatched source files in %r and %r'% (pip_folder, pip_folder_outputed)
assert not mismatch_py, 'mismatched source files in %r and %r: %r'% (pip_folder, pip_folder_outputed, mismatch_py)
def test_pip_second_command_line_interface_works():
@ -302,6 +302,31 @@ def test_install_from_local_directory_with_no_setup_py():
assert "is not installable. File 'setup.py' not found." in result.stdout
def test_editable_install_from_local_directory_with_no_setup_py():
"""
Test installing from a local directory with no 'setup.py'.
"""
reset_env()
result = run_pip('install', '-e', here, expect_error=True)
assert len(result.files_created) == 1, result.files_created
assert 'pip-log.txt' in result.files_created, result.files_created
assert "is not installable. File 'setup.py' not found." in result.stdout
def test_install_as_egg():
"""
Test installing as egg, instead of flat install.
"""
env = reset_env()
to_install = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', to_install, '--egg', expect_error=False)
fspkg_folder = env.site_packages/'fspkg'
egg_folder = env.site_packages/'FSPkg-0.1dev-py%s.egg' % pyversion
assert fspkg_folder not in result.files_created, str(result.stdout)
assert egg_folder in result.files_created, str(result)
assert join(egg_folder, 'fspkg') in result.files_created, str(result)
def test_install_curdir():
"""
Test installing current directory ('.').
@ -319,77 +344,6 @@ def test_install_curdir():
assert egg_info_folder in result.files_created, str(result)
def test_install_curdir_usersite_fails_in_old_python():
"""
Test --user option on older Python versions (pre 2.6) fails intelligibly
"""
if sys.version_info >= (2, 6):
raise SkipTest()
reset_env()
run_from = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=True)
assert '--user is only supported in Python version 2.6 and newer' in result.stdout
def test_install_curdir_usersite():
"""
Test installing current directory ('.') into usersite
"""
if sys.version_info < (2, 6):
raise SkipTest()
# FIXME distutils --user option seems to be broken in pypy
if hasattr(sys, "pypy_version_info"):
raise SkipTest()
env = reset_env(use_distribute=True)
run_from = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=False)
fspkg_folder = env.user_site/'fspkg'
egg_info_folder = env.user_site/'FSPkg-0.1dev-py%s.egg-info' % pyversion
assert fspkg_folder in result.files_created, str(result.stdout)
assert egg_info_folder in result.files_created, str(result)
def test_install_subversion_usersite_editable_with_distribute():
"""
Test installing current directory ('.') into usersite after installing distribute
"""
if sys.version_info < (2, 6):
raise SkipTest()
# FIXME distutils --user option seems to be broken in pypy
if hasattr(sys, "pypy_version_info"):
raise SkipTest()
env = reset_env(use_distribute=True)
(env.lib_path/'no-global-site-packages.txt').rm() # this one reenables user_site
result = run_pip('install', '--user', '-e',
'%s#egg=initools-dev' %
local_checkout('svn+http://svn.colorstudy.com/INITools/trunk'))
result.assert_installed('INITools', use_user_site=True)
def test_install_subversion_usersite_editable_with_setuptools_fails():
"""
Test installing current directory ('.') into usersite using setuptools fails
"""
# --user only works on 2.6 or higher
if sys.version_info < (2, 6):
raise SkipTest()
# We don't try to use setuptools for 3.X.
elif sys.version_info >= (3,):
raise SkipTest()
env = reset_env(use_distribute=False)
no_site_packages = env.lib_path/'no-global-site-packages.txt'
if os.path.isfile(no_site_packages):
no_site_packages.rm() # this re-enables user_site
result = run_pip('install', '--user', '-e',
'%s#egg=initools-dev' %
local_checkout('svn+http://svn.colorstudy.com/INITools/trunk'),
expect_error=True)
assert '--user --editable not supported with setuptools, use distribute' in result.stdout
def test_install_pardir():
"""
Test installing parent directory ('..').
@ -601,3 +555,4 @@ def test_find_command_trys_supplied_pathext(mock_isfile, getpath_mock):
assert_raises(BadCommand, find_command, 'foo', 'path_one', pathext)
assert mock_isfile.call_args_list == expected, "Actual: %s\nExpected %s" % (mock_isfile.call_args_list, expected)
assert not getpath_mock.called, "Should not call get_pathext"

View file

@ -1,5 +1,3 @@
from pip.backwardcompat import any
import textwrap
from tests.test_pip import reset_env, run_pip, write_file
from tests.path import Path

View file

@ -1,5 +1,6 @@
from pip.req import InstallRequirement
def test_url_with_query():
"""InstallRequirement should strip the fragment, but not the query."""
url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz'

View file

@ -102,13 +102,30 @@ def install_setuptools(env):
env = None
def reset_env(environ=None, use_distribute=None):
def reset_env(environ=None, use_distribute=None, system_site_packages=False, sitecustomize=None):
"""Return a test environment.
Keyword arguments:
environ: an environ object to use.
use_distribute: use distribute, not setuptools.
system_site_packages: create a virtualenv that simulates --system-site-packages.
sitecustomize: a string containing python code to add to sitecustomize.py.
"""
global env
# FastTestPipEnv reuses env, not safe if use_distribute specified
if use_distribute is None:
env = FastTestPipEnvironment(environ)
if use_distribute is None and not system_site_packages:
env = FastTestPipEnvironment(environ, sitecustomize=sitecustomize)
else:
env = TestPipEnvironment(environ, use_distribute=use_distribute)
env = TestPipEnvironment(environ, use_distribute=use_distribute, sitecustomize=sitecustomize)
if system_site_packages:
#testing often occurs starting from a private virtualenv (e.g. with tox)
#from that context, you can't successfully use virtualenv.create_environment
#to create a 'system-site-packages' virtualenv
#hence, this workaround
(env.lib_path/'no-global-site-packages.txt').rm()
return env
@ -262,7 +279,7 @@ class TestPipEnvironment(TestFileEnvironment):
verbose = False
def __init__(self, environ=None, use_distribute=None):
def __init__(self, environ=None, use_distribute=None, sitecustomize=None):
self.root_path = Path(tempfile.mkdtemp('-piptest'))
@ -338,7 +355,12 @@ class TestPipEnvironment(TestFileEnvironment):
# Install this version instead
self.run('python', 'setup.py', 'install', cwd=src_folder, expect_stderr=True)
#create sitecustomize.py and add patches
self._create_empty_sitecustomize()
self._use_cached_pypi_server()
if sitecustomize:
self._add_to_sitecustomize(sitecustomize)
def _ignore_file(self, fn):
if fn.endswith('__pycache__') or fn.endswith(".pyc"):
@ -361,21 +383,41 @@ class TestPipEnvironment(TestFileEnvironment):
rmtree(str(self.root_path), ignore_errors=True)
def _use_cached_pypi_server(self):
site_packages = self.root_path / self.site_packages
pth = open(os.path.join(site_packages, 'pypi_intercept.pth'), 'w')
pth.write('import sys; ')
pth.write('sys.path.insert(0, %r); ' % str(here))
pth.write('import pypi_server; pypi_server.PyPIProxy.setup(); ')
pth.write('sys.path.remove(%r); ' % str(here))
pth.close()
# previously, this was handled in a pth file, and not in sitecustomize.py
# pth processing happens during the construction of sys.path.
# 'import pypi_server' ultimately imports pkg_resources (which intializes pkg_resources.working_set based on the current state of sys.path)
# pkg_resources.get_distribution (used in pip.req) requires an accurate pkg_resources.working_set
# therefore, 'import pypi_server' shouldn't occur in a pth file.
patch = """
import sys
sys.path.insert(0, %r)
import pypi_server
pypi_server.PyPIProxy.setup()
sys.path.remove(%r)""" % (str(here), str(here))
self._add_to_sitecustomize(patch)
def _create_empty_sitecustomize(self):
"Create empty sitecustomize.py."
sitecustomize_path = self.lib_path / 'sitecustomize.py'
sitecustomize = open(sitecustomize_path, 'w')
sitecustomize.close()
def _add_to_sitecustomize(self, snippet):
"Adds a python code snippet to sitecustomize.py."
sitecustomize_path = self.lib_path / 'sitecustomize.py'
sitecustomize = open(sitecustomize_path, 'a')
sitecustomize.write(textwrap.dedent('''
%s
''' %snippet))
sitecustomize.close()
fast_test_env_root = here / 'tests_cache' / 'test_ws'
fast_test_env_backup = here / 'tests_cache' / 'test_ws_backup'
class FastTestPipEnvironment(TestPipEnvironment):
def __init__(self, environ=None):
def __init__(self, environ=None, sitecustomize=None):
import virtualenv
self.root_path = fast_test_env_root
@ -459,7 +501,13 @@ class FastTestPipEnvironment(TestPipEnvironment):
# Install this version instead
self.run('python', 'setup.py', 'install', cwd=src_folder, expect_stderr=True)
shutil.copytree(self.root_path, self.backup_path, True)
#create sitecustomize.py and add patches
self._create_empty_sitecustomize()
self._use_cached_pypi_server()
if sitecustomize:
self._add_to_sitecustomize(sitecustomize)
assert self.root_path.exists
def __del__(self):

View file

@ -1,8 +1,9 @@
import os.path
import textwrap
from nose.tools import assert_raises
from nose.tools import assert_equal, assert_raises
from mock import patch
from pip.backwardcompat import urllib
from pip.req import Requirements
from pip.req import Requirements, parse_editable
from tests.test_pip import reset_env, run_pip, write_file, pyversion, here, path_to_url
from tests.local_repos import local_checkout
from tests.path import Path
@ -27,6 +28,7 @@ def test_requirements_file():
fn = '%s-%s-py%s.egg-info' % (other_lib_name, other_lib_version, pyversion)
assert result.files_created[env.site_packages/fn].dir
def test_schema_check_in_requirements_file():
"""
Test installing from a requirements file with an invalid vcs schema..
@ -38,6 +40,7 @@ def test_schema_check_in_requirements_file():
"""))
assert_raises(AssertionError, run_pip, 'install', '-vvv', '-r', env.scratch_path / 'file-egg-req.txt')
def test_relative_requirements_file():
"""
Test installing from a requirements file with a relative path with an egg= definition..
@ -117,3 +120,59 @@ def test_requirements_data_structure_implements__contains__():
assert 'pip' in requirements
assert 'nose' not in requirements
@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):
exists_mock.return_value = isdir_mock.return_value = True
getcwd_mock.return_value = "/some/path"
assert_equal(
parse_editable('.', 'git'),
(None, 'file:///some/path', None)
)
assert_equal(
parse_editable('foo', 'git'),
(None, 'file://' + os.path.join("/some/path", 'foo'), None)
)
def test_parse_editable_default_vcs():
assert_equal(
parse_editable('https://foo#egg=foo', 'git'),
('foo', 'git+https://foo#egg=foo', None)
)
def test_parse_editable_explicit_vcs():
assert_equal(
parse_editable('svn+https://foo#egg=foo', 'git'),
('foo', 'svn+https://foo#egg=foo', None)
)
def test_parse_editable_vcs_extras():
assert_equal(
parse_editable('svn+https://foo#egg=foo[extras]', 'git'),
('foo[extras]', 'svn+https://foo#egg=foo[extras]', None)
)
@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):
exists_mock.return_value = isdir_mock.return_value = True
getcwd_mock.return_value = "/some/path"
assert_equal(
parse_editable('.[extras]', 'git'),
(None, 'file://' + "/some/path", ('extras',))
)
assert_equal(
parse_editable('foo[bar,baz]', 'git'),
(None, 'file://' + os.path.join("/some/path", 'foo'), ('bar', 'baz'))
)
def test_install_local_editable_with_extras():
env = reset_env()
to_install = os.path.abspath(os.path.join(here, 'packages', 'LocalExtras'))
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

View file

@ -41,6 +41,7 @@ def test_pypi_xml_transformation():
{'score': 50, 'versions': ['1.0'], 'name': 'bar', 'summary': 'bar summary'}]
assert_equal(transform_hits(pypi_hits), expected)
def test_invalid_pypi_transformation():
"""
Test transformation of pypi when ordering None
@ -52,6 +53,7 @@ def test_invalid_pypi_transformation():
{'score': 0, 'versions': ['1.0'], 'name': 'bar', 'summary': 'bar summary'}]
assert_equal(transform_hits(pypi_hits), expected)
def test_search():
"""
End to end test of search command.

68
tests/test_test.py Normal file
View file

@ -0,0 +1,68 @@
"""Test the test support."""
import sys
import os
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
patch_urlopen = """
def mock_urlopen():
pass
import pip
pip.backwardcompat.urllib2.urlopen = mock_urlopen
"""
def test_pypiproxy_patch_applied():
"""
Test the PyPIProxy.setup() patch was applied, and sys.path returned to normal
"""
env = reset_env()
result = env.run('python', '-c', "import pip; print(pip.backwardcompat.urllib2.urlopen.__module__)")
#if it were not patched, the result would be 'urllib2'
assert "pypi_server"== result.stdout.strip(), result.stdout
#confirm the temporary sys.path adjustment is gone
result = env.run('python', '-c', "import sys; print(sys.path)")
paths = eval(result.stdout.strip())
assert here not in paths, paths
def test_add_patch_to_sitecustomize():
"""
Test adding monkey patch snippet to sitecustomize.py (using TestPipEnvironment)
"""
env = reset_env(sitecustomize=patch_urlopen, use_distribute=True)
result = env.run('python', '-c', "import pip; print(pip.backwardcompat.urllib2.urlopen.__module__)")
assert "sitecustomize"== result.stdout.strip(), result.stdout
def test_add_patch_to_sitecustomize_fast():
"""
Test adding monkey patch snippet to sitecustomize.py (using FastTestPipEnvironment)
"""
env = reset_env(sitecustomize=patch_urlopen)
result = env.run('python', '-c', "import pip; print(pip.backwardcompat.urllib2.urlopen.__module__)")
assert "sitecustomize"== result.stdout.strip(), result.stdout
def test_sitecustomize_not_growing_in_fast_environment():
"""
Test that the sitecustomize is not growing with redundant patches in the cached fast environment
"""
patch = "fu = 'bar'"
env1 = reset_env(sitecustomize=patch)
sc1 = env1.lib_path / 'sitecustomize.py'
size1 = os.stat(sc1).st_size
env2 = reset_env(sitecustomize=patch)
sc2 = env2.lib_path / 'sitecustomize.py'
size2 = os.stat(sc2).st_size
assert size1==size2, "size before, %d != size after, %d" %(size1, size2)

View file

@ -1,8 +1,8 @@
import textwrap
import sys
from os.path import join
from os.path import join, abspath
from tempfile import mkdtemp
from tests.test_pip import reset_env, run_pip, assert_all_changes, write_file
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
from pip.util import rmtree
@ -14,9 +14,9 @@ def test_simple_uninstall():
"""
env = reset_env()
result = run_pip('install', 'INITools==0.2', expect_error=True)
result = run_pip('install', 'INITools==0.2')
assert join(env.site_packages, 'initools') in result.files_created, sorted(result.files_created.keys())
result2 = run_pip('uninstall', 'INITools', '-y', expect_error=True)
result2 = run_pip('uninstall', 'INITools', '-y')
assert_all_changes(result, result2, [env.venv/'build', 'cache'])
@ -137,3 +137,21 @@ def test_uninstall_from_reqs_file():
result2 = run_pip('uninstall', '-r', 'test-req.txt', '-y')
assert_all_changes(
result, result2, [env.venv/'build', env.venv/'src', env.scratch/'test-req.txt'])
def test_uninstall_as_egg():
"""
Test uninstall package installed as egg.
"""
env = reset_env()
to_install = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', to_install, '--egg', expect_error=False)
fspkg_folder = env.site_packages/'fspkg'
egg_folder = env.site_packages/'FSPkg-0.1dev-py%s.egg' % pyversion
assert fspkg_folder not in result.files_created, str(result.stdout)
assert egg_folder in result.files_created, str(result)
result2 = run_pip('uninstall', 'FSPkg', '-y', expect_error=True)
assert_all_changes(result, result2, [env.venv/'build', 'cache'])

View file

@ -1,5 +1,6 @@
import textwrap
from os.path import join
from nose.tools import nottest
from tests.test_pip import (here, reset_env, run_pip, assert_all_changes,
write_file, pyversion, _create_test_package,
_change_test_package_version)
@ -150,7 +151,8 @@ def test_uninstall_rollback():
assert env.run('python', '-c', "import broken; print(broken.VERSION)").stdout == '0.1\n'
assert_all_changes(result.files_after, result2, [env.venv/'build', 'pip-log.txt'])
# Issue #530 - temporarily disable flaky test
@nottest
def test_editable_git_upgrade():
"""
Test installing an editable git package from a repository, upgrading the repository,
@ -164,7 +166,7 @@ def test_editable_git_upgrade():
_change_test_package_version(env, version_pkg_path)
run_pip('install', '-e', '%s#egg=version_pkg' % ('git+file://' + version_pkg_path))
version2 = env.run('version_pkg')
assert 'some different version' in version2.stdout
assert 'some different version' in version2.stdout, "Output: %s" % (version2.stdout)
def test_should_not_install_always_from_cache():

168
tests/test_user_site.py Normal file
View file

@ -0,0 +1,168 @@
"""
tests specific to "--user" option
"""
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
def test_install_curdir_usersite_fails_in_old_python():
"""
Test --user option on older Python versions (pre 2.6) fails intelligibly
"""
if sys.version_info >= (2, 6):
raise SkipTest()
reset_env(system_site_packages=True)
run_from = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=True)
assert '--user is only supported in Python version 2.6 and newer' in result.stdout
class Tests_UserSite:
def setup(self):
# --user only works on 2.6 or higher
if sys.version_info < (2, 6):
raise SkipTest()
def test_reset_env_system_site_packages_usersite(self):
"""
reset_env(system_site_packages=True) produces env where a --user install can be found using pkg_resources
"""
env = reset_env(system_site_packages=True)
run_pip('install', '--user', 'INITools==0.2')
result = env.run('python', '-c', "import pkg_resources; print(pkg_resources.get_distribution('initools').project_name)")
project_name = result.stdout.strip()
assert 'INITools'== project_name, "'%s' should be 'INITools'" %project_name
def test_install_subversion_usersite_editable_with_setuptools_fails(self):
"""
Test installing current directory ('.') into usersite using setuptools fails
"""
# We don't try to use setuptools for 3.X.
if sys.version_info >= (3,):
raise SkipTest()
env = reset_env(use_distribute=False, system_site_packages=True)
result = run_pip('install', '--user', '-e',
'%s#egg=initools-dev' %
local_checkout('svn+http://svn.colorstudy.com/INITools/trunk'),
expect_error=True)
assert '--user --editable not supported with setuptools, use distribute' in result.stdout
def test_install_subversion_usersite_editable_with_distribute(self):
"""
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' %
local_checkout('svn+http://svn.colorstudy.com/INITools/trunk'))
result.assert_installed('INITools', use_user_site=True)
def test_install_curdir_usersite(self):
"""
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)
fspkg_folder = env.user_site/'fspkg'
egg_info_folder = env.user_site/'FSPkg-0.1dev-py%s.egg-info' % pyversion
assert fspkg_folder in result.files_created, str(result.stdout)
assert egg_info_folder in result.files_created, str(result)
def test_install_user_venv_nositepkgs_fails(self):
"""
user install in virtualenv (with no system packages) fails with message
"""
env = reset_env()
run_from = abspath(join(here, 'packages', 'FSPkg'))
result = run_pip('install', '--user', curdir, cwd=run_from, expect_error=True)
assert "Can not perform a '--user' install. User site-packages are not visible in this virtualenv." in result.stdout
def test_install_user_conflict_in_usersite(self):
"""
Test user install with conflict in usersite updates usersite.
"""
env = reset_env(system_site_packages=True)
result1 = run_pip('install', '--user', 'INITools==0.3')
result2 = run_pip('install', '--user', 'INITools==0.1')
#usersite has 0.1
egg_info_folder = env.user_site / 'INITools-0.1-py%s.egg-info' % pyversion
initools_v3_file = env.root_path / env.user_site / 'initools' / 'configparser.py' #file only in 0.3
assert egg_info_folder in result2.files_created, str(result2)
assert not isfile(initools_v3_file), initools_v3_file
def test_install_user_conflict_in_site(self):
"""
Test user install with conflict in site ignores site and installs to usersite
"""
#the test framework only supports testing using virtualenvs
#this test will use a --system_site_packages virtualenv to achieve the conflict scenario.
env = reset_env(system_site_packages=True)
result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', 'INITools==0.1')
#usersite has 0.1
egg_info_folder = env.user_site / 'INITools-0.1-py%s.egg-info' % pyversion
initools_folder = env.user_site / 'initools'
assert egg_info_folder in result2.files_created, str(result2)
assert initools_folder in result2.files_created, str(result2)
#site still has 0.2 (can't look in result1; have to check)
egg_info_folder = env.root_path / env.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
initools_folder = env.root_path / env.site_packages / 'initools'
assert isdir(egg_info_folder)
assert isdir(initools_folder)
def test_install_user_conflict_in_globalsite_and_usersite(self):
"""
Test user install with conflict in globalsite and usersite ignores global site and updates usersite.
"""
#the test framework only supports testing using virtualenvs
#this test will use a --system_site_packages virtualenv to achieve the conflict scenario.
env = reset_env(system_site_packages=True)
# the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv site, usersite, global site
# given this ordering you *can't* use it to simulate the scenario for this test.
# this test will add the usersite to PYTHONPATH to simulate the desired ordering
env.environ["PYTHONPATH"] = env.root_path / env.user_site
result1 = run_pip('install', 'INITools==0.2')
result2 = run_pip('install', '--user', 'INITools==0.3')
result3 = run_pip('install', '--user', 'INITools==0.1')
#usersite has 0.1
egg_info_folder = env.user_site / 'INITools-0.1-py%s.egg-info' % pyversion
initools_v3_file = env.root_path / env.user_site / 'initools' / 'configparser.py' #file only in 0.3
assert egg_info_folder in result3.files_created, str(result3)
assert not isfile(initools_v3_file), initools_v3_file
#site still has 0.2 (can't just look in result1; have to check)
egg_info_folder = env.root_path / env.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion
initools_folder = env.root_path / env.site_packages / 'initools'
assert isdir(egg_info_folder)
assert isdir(initools_folder)

View file

@ -67,7 +67,7 @@ def test_git_with_tag_name_and_update():
'%s@0.1.2#egg=pip-test-package' %
local_checkout('git+http://github.com/pypa/pip-test-package.git'),
expect_error=True)
assert '0.1.2\n' in result.stdout
assert '0.1.2' in result.stdout
def test_git_branch_should_not_be_changed():
@ -92,7 +92,7 @@ def test_git_with_non_editable_unpacking():
result = run_pip('install', '--global-option=--version', local_checkout(
'git+http://github.com/pypa/pip-test-package.git@0.1.2#egg=pip-test-package'
), expect_error=True)
assert '0.1.2\n' in result.stdout
assert '0.1.2' in result.stdout
def test_git_with_editable_where_egg_contains_dev_string():
@ -137,7 +137,6 @@ def test_git_works_with_editable_non_origin_repo():
version_pkg_path = _create_test_package(env)
run_pip('install', '-e', version_pkg_path.abspath)
# 'freeze'ing this should not fall over, but should result in stderr output warning
result = run_pip('freeze', expect_stderr=True)
assert "Error when trying to get requirement" in result.stderr

View file

@ -1,7 +1,12 @@
from mock import patch
from pip.vcs.git import Git
from tests.test_pip import (reset_env, run_pip,
_create_test_package)
_create_test_package,)
from tests.git_submodule_helpers import (
_change_test_package_submodule,
_pull_in_submodule_changes_to_module,
_create_test_package_with_submodule,
)
def test_get_tag_revs_should_return_tag_name_and_commit_pair():
@ -75,3 +80,24 @@ def test_check_rev_options_should_handle_ambiguous_commit(branches_revs_mock,
result = git.check_rev_options('0.1', '.', [])
assert result == ['123456'], result
def test_check_submodule_addition():
"""
Submodules are pulled in on install and updated on upgrade.
"""
env = reset_env()
module_path, submodule_path = _create_test_package_with_submodule(env)
install_result = run_pip('install', '-e', 'git+'+module_path+'#egg=version_pkg')
assert '.virtualenv/src/version-pkg/testpkg/static/testfile' in install_result.files_created
_change_test_package_submodule(env, submodule_path)
_pull_in_submodule_changes_to_module(env, module_path)
# expect error because git may write to stderr
update_result = run_pip('install', '-e', 'git+'+module_path+'#egg=version_pkg', '--upgrade', expect_error=True)
assert env.venv/'src/version-pkg/testpkg/static/testfile2' in update_result.files_created

View file

@ -2,6 +2,7 @@ from mock import patch
from pip.vcs.subversion import Subversion
from tests.test_pip import reset_env
@patch('pip.vcs.subversion.call_subprocess')
def test_obtain_should_recognize_auth_info_in_url(call_subprocess_mock):
env = reset_env()
@ -11,6 +12,7 @@ def test_obtain_should_recognize_auth_info_in_url(call_subprocess_mock):
svn.cmd, 'checkout', '-q', '--username', 'username', '--password', 'password',
'http://username:password@svn.example.com/', env.scratch_path/'test'])
@patch('pip.vcs.subversion.call_subprocess')
def test_export_should_recognize_auth_info_in_url(call_subprocess_mock):
env = reset_env()