pip/pip/basecommand.py

202 lines
6.5 KiB
Python
Raw Normal View History

"""Base Command class, and related routines"""
import os
import socket
import sys
import tempfile
import traceback
2009-11-20 19:19:47 +01:00
import time
import optparse
from pip.log import logger
from pip.download import urlopen
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
CommandError, PreviousBuildDirError)
2013-04-18 07:50:22 +02:00
from pip.backwardcompat import StringIO
from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
from pip.status_codes import (SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND,
PREVIOUS_BUILD_DIR_ERROR)
from pip.util import get_prog
__all__ = ['Command']
# for backwards compatibiliy
get_proxy = urlopen.get_proxy
class Command(object):
name = None
usage = None
hidden = False
def __init__(self, main_parser):
parser_kw = {
'usage': self.usage,
'prog': '%s %s' % (get_prog(), self.name),
'formatter': UpdatingDefaultsHelpFormatter(),
'add_help_option': False,
'name': self.name,
'description': self.__doc__,
}
self.main_parser = main_parser
self.parser = ConfigOptionParser(**parser_kw)
# Commands should add options to this option group
optgroup_name = '%s Options' % self.name.capitalize()
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
# Re-add all options and option groups.
for group in main_parser.option_groups:
self._copy_option_group(self.parser, group)
# Copies all general options from the main parser.
self._copy_options(self.parser, main_parser.option_list)
2013-01-18 22:25:15 +01:00
def _copy_options(self, parser, options):
"""Populate an option parser or group with options."""
for option in options:
2012-12-11 11:39:57 +01:00
if not option.dest:
continue
parser.add_option(option)
def _copy_option_group(self, parser, group):
"""Copy option group (including options) to another parser."""
new_group = optparse.OptionGroup(parser, group.title)
self._copy_options(new_group, group.option_list)
parser.add_option_group(new_group)
def setup_logging(self):
pass
def main(self, args):
options, args = self.parser.parse_args(args)
level = 1 # Notify
level += options.verbose
level -= options.quiet
level = logger.level_for_integer(4 - level)
complete_log = []
logger.add_consumers(
(level, sys.stdout),
(logger.DEBUG, complete_log.append),
)
if options.log_explicit_levels:
logger.explicit_levels = True
self.setup_logging()
2013-02-19 04:38:26 +01:00
#TODO: try to get these passing down from the command?
# without resorting to os.environ to hold these.
2011-10-04 16:06:34 +02:00
if options.no_input:
os.environ['PIP_NO_INPUT'] = '1'
2011-10-04 16:10:46 +02:00
if options.exists_action:
os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action)
2013-02-18 07:17:59 +01:00
if options.cert:
os.environ['PIP_CERT'] = options.cert
2013-02-07 08:27:53 +01:00
if options.require_venv:
# If a venv is required check if it can really be found
if not os.environ.get('VIRTUAL_ENV'):
logger.fatal('Could not find an activated virtualenv (required).')
sys.exit(VIRTUALENV_NOT_FOUND)
if options.log:
log_fp = open_logfile(options.log, 'a')
logger.add_consumers((logger.DEBUG, log_fp))
else:
log_fp = None
socket.setdefaulttimeout(options.timeout or None)
urlopen.setup(proxystr=options.proxy, prompting=not options.no_input)
exit = SUCCESS
store_log = False
try:
status = self.run(options, args)
# FIXME: all commands should return an exit status
# and when it is done, isinstance is not needed anymore
if isinstance(status, int):
exit = status
except PreviousBuildDirError:
e = sys.exc_info()[1]
logger.fatal(str(e))
logger.info('Exception information:\n%s' % format_exc())
store_log = True
exit = PREVIOUS_BUILD_DIR_ERROR
2011-03-15 20:49:48 +01:00
except (InstallationError, UninstallationError):
e = sys.exc_info()[1]
logger.fatal(str(e))
logger.info('Exception information:\n%s' % format_exc())
store_log = True
exit = ERROR
2011-03-15 20:49:48 +01:00
except BadCommand:
e = sys.exc_info()[1]
logger.fatal(str(e))
logger.info('Exception information:\n%s' % format_exc())
store_log = True
exit = ERROR
except CommandError:
e = sys.exc_info()[1]
logger.fatal('ERROR: %s' % e)
logger.info('Exception information:\n%s' % format_exc())
exit = ERROR
except KeyboardInterrupt:
logger.fatal('Operation cancelled by user')
logger.info('Exception information:\n%s' % format_exc())
store_log = True
exit = ERROR
except:
logger.fatal('Exception:\n%s' % format_exc())
store_log = True
exit = UNKNOWN_ERROR
if log_fp is not None:
log_fp.close()
if store_log:
log_fn = options.log_file
text = '\n'.join(complete_log)
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
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()
def open_logfile(filename, mode='a'):
"""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)
filename = os.path.abspath(filename)
dirname = os.path.dirname(filename)
if not os.path.exists(dirname):
os.makedirs(dirname)
exists = os.path.exists(filename)
log_fp = open(filename, mode)
if exists:
log_fp.write('%s\n' % ('-' * 60))
2011-03-15 20:49:48 +01:00
log_fp.write('%s run on %s\n' % (sys.argv[0], time.strftime('%c')))
return log_fp