2009-11-20 09:47:28 +01:00
|
|
|
"""Base Command class, and related routines"""
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import socket
|
|
|
|
import urllib2
|
|
|
|
import urllib
|
|
|
|
from cStringIO import StringIO
|
|
|
|
import traceback
|
2009-11-20 19:19:47 +01:00
|
|
|
import time
|
2009-11-20 09:47:28 +01:00
|
|
|
from pip.log import logger
|
|
|
|
from pip.baseparser import parser, ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
2010-04-15 16:14:31 +02:00
|
|
|
from pip.exceptions import BadCommand, InstallationError, UninstallationError
|
2009-11-20 09:47:28 +01:00
|
|
|
from pip.venv import restart_in_venv
|
|
|
|
|
2009-11-20 19:19:47 +01:00
|
|
|
__all__ = ['command_dict', 'Command', 'load_all_commands',
|
|
|
|
'load_command', 'command_names']
|
2009-11-20 09:47:28 +01:00
|
|
|
|
|
|
|
command_dict = {}
|
|
|
|
|
|
|
|
class Command(object):
|
|
|
|
name = None
|
|
|
|
usage = None
|
|
|
|
hidden = False
|
|
|
|
def __init__(self):
|
|
|
|
assert self.name
|
|
|
|
self.parser = ConfigOptionParser(
|
|
|
|
usage=self.usage,
|
|
|
|
prog='%s %s' % (sys.argv[0], self.name),
|
|
|
|
version=parser.version,
|
|
|
|
formatter=UpdatingDefaultsHelpFormatter(),
|
|
|
|
name=self.name)
|
|
|
|
for option in parser.option_list:
|
|
|
|
if not option.dest or option.dest == 'help':
|
|
|
|
# -h, --version, etc
|
|
|
|
continue
|
|
|
|
self.parser.add_option(option)
|
|
|
|
command_dict[self.name] = self
|
|
|
|
|
|
|
|
def merge_options(self, initial_options, options):
|
|
|
|
# Make sure we have all global options carried over
|
|
|
|
for attr in ['log', 'venv', 'proxy', 'venv_base', 'require_venv',
|
|
|
|
'respect_venv', 'log_explicit_levels', 'log_file',
|
|
|
|
'timeout', 'default_vcs', 'skip_requirements_regex']:
|
|
|
|
setattr(options, attr, getattr(initial_options, attr) or getattr(options, attr))
|
|
|
|
options.quiet += initial_options.quiet
|
|
|
|
options.verbose += initial_options.verbose
|
|
|
|
|
2010-03-09 22:31:03 +01:00
|
|
|
def setup_logging(self):
|
|
|
|
pass
|
|
|
|
|
2009-11-20 09:47:28 +01:00
|
|
|
def main(self, complete_args, args, initial_options):
|
|
|
|
options, args = self.parser.parse_args(args)
|
|
|
|
self.merge_options(initial_options, options)
|
|
|
|
|
2010-03-09 22:31:03 +01:00
|
|
|
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()
|
|
|
|
|
2009-11-20 09:47:28 +01:00
|
|
|
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'):
|
2010-03-09 22:31:03 +01:00
|
|
|
logger.fatal('Could not find an activated virtualenv (required).')
|
2009-11-20 09:47:28 +01:00
|
|
|
sys.exit(3)
|
|
|
|
# Automatically install in currently activated venv if required
|
|
|
|
options.respect_venv = True
|
|
|
|
|
|
|
|
if args and args[-1] == '___VENV_RESTART___':
|
|
|
|
## FIXME: We don't do anything this this value yet:
|
|
|
|
venv_location = args[-2]
|
|
|
|
args = args[:-2]
|
|
|
|
options.venv = None
|
|
|
|
else:
|
|
|
|
# If given the option to respect the activated environment
|
|
|
|
# check if no venv is given as a command line parameter
|
|
|
|
if options.respect_venv and os.environ.get('VIRTUAL_ENV'):
|
|
|
|
if options.venv and os.path.exists(options.venv):
|
|
|
|
# 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'))):
|
2010-03-09 22:31:03 +01:00
|
|
|
logger.fatal("Given virtualenv (%s) doesn't match "
|
|
|
|
"currently activated virtualenv (%s)."
|
|
|
|
% (options.venv, os.environ.get('VIRTUAL_ENV')))
|
2009-11-20 09:47:28 +01:00
|
|
|
sys.exit(3)
|
|
|
|
else:
|
|
|
|
options.venv = os.environ.get('VIRTUAL_ENV')
|
2010-03-11 00:17:50 +01:00
|
|
|
logger.info('Using already activated environment %s' % options.venv)
|
2009-11-20 09:47:28 +01:00
|
|
|
if options.venv:
|
2010-03-09 22:31:03 +01:00
|
|
|
logger.info('Running in environment %s' % options.venv)
|
2009-11-20 09:47:28 +01:00
|
|
|
site_packages=False
|
|
|
|
if options.site_packages:
|
|
|
|
site_packages=True
|
|
|
|
restart_in_venv(options.venv, options.venv_base, site_packages,
|
|
|
|
complete_args)
|
|
|
|
# restart_in_venv should actually never return, but for clarity...
|
|
|
|
return
|
2010-03-09 22:31:03 +01:00
|
|
|
|
2009-11-20 09:47:28 +01:00
|
|
|
## FIXME: not sure if this sure come before or after venv restart
|
|
|
|
if options.log:
|
2010-03-11 00:41:42 +01:00
|
|
|
log_fp = open_logfile(options.log, 'a')
|
2009-11-20 09:47:28 +01:00
|
|
|
logger.consumers.append((logger.DEBUG, log_fp))
|
|
|
|
else:
|
|
|
|
log_fp = None
|
|
|
|
|
|
|
|
socket.setdefaulttimeout(options.timeout or None)
|
|
|
|
|
|
|
|
setup_proxy_handler(options.proxy)
|
|
|
|
|
|
|
|
exit = 0
|
|
|
|
try:
|
|
|
|
self.run(options, args)
|
|
|
|
except (InstallationError, UninstallationError), e:
|
|
|
|
logger.fatal(str(e))
|
|
|
|
logger.info('Exception information:\n%s' % format_exc())
|
|
|
|
exit = 1
|
2010-04-15 16:14:31 +02:00
|
|
|
except BadCommand, e:
|
|
|
|
logger.fatal(str(e))
|
|
|
|
logger.info('Exception information:\n%s' % format_exc())
|
|
|
|
exit = 1
|
2009-11-20 09:47:28 +01:00
|
|
|
except:
|
|
|
|
logger.fatal('Exception:\n%s' % format_exc())
|
|
|
|
exit = 2
|
|
|
|
|
|
|
|
if log_fp is not None:
|
|
|
|
log_fp.close()
|
|
|
|
if exit:
|
|
|
|
log_fn = options.log_file
|
|
|
|
text = '\n'.join(complete_log)
|
|
|
|
logger.fatal('Storing complete log in %s' % log_fn)
|
2010-03-11 00:41:42 +01:00
|
|
|
log_fp = open_logfile(log_fn, 'w')
|
2009-11-20 09:47:28 +01:00
|
|
|
log_fp.write(text)
|
|
|
|
log_fp.close()
|
|
|
|
return exit
|
|
|
|
|
|
|
|
## FIXME: should get moved somewhere else:
|
|
|
|
def setup_proxy_handler(proxystr=''):
|
|
|
|
"""Set the proxy handler given the option passed on the command
|
|
|
|
line. If an empty string is passed it looks at the HTTP_PROXY
|
|
|
|
environment variable. """
|
|
|
|
proxy = get_proxy(proxystr)
|
|
|
|
if proxy:
|
|
|
|
proxy_support = urllib2.ProxyHandler({"http": proxy, "ftp": proxy})
|
|
|
|
opener = urllib2.build_opener(proxy_support, urllib2.CacheFTPHandler)
|
|
|
|
urllib2.install_opener(opener)
|
|
|
|
|
|
|
|
def get_proxy(proxystr=''):
|
|
|
|
"""Get the proxy given the option passed on the command line. If an
|
|
|
|
empty string is passed it looks at the HTTP_PROXY environment
|
|
|
|
variable."""
|
|
|
|
if not proxystr:
|
|
|
|
proxystr = os.environ.get('HTTP_PROXY', '')
|
|
|
|
if proxystr:
|
|
|
|
if '@' in proxystr:
|
|
|
|
user_password, server_port = proxystr.split('@', 1)
|
|
|
|
if ':' in user_password:
|
|
|
|
user, password = user_password.split(':', 1)
|
|
|
|
else:
|
|
|
|
user = user_password
|
|
|
|
import getpass
|
|
|
|
prompt = 'Password for %s@%s: ' % (user, server_port)
|
|
|
|
password = urllib.quote(getpass.getpass(prompt))
|
|
|
|
return '%s:%s@%s' % (user, password, server_port)
|
|
|
|
else:
|
|
|
|
return proxystr
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def format_exc(exc_info=None):
|
|
|
|
if exc_info is None:
|
|
|
|
exc_info = sys.exc_info()
|
|
|
|
out = StringIO()
|
|
|
|
traceback.print_exception(*exc_info, **dict(file=out))
|
|
|
|
return out.getvalue()
|
|
|
|
|
2010-03-11 00:41:42 +01:00
|
|
|
def open_logfile(filename, mode='a'):
|
2009-11-20 09:47:28 +01:00
|
|
|
"""Open the named log file in append mode.
|
|
|
|
|
|
|
|
If the file already exists, a separator will also be printed to
|
|
|
|
the file to separate past activity from current activity.
|
|
|
|
"""
|
2010-03-10 23:41:41 +01:00
|
|
|
filename = os.path.expanduser(filename)
|
2010-04-19 11:28:33 +02:00
|
|
|
filename = os.path.abspath(filename)
|
2010-04-09 18:12:44 +02:00
|
|
|
dirname = os.path.dirname(filename)
|
|
|
|
if not os.path.exists(dirname):
|
|
|
|
os.makedirs(dirname)
|
2009-11-20 09:47:28 +01:00
|
|
|
exists = os.path.exists(filename)
|
2010-03-11 00:41:42 +01:00
|
|
|
log_fp = open(filename, mode)
|
2009-11-20 09:47:28 +01:00
|
|
|
if exists:
|
|
|
|
print >> log_fp, '-'*60
|
|
|
|
print >> log_fp, '%s run on %s' % (sys.argv[0], time.strftime('%c'))
|
|
|
|
return log_fp
|
2009-11-20 19:19:47 +01:00
|
|
|
|
|
|
|
def load_command(name):
|
|
|
|
full_name = 'pip.commands.%s' % name
|
|
|
|
if full_name in sys.modules:
|
|
|
|
return
|
2009-12-04 19:43:46 +01:00
|
|
|
try:
|
|
|
|
__import__(full_name)
|
|
|
|
except ImportError:
|
|
|
|
pass
|
2009-11-20 19:19:47 +01:00
|
|
|
|
|
|
|
def load_all_commands():
|
|
|
|
for name in command_names():
|
|
|
|
load_command(name)
|
|
|
|
|
|
|
|
def command_names():
|
|
|
|
dir = os.path.join(os.path.dirname(__file__), 'commands')
|
|
|
|
names = []
|
|
|
|
for name in os.listdir(dir):
|
|
|
|
if name.endswith('.py') and os.path.isfile(os.path.join(dir, name)):
|
|
|
|
names.append(os.path.splitext(name)[0])
|
|
|
|
return names
|