basecommand.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. """Base Command class, and related routines"""
  2. import os
  3. import sys
  4. import tempfile
  5. import traceback
  6. import time
  7. import optparse
  8. from pip import cmdoptions
  9. from pip.locations import running_under_virtualenv
  10. from pip.log import logger
  11. from pip.download import PipSession
  12. from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
  13. CommandError, PreviousBuildDirError)
  14. from pip.backwardcompat import StringIO
  15. from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
  16. from pip.status_codes import (SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND,
  17. PREVIOUS_BUILD_DIR_ERROR)
  18. from pip.util import get_prog
  19. __all__ = ['Command']
  20. class Command(object):
  21. name = None
  22. usage = None
  23. hidden = False
  24. def __init__(self):
  25. parser_kw = {
  26. 'usage': self.usage,
  27. 'prog': '%s %s' % (get_prog(), self.name),
  28. 'formatter': UpdatingDefaultsHelpFormatter(),
  29. 'add_help_option': False,
  30. 'name': self.name,
  31. 'description': self.__doc__,
  32. }
  33. self.parser = ConfigOptionParser(**parser_kw)
  34. # Commands should add options to this option group
  35. optgroup_name = '%s Options' % self.name.capitalize()
  36. self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
  37. # Add the general options
  38. gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, self.parser)
  39. self.parser.add_option_group(gen_opts)
  40. def _build_session(self, options):
  41. session = PipSession()
  42. # Handle custom ca-bundles from the user
  43. if options.cert:
  44. session.verify = options.cert
  45. # Handle timeouts
  46. if options.timeout:
  47. session.timeout = options.timeout
  48. # Handle configured proxies
  49. if options.proxy:
  50. session.proxies = {
  51. "http": options.proxy,
  52. "https": options.proxy,
  53. }
  54. # Determine if we can prompt the user for authentication or not
  55. session.auth.prompting = not options.no_input
  56. return session
  57. def setup_logging(self):
  58. pass
  59. def parse_args(self, args):
  60. # factored out for testability
  61. return self.parser.parse_args(args)
  62. def main(self, args):
  63. options, args = self.parse_args(args)
  64. level = 1 # Notify
  65. level += options.verbose
  66. level -= options.quiet
  67. level = logger.level_for_integer(4 - level)
  68. complete_log = []
  69. logger.add_consumers(
  70. (level, sys.stdout),
  71. (logger.DEBUG, complete_log.append),
  72. )
  73. if options.log_explicit_levels:
  74. logger.explicit_levels = True
  75. self.setup_logging()
  76. #TODO: try to get these passing down from the command?
  77. # without resorting to os.environ to hold these.
  78. if options.no_input:
  79. os.environ['PIP_NO_INPUT'] = '1'
  80. if options.exists_action:
  81. os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action)
  82. if options.require_venv:
  83. # If a venv is required check if it can really be found
  84. if not running_under_virtualenv():
  85. logger.fatal('Could not find an activated virtualenv (required).')
  86. sys.exit(VIRTUALENV_NOT_FOUND)
  87. if options.log:
  88. log_fp = open_logfile(options.log, 'a')
  89. logger.add_consumers((logger.DEBUG, log_fp))
  90. else:
  91. log_fp = None
  92. exit = SUCCESS
  93. store_log = False
  94. try:
  95. status = self.run(options, args)
  96. # FIXME: all commands should return an exit status
  97. # and when it is done, isinstance is not needed anymore
  98. if isinstance(status, int):
  99. exit = status
  100. except PreviousBuildDirError:
  101. e = sys.exc_info()[1]
  102. logger.fatal(str(e))
  103. logger.info('Exception information:\n%s' % format_exc())
  104. store_log = True
  105. exit = PREVIOUS_BUILD_DIR_ERROR
  106. except (InstallationError, UninstallationError):
  107. e = sys.exc_info()[1]
  108. logger.fatal(str(e))
  109. logger.info('Exception information:\n%s' % format_exc())
  110. store_log = True
  111. exit = ERROR
  112. except BadCommand:
  113. e = sys.exc_info()[1]
  114. logger.fatal(str(e))
  115. logger.info('Exception information:\n%s' % format_exc())
  116. store_log = True
  117. exit = ERROR
  118. except CommandError:
  119. e = sys.exc_info()[1]
  120. logger.fatal('ERROR: %s' % e)
  121. logger.info('Exception information:\n%s' % format_exc())
  122. exit = ERROR
  123. except KeyboardInterrupt:
  124. logger.fatal('Operation cancelled by user')
  125. logger.info('Exception information:\n%s' % format_exc())
  126. store_log = True
  127. exit = ERROR
  128. except:
  129. logger.fatal('Exception:\n%s' % format_exc())
  130. store_log = True
  131. exit = UNKNOWN_ERROR
  132. if store_log:
  133. log_file_fn = options.log_file
  134. text = '\n'.join(complete_log)
  135. try:
  136. log_file_fp = open_logfile(log_file_fn, 'w')
  137. except IOError:
  138. temp = tempfile.NamedTemporaryFile(delete=False)
  139. log_file_fn = temp.name
  140. log_file_fp = open_logfile(log_file_fn, 'w')
  141. logger.fatal('Storing debug log for failure in %s' % log_file_fn)
  142. log_file_fp.write(text)
  143. log_file_fp.close()
  144. if log_fp is not None:
  145. log_fp.close()
  146. return exit
  147. def format_exc(exc_info=None):
  148. if exc_info is None:
  149. exc_info = sys.exc_info()
  150. out = StringIO()
  151. traceback.print_exception(*exc_info, **dict(file=out))
  152. return out.getvalue()
  153. def open_logfile(filename, mode='a'):
  154. """Open the named log file in append mode.
  155. If the file already exists, a separator will also be printed to
  156. the file to separate past activity from current activity.
  157. """
  158. filename = os.path.expanduser(filename)
  159. filename = os.path.abspath(filename)
  160. dirname = os.path.dirname(filename)
  161. if not os.path.exists(dirname):
  162. os.makedirs(dirname)
  163. exists = os.path.exists(filename)
  164. log_fp = open(filename, mode)
  165. if exists:
  166. log_fp.write('%s\n' % ('-' * 60))
  167. log_fp.write('%s run on %s\n' % (sys.argv[0], time.strftime('%c')))
  168. return log_fp