mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merged with upstream
This commit is contained in:
commit
18f2e2cbe5
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
|
|
592
pip/_pkgutil.py
592
pip/_pkgutil.py
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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')
|
||||
|
|
104
pip/req.py
104
pip/req.py
|
@ -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):
|
||||
|
|
85
pip/util.py
85
pip/util.py
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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")
|
||||
|
|
62
tests/git_submodule_helpers.py
Normal file
62
tests/git_submodule_helpers.py
Normal 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
1
tests/packages/LocalExtras/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/LocalExtras.egg-info
|
0
tests/packages/LocalExtras/localextras/__init__.py
Normal file
0
tests/packages/LocalExtras/localextras/__init__.py
Normal file
13
tests/packages/LocalExtras/setup.py
Normal file
13
tests/packages/LocalExtras/setup.py
Normal 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]
|
||||
)
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
68
tests/test_test.py
Normal 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)
|
||||
|
|
@ -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'])
|
||||
|
||||
|
|
|
@ -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
168
tests/test_user_site.py
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue