奇怪〜使用python setuptools启动惩罚150ms

我使用
python的setuptools一直有一个奇怪的150ms启动惩罚,我构建了一个最小的测试用例,问题仍然存在于那里:

这个最小案例的项目布局是:

- setup.py
- setuptest
- - __init__.py
- - __main__.py

setup.py文件包含:

from setuptools import setup

setup(
    name         = 'setuptest',
    version      = '0.1',
    packages     = ['setuptest'],

    entry_points = {
        'console_scripts' : ['setuptest = setuptest.__main__:main']
        } ,
    )

__main__.py文件只包含:

#!/usr/bin/env python2

def main ():
    print "hai"

if __name__ == '__main__':
    main()

在项目根目录中执行此操作:

 —— — time python2 setuptest
hai

real    0m0.021s
user    0m0.017s
sys     0m0.004s

然而,在运行sudo python2 setup.py install并执行以下操作后,我总共执行了21 ms的脚本:

 —— — time setuptest 
hai

real    0m0.158s
user    0m0.144s
sys     0m0.012s
 —— — 

给我158ms.这个150s的启动延迟时间是一致的,当我使用setuptools时会发生这种情况,但是我没有通过包管理器安装的东西或者手动安装其他人的项目,这让我觉得我显然做了非常错误的事情.

最佳答案 好吧,当你使用setuptools安装软件时,它会在bin目录中生成可执行脚本,如下所示:

import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('<PACKAGE_NAME>', 'console_scripts', '<ENTRY_POINT>')()
    )

因为load_entry_point()将解析sys.path中可用的所有包,所以安装的位置和包越多,构建列表所需的时间越长,然后查找它.

有关更多详细信息,我们需要查看setuptools的load_entry_point()实现:

setuptools.py:load_entry_point()

def load_entry_point(dist, group, name):
    """Return `name` entry point of `group` for `dist` or raise ImportError"""
    return get_distribution(dist).load_entry_point(group, name)

‘setuptools.py:get_distribution()’开始:

def get_distribution(dist):
    """Return a current distribution object for a Requirement or string"""
    if isinstance(dist,basestring): dist = Requirement.parse(dist)
    if isinstance(dist,Requirement): dist = get_provider(dist)
    if not isinstance(dist,Distribution):
        raise TypeError("Expected string, Requirement, or Distribution", dist)
    return dist

setuptools.py:Distribution.load_entry_point()

def load_entry_point(self, group, name):
    """Return the `name` entry point of `group` or raise ImportError"""
    ep = self.get_entry_info(group,name)
    if ep is None:
        raise ImportError("Entry point %r not found" % ((group,name),))
    return ep.load()

setuptools.py:Distribution.get_entry_info()

def get_entry_info(self, group, name):
    """Return the EntryPoint object for `group`+`name`, or ``None``"""
    return self.get_entry_map(group).get(name)

我会把它留在那里,你可以跟进它变得昂贵的地方.我想Distribution中完成映射的方法(如_dep_map属性)在执行时可能非常昂贵.

点赞