pip概述
pip是python提供的包管理工具,该工具提供了对python包的查找、下载、安装与卸载等功能的工具,当前是python中比较主流的管理工具。
pip下载安装包的概述
pip工具的本质通过网络请求去请求服务端对应名称的文件然后解压到本地的python的库文件夹中,从而达到将远端的python包下载并安装到本地。概述流程如下:
pip install django
1.先获取到远端的服务器地址url比如:http://mirrors.aliyun.com/pypi/simple/
2.然后获取到本地的库安装的路径,通过服务器url去查找对应的django包
3.讲找到的包下载到本地
4.解压该包到python的site-packages文件夹中,然后检查是否需要依赖其他包,如果依赖就安装其他的包
5.如果有依赖安装则按照同样的流程执行,待完成之后包就安装完成
pip安装的初始化流程
因为pip安装的本质就是通过url去服务端请求文件并下载,pip网络请求使用的requests库,只不过被封装到了pip的_vendor文件夹里面,并没有将requests包进行安装。
本次执行示例,假设python环境中还未安装Django,此时我们执行pip install django,函数的入口如下:
pip = pip._internal:main
查看main函数;
def main(args=None):
if args is None:
args = sys.argv[1:] # 获取命令行输入参数
# Configure our deprecation warnings to be sent through loggers
deprecation.install_warning_logger()
autocomplete()
try:
cmd_name, cmd_args = parse_command(args) # 解析传入的命令行参数
except PipError as exc:
sys.stderr.write("ERROR: %s" % exc)
sys.stderr.write(os.linesep)
sys.exit(1)
# Needed for locale.getpreferredencoding(False) to work
# in pip._internal.utils.encoding.auto_decode
try:
locale.setlocale(locale.LC_ALL, '')
except locale.Error as e:
# setlocale can apparently crash if locale are uninitialized
logger.debug("Ignoring error %s when setting locale", e)
command = commands_dict[cmd_name](isolated=("--isolated" in cmd_args)) # 根据传入的命令找到对应的command 本例中传入的是install
return command.main(cmd_args) # 执行对应cmd的main 方法
此时查看commands_dict中对应的install命令,该命令是InstallCommand,通过继承关系可知,InstallCommand继承自 RequirementCommand,而RequirementCommand继承自Command类,从command.main的执行的是Command类的main方法,
def main(self, args):
# type: (List[str]) -> int
options, args = self.parse_args(args)。 # 解析传入的参数
# Set verbosity so that it can be used elsewhere.
self.verbosity = options.verbose - options.quiet
level_number = setup_logging(
verbosity=self.verbosity,
no_color=options.no_color,
user_log_file=options.log,
) # 设置打印日志的等级
if sys.version_info[:2] == (3, 4): # 获取当前的版本是否是3.4
deprecated(
"Python 3.4 support has been deprecated. pip 19.1 will be the "
"last one supporting it. Please upgrade your Python as Python "
"3.4 won't be maintained after March 2019 (cf PEP 429).",
replacement=None,
gone_in='19.2',
)
elif sys.version_info[:2] == (2, 7): # 是否是2.7
message = (
"A future version of pip will drop support for Python 2.7."
)
if platform.python_implementation() == "CPython":
message = (
"Python 2.7 will reach the end of its life on January "
"1st, 2020. Please upgrade your Python as Python 2.7 "
"won't be maintained after that date. "
) + message
deprecated(message, replacement=None, gone_in=None)
# TODO: Try to get these passing down from the command?
# without resorting to os.environ to hold these.
# This also affects isolated builds and it should.
if options.no_input:
os.environ['PIP_NO_INPUT'] = '1'
if options.exists_action:
os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action)
if options.require_venv and not self.ignore_require_venv:
# If a venv is required check if it can really be found
if not running_under_virtualenv():
logger.critical(
'Could not find an activated virtualenv (required).'
)
sys.exit(VIRTUALENV_NOT_FOUND)
try:
status = self.run(options, args) # 调用子类实现的run方法来执行业务
# FIXME: all commands should return an exit status
# and when it is done, isinstance is not needed anymore
if isinstance(status, int):
return status
except PreviousBuildDirError as exc: # 报错处理流程
logger.critical(str(exc))
logger.debug('Exception information:', exc_info=True)
return PREVIOUS_BUILD_DIR_ERROR
except (InstallationError, UninstallationError, BadCommand) as exc:
logger.critical(str(exc))
logger.debug('Exception information:', exc_info=True)
return ERROR
except CommandError as exc:
logger.critical('ERROR: %s', exc)
logger.debug('Exception information:', exc_info=True)
return ERROR
except BrokenStdoutLoggingError:
# Bypass our logger and write any remaining messages to stderr
# because stdout no longer works.
print('ERROR: Pipe to stdout was broken', file=sys.stderr)
if level_number <= logging.DEBUG:
traceback.print_exc(file=sys.stderr)
return ERROR
except KeyboardInterrupt:
logger.critical('Operation cancelled by user')
logger.debug('Exception information:', exc_info=True)
return ERROR
except BaseException:
logger.critical('Exception:', exc_info=True)
return UNKNOWN_ERROR
finally:
allow_version_check = (
# Does this command have the index_group options?
hasattr(options, "no_index") and
# Is this command allowed to perform this check?
not (options.disable_pip_version_check or options.no_index)
)
# Check if we're using the latest version of pip available
if allow_version_check:
session = self._build_session(
options,
retries=0,
timeout=min(5, options.timeout)
)
with session:
pip_version_check(session, options)
# Shutdown the logging module
logging.shutdown()
return SUCCESS
此时我们查看InstallCommand类的run方法,
def run(self, options, args):
cmdoptions.check_install_build_global(options) # 检查输入的build-option等信息
upgrade_strategy = "to-satisfy-only"
if options.upgrade:
upgrade_strategy = options.upgrade_strategy
if options.build_dir:
options.build_dir = os.path.abspath(options.build_dir) # _build_package_finder
cmdoptions.check_dist_restriction(options, check_target=True)
if options.python_version:
python_versions = [options.python_version] # python版本
else:
python_versions = None
options.src_dir = os.path.abspath(options.src_dir) # 获取src_dir文件路径
install_options = options.install_options or [] # 注册选项
if options.use_user_site:
if options.prefix_path:
raise CommandError(
"Can not combine '--user' and '--prefix' as they imply "
"different installation locations"
)
if virtualenv_no_global():
raise InstallationError(
"Can not perform a '--user' install. User site-packages "
"are not visible in this virtualenv."
)
install_options.append('--user')
install_options.append('--prefix=')
target_temp_dir = TempDirectory(kind="target")
if options.target_dir:
options.ignore_installed = True
options.target_dir = os.path.abspath(options.target_dir)
if (os.path.exists(options.target_dir) and not
os.path.isdir(options.target_dir)):
raise CommandError(