4 votes

Setuptools ajoute une latence de démarrage de 150ms à tous les scripts

Je travaille sur un projet Python multiplateforme. C'est un outil en ligne de commande avec une auto-complétion de shell, donc la rapidité est importante.

La façon dont setuptools génère le script console impose au moins un surcoût de 150 ms - parfois plus. C'est tout simplement inacceptable pour le type d'outil que je suis en train d'écrire, et cela ne devrait pas être nécessaire étant donné ce qu'il fait dans le cas de base.

J'essaie de suivre les meilleures pratiques pour un projet Python moderne, c'est pourquoi j'utilise setuptools pour construire le projet. Le package prend en charge Windows, donc sa capacité à générer des wrappers binaires pour les points d'entrée est essentielle.

Cela se produit indépendamment de la manière dont j'installe le package - pip install, pip install -e, pip install --egg, python setup.py install, etc.

Il y a un problème github qui discute du problème, mais pour l'instant, il n'y a pas de solution ou de contournement.

Ailleurs, j'ai vu des gens revenir à distutils à cause de cela, mais ce n'est pas une option pour mon projet.

Le seul contournement auquel je pense est d'une manière ou d'une autre étendre ou personnaliser ce que setuptools fait lors de l'installation de mon projet afin que le shim binaire n'utilise pas pkg_resources.

Que puis-je faire en dehors de cette mesure assez drastique et non constructive ?


Mon setup.py est basique - approximativement comme suit :

import pip.req
import setuptools

def install_reqs():
    reqs = pip.req.parse_requirements('requirements.txt', session=False)
    reqs = [str(ir.req) for ir in reqs]
    return reqs

setuptools.setup(
    name='myproj',
    version='v1.0.0-dev',
    packages=setuptools.find_packages(exclude=('tests')),
    install_requires=install_reqs(),
    include_package_data=True,
    entry_points={
        'console_scripts': [
            'myproj = myproj.cli.myproj:main',
        ]
    },
)

Le shim que génère setuptools pour le point d'entrée ressemble à ceci :

!$myhome/.venv/myproj/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'myproj==1.0.0.dev0','console_scripts','myproj'
__requires__ = 'myproj==1.0.0.dev0'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('myproj==1.0.0.dev0', 'console_scripts', 'myproj')()
    )

Voici quelques statistiques de cProfile en utilisant le script console généré par setuptools :

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
121/1    0.015    0.000    0.278    0.278 {built-in method builtins.exec}
    1    0.000    0.000    0.278    0.278 myproj:3()
125/3    0.001    0.000    0.221    0.074 :966(_find_and_load)
125/3    0.001    0.000    0.221    0.074 :939(_find_and_load_unlocked)
125/5    0.001    0.000    0.219    0.044 :659(_load_unlocked)
 99/5    0.001    0.000    0.219    0.044 :656(exec_module)
152/4    0.000    0.000    0.218    0.054 :214(_call_with_frames_removed)
    2    0.000    0.000    0.204    0.102 __init__.py:15()
32/15    0.000    0.000    0.135    0.009 {built-in method builtins.__import__}
    1    0.000    0.000    0.088    0.088 __init__.py:540(load_entry_point)
    1    0.000    0.000    0.085    0.085 __init__.py:2564(load_entry_point)
    1    0.000    0.000    0.083    0.083 __init__.py:2216(load)

Et le voici en tant que script personnalisé sans le shim de setuptools :

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 58/1    0.006    0.000    0.053    0.053 {built-in method builtins.exec}
    1    0.000    0.000    0.053    0.053 test.py:1()
 53/3    0.000    0.000    0.052    0.017 :966(_find_and_load)
 53/3    0.000    0.000    0.052    0.017 :939(_find_and_load_unlocked)
 53/5    0.000    0.000    0.051    0.010 :659(_load_unlocked)
 65/4    0.000    0.000    0.051    0.013 :214(_call_with_frames_removed)
 45/5    0.000    0.000    0.051    0.010 :656(exec_module)

Le script personnalisé - test.py - est très simple :

from myproj.cli.myproj import main

main()

0voto

Jake Kugel Points 31

J'ai également rencontré ce problème si j'installe un projet à partir de la source en utilisant 'pip install .'. Cependant, si je crée d'abord un fichier de roue binaire et ensuite j'installe la roue, le shim généré ne semble pas utiliser pkg_resources et est plus rapide. J'ai testé cela avec le projet cookiecutter :

https://github.com/audreyr/cookiecutter

Si je clone ce projet et ensuite j'installe en utilisant la méthode originale, 'pip install .', le script exécutable généré contient l'import de pkg_resources (et est lent) :

#!/usr/local/opt/python3/bin/python3.5
# EASY-INSTALL-ENTRY-SCRIPT: 'cookiecutter==1.5.1','console_scripts','cookiecutter'
__requires__ = 'cookiecutter==1.5.1'
import re
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(
        load_entry_point('cookiecutter==1.5.1', 'console_scripts', 'cookiecutter')()
    )

Cependant, si j'exécute les deux commandes suivantes :

python setup.py bdist_wheel
pip install dist/cookiecutter-1.5.1-py2.py3-none-any.whl

le shim généré ne contient pas pkg_resources (et est plus rapide) :

#!/usr/local/opt/python3/bin/python3.5

# -*- coding: utf-8 -*-
import re
import sys

from cookiecutter.__main__ import main

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

Et lorsque j'ai essayé la dernière méthode sur Windows, elle a quand même créé le shim .exe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X