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