pip包管理工具-install执行流程简单查看

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(
      
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值