提交 459f31bf 编写于 作者: J Jannis Leidel

Automated merge with http://bitbucket.org/dholth/pip/

......@@ -2,4 +2,3 @@ recursive-include docs *.txt
recursive-include docs *.html
recursive-exclude docs/_build *.txt
prune docs/_build/_sources
recursive-include scripts/completion *
......@@ -9,6 +9,7 @@ from pip.baseparser import parser
from pip.exceptions import InstallationError
from pip.basecommand import command_dict, load_command, load_all_commands
from pip.vcs import vcs, get_src_requirement, import_vcs_support
from pip.util import get_installed_distributions
def autocomplete():
"""Command and option completion for the main option parser (and options)
......@@ -29,17 +30,28 @@ def autocomplete():
subcommands = [cmd for cmd, cls in command_dict.items() if not cls.hidden]
options = []
# subcommand
if cword == 1:
# show options of main parser only when necessary
if current.startswith('-') or current.startswith('--'):
subcommands += [opt.get_opt_string()
for opt in parser.option_list
if opt.help != optparse.SUPPRESS_HELP]
print ' '.join(filter(lambda x: x.startswith(current), subcommands))
try:
subcommand_name = [w for w in cwords if w in subcommands][0]
except IndexError:
subcommand_name = None
# subcommand options
# special case: the 'help' subcommand has no options
elif cwords[0] in subcommands and cwords[0] != 'help':
subcommand = command_dict.get(cwords[0])
if subcommand_name:
# special case: 'help' subcommand has no options
if subcommand_name == 'help':
sys.exit(1)
# special case: list locally installed dists for uninstall command
if subcommand_name == 'uninstall' and not current.startswith('-'):
installed = []
lc = current.lower()
for dist in get_installed_distributions(local_only=True):
if dist.key.startswith(lc) and dist.key not in cwords[1:]:
installed.append(dist.key)
# if there are no dists installed, fall back to option completion
if installed:
for dist in installed:
print dist
sys.exit(1)
subcommand = command_dict.get(subcommand_name)
options += [(opt.get_opt_string(), opt.nargs)
for opt in subcommand.parser.option_list
if opt.help != optparse.SUPPRESS_HELP]
......@@ -54,6 +66,13 @@ def autocomplete():
if option[1]:
opt_label += '='
print opt_label
else:
# show options of main parser only when necessary
if current.startswith('-') or current.startswith('--'):
subcommands += [opt.get_opt_string()
for opt in parser.option_list
if opt.help != optparse.SUPPRESS_HELP]
print ' '.join(filter(lambda x: x.startswith(current), subcommands))
sys.exit(1)
def main(initial_args=None):
......
......@@ -46,14 +46,30 @@ class Command(object):
options.quiet += initial_options.quiet
options.verbose += initial_options.verbose
def setup_logging(self):
pass
def main(self, complete_args, args, initial_options):
options, args = self.parser.parse_args(args)
self.merge_options(initial_options, options)
level = 1 # Notify
level += options.verbose
level -= options.quiet
level = logger.level_for_integer(4-level)
complete_log = []
logger.consumers.extend(
[(level, sys.stdout),
(logger.DEBUG, complete_log.append)])
if options.log_explicit_levels:
logger.explicit_levels = True
self.setup_logging()
if options.require_venv and not options.venv:
# If a venv is required check if it can really be found
if not os.environ.get('VIRTUAL_ENV'):
print 'Could not find an activated virtualenv (required).'
logger.fatal('Could not find an activated virtualenv (required).')
sys.exit(3)
# Automatically install in currently activated venv if required
options.respect_venv = True
......@@ -71,27 +87,15 @@ class Command(object):
# Make sure command line venv and environmental are the same
if (os.path.realpath(os.path.expanduser(options.venv)) !=
os.path.realpath(os.environ.get('VIRTUAL_ENV'))):
print ("Given virtualenv (%s) doesn't match "
"currently activated virtualenv (%s)."
% (options.venv, os.environ.get('VIRTUAL_ENV')))
logger.fatal("Given virtualenv (%s) doesn't match "
"currently activated virtualenv (%s)."
% (options.venv, os.environ.get('VIRTUAL_ENV')))
sys.exit(3)
else:
options.venv = os.environ.get('VIRTUAL_ENV')
print 'Using already activated environment %s' % options.venv
level = 1 # Notify
level += options.verbose
level -= options.quiet
level = logger.level_for_integer(4-level)
complete_log = []
logger.consumers.extend(
[(level, sys.stdout),
(logger.DEBUG, complete_log.append)])
if options.log_explicit_levels:
logger.explicit_levels = True
logger.notify('Using already activated environment %s' % options.venv)
if options.venv:
if options.verbose > 0:
# The logger isn't setup yet
print 'Running in environment %s' % options.venv
logger.info('Running in environment %s' % options.venv)
site_packages=False
if options.site_packages:
site_packages=True
......@@ -99,6 +103,7 @@ class Command(object):
complete_args)
# restart_in_venv should actually never return, but for clarity...
return
## FIXME: not sure if this sure come before or after venv restart
if options.log:
log_fp = open_logfile_append(options.log)
......
......@@ -5,7 +5,7 @@ import pip
from pip.req import InstallRequirement
from pip.log import logger
from pip.basecommand import Command
from pip.util import dist_location, is_local
from pip.util import get_installed_distributions
class FreezeCommand(Command):
name = 'freeze'
......@@ -35,6 +35,9 @@ class FreezeCommand(Command):
default=False,
help='If in a virtualenv, do not report globally-installed packages')
def setup_logging(self):
logger.move_stdout_to_stderr()
def run(self, options, args):
requirement = options.requirement
find_links = options.find_links or []
......@@ -47,7 +50,6 @@ class FreezeCommand(Command):
if skip_regex:
skip_match = re.compile(skip_regex)
logger.move_stdout_to_stderr()
dependency_links = []
f = sys.stdout
......@@ -61,12 +63,7 @@ class FreezeCommand(Command):
for link in find_links:
f.write('-f %s\n' % link)
installations = {}
for dist in pkg_resources.working_set:
if local_only and not is_local(dist_location(dist)):
continue
if dist.key in ('setuptools', 'pip', 'python'):
## FIXME: also skip virtualenv?
continue
for dist in get_installed_distributions(local_only=local_only):
req = pip.FrozenRequirement.from_dist(dist, dependency_links, find_tags=find_tags)
installations[req.name] = req
if requirement:
......
......@@ -11,6 +11,7 @@ import urlparse
import urllib2
import urllib
import ConfigParser
from distutils.sysconfig import get_python_version
from email.FeedParser import FeedParser
from pip.locations import bin_py, site_packages
from pip.exceptions import InstallationError, UninstallationError
......@@ -19,11 +20,12 @@ from pip.log import logger
from pip.util import display_path, rmtree, format_size
from pip.util import splitext, ask, backup_dir
from pip.util import url_to_filename, filename_to_url
from pip.util import is_url, is_filename, is_local
from pip.util import is_url, is_filename, is_local, dist_is_local
from pip.util import renames, normalize_path, egg_link_path
from pip.util import make_path_relative, is_svn_page, file_contents
from pip.util import has_leading_dir, split_leading_dir
from pip.util import get_file_content
from pip.util import in_venv
from pip import call_subprocess
from pip.backwardcompat import any, md5
from pip.index import Link
......@@ -421,8 +423,6 @@ execfile(__file__)
easy_install_pth = os.path.join(os.path.dirname(develop_egg_link),
'easy-install.pth')
paths_to_remove.add_pth(easy_install_pth, dist.location)
# fix location (so we can uninstall links to sources outside venv)
paths_to_remove.location = normalize_path(develop_egg_link)
# find distutils scripts= scripts
if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'):
......@@ -512,17 +512,21 @@ execfile(__file__)
return
temp_location = tempfile.mkdtemp('-record', 'pip-')
record_filename = os.path.join(temp_location, 'install-record.txt')
## 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
header_dir = os.path.join(os.path.dirname(os.path.dirname(self.source_dir)), 'lib', 'include')
install_args = [sys.executable, '-c',
"import setuptools; __file__=%r; execfile(%r)" % (self.setup_py, self.setup_py),
'install', '--single-version-externally-managed', '--record', record_filename]
if in_venv():
## 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
install_args += ['--install-headers',
os.path.join(sys.prefix, 'include', 'site',
'python' + get_python_version())]
logger.notify('Running setup.py install for %s' % self.name)
logger.indent += 2
try:
call_subprocess(
[sys.executable, '-c',
"import setuptools; __file__=%r; execfile(%r)" % (self.setup_py, self.setup_py),
'install', '--single-version-externally-managed', '--record', record_filename,
'--install-headers', header_dir] + install_options,
call_subprocess(install_args + install_options,
cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False)
finally:
logger.indent -= 2
......@@ -1368,20 +1372,19 @@ class UninstallPathSet(object):
self._refuse = set()
self.pth = {}
self.dist = dist
self.location = normalize_path(dist.location)
self.save_dir = None
self._moved_paths = []
def _permitted(self, path):
"""
Return True if the given path is one we are permitted to remove,
False otherwise.
Return True if the given path is one we are permitted to
remove/modify, False otherwise.
"""
return is_local(path)
def _can_uninstall(self):
if not self._permitted(self.location):
if not dist_is_local(self.dist):
logger.notify("Not uninstalling %s at %s, outside environment %s"
% (self.dist.project_name, self.location, sys.prefix))
return False
......@@ -1412,7 +1415,7 @@ class UninstallPathSet(object):
/a/path/to/a/file.txt are both in the set, leave only the
shorter path."""
short_paths = set()
for path in sorted(paths, lambda x, y: cmp(len(x), len(y))):
for path in sorted(paths, key=len):
if not any([(path.startswith(shortpath) and
path[len(shortpath.rstrip(os.path.sep))] == os.path.sep)
for shortpath in short_paths]):
......
......@@ -6,6 +6,9 @@ import stat
import urllib
import urllib2
import re
import pkg_resources
from pip.backwardcompat import WindowsError
from pip.exceptions import InstallationError
from pip.locations import site_packages
......@@ -312,6 +315,13 @@ def renames(old, new):
except OSError:
pass
def in_venv():
"""
Return True if we're running inside a virtualenv, False otherwise.
"""
return hasattr(sys, 'real_prefix')
def is_local(path):
"""
Return True if path is within sys.prefix, if we're running in a virtualenv.
......@@ -319,10 +329,38 @@ def is_local(path):
If we're not in a virtualenv, all paths are considered "local."
"""
if not hasattr(sys, 'real_prefix'):
if not in_venv():
return True
return normalize_path(path).startswith(normalize_path(sys.prefix))
def dist_is_local(dist):
"""
Return True if given Distribution object is installed locally
(i.e. within current virtualenv).
Always True if we're not in a virtualenv.
"""
return is_local(dist_location(dist))
def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')):
"""
Return a list of installed Distribution objects.
If ``local_only`` is True (default), only return installations
local to the current virtualenv, if in a virtualenv.
``skip`` argument is an iterable of lower-case project names to
ignore; defaults to ('setuptools', 'pip', 'python'). [FIXME also
skip virtualenv?]
"""
if local_only:
local_test = dist_is_local
else:
local_test = lambda d: True
return [d for d in pkg_resources.working_set if local_test(d) and d.key not in skip]
def egg_link_path(dist):
"""
Return the path where we'd expect to find a .egg-link file for
......
from os import makedirs
from os.path import join
import textwrap
from test_pip import here, reset_env, run_pip, pyversion, lib_py, get_env, diff_states, write_file
def test_download_if_requested():
"""
It should download and not install if requested.
"""
reset_env()
result = run_pip('install', 'INITools==0.1', '-d', '.', expect_error=True)
assert 'INITools-0.1.tar.gz' in result.files_created
assert join(lib_py + 'site-packages', 'initools') not in result.files_created
def test_single_download_from_requirements_file():
"""
It should support download from PyPi from a requirements file"
"""
reset_env()
write_file('test-req.txt', textwrap.dedent("""
INITools==0.1
"""))
result = run_pip('install', '-r', 'test-req.txt', '-d', '.', expect_error=True)
assert 'INITools-0.1.tar.gz' in result.files_created
assert join(lib_py + 'site-packages', 'initools') not in result.files_created
......@@ -49,11 +49,7 @@ def reset_env(environ=None):
def run_pip(*args, **kw):
args = (sys.executable, '-c', 'import pip; pip.main()', '-E', env.base_path) + args
#print >> sys.__stdout__, 'running', ' '.join(args)
if getattr(options, 'show_error', False):
kw['expect_error'] = True
result = env.run(*args, **kw)
if getattr(options, 'show_error', False) and result.returncode:
print result
return result
def write_file(filename, text):
......@@ -104,38 +100,7 @@ def diff_states(start, end, ignore=None):
updated[k] = end[k]
return dict(deleted=deleted, created=created, updated=updated)
import optparse
parser = optparse.OptionParser(usage='%prog [OPTIONS] [TEST_FILE...]')
parser.add_option('--first', action='store_true',
help='Show only first failure')
parser.add_option('--diff', action='store_true',
help='Show diffs in doctest failures')
parser.add_option('--show-error', action='store_true',
help='Show the errors (use expect_error=True in run_pip)')
parser.add_option('-v', action='store_true',
help='Be verbose')
options = None
def main():
global options
options, args = parser.parse_args()
reset_env()
if not args:
args = ['test_basic.txt', 'test_requirements.txt', 'test_freeze.txt', 'test_proxy.txt', 'test_uninstall.txt', 'test_upgrade.txt', 'test_config.txt']
optionflags = doctest.ELLIPSIS
if options.first:
optionflags |= doctest.REPORT_ONLY_FIRST_FAILURE
if options.diff:
optionflags |= doctest.REPORT_UDIFF
for filename in args:
if not filename.endswith('.txt'):
filename += '.txt'
if not filename.startswith('test_'):
filename = 'test_' + filename
## FIXME: test for filename existance
failures, successes = doctest.testfile(filename, optionflags=optionflags)
if options.first and failures:
break
if __name__ == '__main__':
main()
sys.stderr.write("Run pip's tests using nosetests.\n")
sys.exit(1)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册