debug.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. # The following comment should be removed at some point in the future.
  2. # mypy: disallow-untyped-defs=False
  3. from __future__ import absolute_import
  4. import locale
  5. import logging
  6. import os
  7. import sys
  8. import pip._vendor
  9. from pip._vendor import pkg_resources
  10. from pip._vendor.certifi import where
  11. from pip import __file__ as pip_location
  12. from pip._internal.cli import cmdoptions
  13. from pip._internal.cli.base_command import Command
  14. from pip._internal.cli.cmdoptions import make_target_python
  15. from pip._internal.cli.status_codes import SUCCESS
  16. from pip._internal.utils.logging import indent_log
  17. from pip._internal.utils.misc import get_pip_version
  18. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  19. if MYPY_CHECK_RUNNING:
  20. from types import ModuleType
  21. from typing import Any, List, Optional, Dict
  22. from optparse import Values
  23. logger = logging.getLogger(__name__)
  24. def show_value(name, value):
  25. # type: (str, Optional[str]) -> None
  26. logger.info('{}: {}'.format(name, value))
  27. def show_sys_implementation():
  28. # type: () -> None
  29. logger.info('sys.implementation:')
  30. if hasattr(sys, 'implementation'):
  31. implementation = sys.implementation # type: ignore
  32. implementation_name = implementation.name
  33. else:
  34. implementation_name = ''
  35. with indent_log():
  36. show_value('name', implementation_name)
  37. def create_vendor_txt_map():
  38. # type: () -> Dict[str, str]
  39. vendor_txt_path = os.path.join(
  40. os.path.dirname(pip_location),
  41. '_vendor',
  42. 'vendor.txt'
  43. )
  44. with open(vendor_txt_path) as f:
  45. # Purge non version specifying lines.
  46. # Also, remove any space prefix or suffixes (including comments).
  47. lines = [line.strip().split(' ', 1)[0]
  48. for line in f.readlines() if '==' in line]
  49. # Transform into "module" -> version dict.
  50. return dict(line.split('==', 1) for line in lines) # type: ignore
  51. def get_module_from_module_name(module_name):
  52. # type: (str) -> ModuleType
  53. # Module name can be uppercase in vendor.txt for some reason...
  54. module_name = module_name.lower()
  55. # PATCH: setuptools is actually only pkg_resources.
  56. if module_name == 'setuptools':
  57. module_name = 'pkg_resources'
  58. __import__(
  59. 'pip._vendor.{}'.format(module_name),
  60. globals(),
  61. locals(),
  62. level=0
  63. )
  64. return getattr(pip._vendor, module_name)
  65. def get_vendor_version_from_module(module_name):
  66. # type: (str) -> str
  67. module = get_module_from_module_name(module_name)
  68. version = getattr(module, '__version__', None)
  69. if not version:
  70. # Try to find version in debundled module info
  71. pkg_set = pkg_resources.WorkingSet(
  72. [os.path.dirname(getattr(module, '__file__'))]
  73. )
  74. package = pkg_set.find(pkg_resources.Requirement.parse(module_name))
  75. version = getattr(package, 'version', None)
  76. return version
  77. def show_actual_vendor_versions(vendor_txt_versions):
  78. # type: (Dict[str, str]) -> None
  79. # Logs the actual version and print extra info
  80. # if there is a conflict or if the actual version could not be imported.
  81. for module_name, expected_version in vendor_txt_versions.items():
  82. extra_message = ''
  83. actual_version = get_vendor_version_from_module(module_name)
  84. if not actual_version:
  85. extra_message = ' (Unable to locate actual module version, using'\
  86. ' vendor.txt specified version)'
  87. actual_version = expected_version
  88. elif actual_version != expected_version:
  89. extra_message = ' (CONFLICT: vendor.txt suggests version should'\
  90. ' be {})'.format(expected_version)
  91. logger.info(
  92. '{name}=={actual}{extra}'.format(
  93. name=module_name,
  94. actual=actual_version,
  95. extra=extra_message
  96. )
  97. )
  98. def show_vendor_versions():
  99. # type: () -> None
  100. logger.info('vendored library versions:')
  101. vendor_txt_versions = create_vendor_txt_map()
  102. with indent_log():
  103. show_actual_vendor_versions(vendor_txt_versions)
  104. def show_tags(options):
  105. # type: (Values) -> None
  106. tag_limit = 10
  107. target_python = make_target_python(options)
  108. tags = target_python.get_tags()
  109. # Display the target options that were explicitly provided.
  110. formatted_target = target_python.format_given()
  111. suffix = ''
  112. if formatted_target:
  113. suffix = ' (target: {})'.format(formatted_target)
  114. msg = 'Compatible tags: {}{}'.format(len(tags), suffix)
  115. logger.info(msg)
  116. if options.verbose < 1 and len(tags) > tag_limit:
  117. tags_limited = True
  118. tags = tags[:tag_limit]
  119. else:
  120. tags_limited = False
  121. with indent_log():
  122. for tag in tags:
  123. logger.info(str(tag))
  124. if tags_limited:
  125. msg = (
  126. '...\n'
  127. '[First {tag_limit} tags shown. Pass --verbose to show all.]'
  128. ).format(tag_limit=tag_limit)
  129. logger.info(msg)
  130. def ca_bundle_info(config):
  131. levels = set()
  132. for key, value in config.items():
  133. levels.add(key.split('.')[0])
  134. if not levels:
  135. return "Not specified"
  136. levels_that_override_global = ['install', 'wheel', 'download']
  137. global_overriding_level = [
  138. level for level in levels if level in levels_that_override_global
  139. ]
  140. if not global_overriding_level:
  141. return 'global'
  142. if 'global' in levels:
  143. levels.remove('global')
  144. return ", ".join(levels)
  145. class DebugCommand(Command):
  146. """
  147. Display debug information.
  148. """
  149. usage = """
  150. %prog <options>"""
  151. ignore_require_venv = True
  152. def __init__(self, *args, **kw):
  153. super(DebugCommand, self).__init__(*args, **kw)
  154. cmd_opts = self.cmd_opts
  155. cmdoptions.add_target_python_options(cmd_opts)
  156. self.parser.insert_option_group(0, cmd_opts)
  157. self.parser.config.load()
  158. def run(self, options, args):
  159. # type: (Values, List[Any]) -> int
  160. logger.warning(
  161. "This command is only meant for debugging. "
  162. "Do not use this with automation for parsing and getting these "
  163. "details, since the output and options of this command may "
  164. "change without notice."
  165. )
  166. show_value('pip version', get_pip_version())
  167. show_value('sys.version', sys.version)
  168. show_value('sys.executable', sys.executable)
  169. show_value('sys.getdefaultencoding', sys.getdefaultencoding())
  170. show_value('sys.getfilesystemencoding', sys.getfilesystemencoding())
  171. show_value(
  172. 'locale.getpreferredencoding', locale.getpreferredencoding(),
  173. )
  174. show_value('sys.platform', sys.platform)
  175. show_sys_implementation()
  176. show_value("'cert' config value", ca_bundle_info(self.parser.config))
  177. show_value("REQUESTS_CA_BUNDLE", os.environ.get('REQUESTS_CA_BUNDLE'))
  178. show_value("CURL_CA_BUNDLE", os.environ.get('CURL_CA_BUNDLE'))
  179. show_value("pip._vendor.certifi.where()", where())
  180. show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED)
  181. show_vendor_versions()
  182. show_tags(options)
  183. return SUCCESS