2016-07-09 4 views
2

Ich arbeite an einem plattformübergreifenden Python-Projekt. Es handelt sich um ein Befehlszeilentool mit automatischer Vervollständigung der Shell.setuptools fügt allen Skripten 150ms Startlatenz hinzu

Die Art und Weise setuptools erzeugt die Konsole Skript mindestens 150ms Overhead ist imposant - manchmal mehr. Das ist einfach inakzeptabel für die Art von Werkzeug, das ich schreibe, und sollte nicht notwendig sein, da es im Grundfall wenig tut.

Ich versuche, beste Praxis für ein modernes Python-Projekt zu folgen, also verwende ich setuptools das Projekt zu erstellen. Das Paket unterstützt Windows, daher ist es wichtig, Binär-Wrapper für Einstiegspunkte zu generieren.

Dies geschieht unabhängig davon, wie ich das Paket installieren - pip install, pip install -e, pip install --egg, python setup.py install usw.

Es gibt ein github issue das Problem diskutiert, aber keine Abhilfe oder Lösung so weit.

An anderer Stelle habe ich gesehen bewegt Menschen, weil dieser zu distutils zurück, aber das ist keine Option für mein Projekt.

Die einzige Problemumgehung, die ich mir vorstellen kann, ist irgendwie zu erweitern oder anzupassen, was setuptools tut, wenn es nach Projekt installiert, so dass das binäre Shim pkg_resources nicht verwendet.

Was kann ich kurz jener ziemlich drastische und unconstructive Maßnahme?


Mein setup.py ist einfach - in etwa wie folgt:

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', 
     ] 
    }, 
) 

Die Scheibe, die für den Einstiegspunkt Setuptools erzeugt das wie folgt aussieht:

!$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')() 
    ) 

Hier sind einige cProfile Statistiken die Setuptools verwenden -generiertes Konsolenskript:

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(<module>) 
125/3 0.001 0.000 0.221 0.074 <frozen importlib._bootstrap>:966(_find_and_load) 
125/3 0.001 0.000 0.221 0.074 <frozen importlib._bootstrap>:939(_find_and_load_unlocked) 
125/5 0.001 0.000 0.219 0.044 <frozen importlib._bootstrap>:659(_load_unlocked) 
99/5 0.001 0.000 0.219 0.044 <frozen importlib._bootstrap_external>:656(exec_module) 
152/4 0.000 0.000 0.218 0.054 <frozen importlib._bootstrap>:214(_call_with_frames_removed) 
    2 0.000 0.000 0.204 0.102 __init__.py:15(<module>) 
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) 

Und hier ist es als ein benutzerdefiniertes Skript ohne Setuptools Shim:

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(<module>) 
53/3 0.000 0.000 0.052 0.017 <frozen importlib._bootstrap>:966(_find_and_load) 
53/3 0.000 0.000 0.052 0.017 <frozen importlib._bootstrap>:939(_find_and_load_unlocked) 
53/5 0.000 0.000 0.051 0.010 <frozen importlib._bootstrap>:659(_load_unlocked) 
65/4 0.000 0.000 0.051 0.013 <frozen importlib._bootstrap>:214(_call_with_frames_removed) 
45/5 0.000 0.000 0.051 0.010 <frozen importlib._bootstrap_external>:656(exec_module) 

Die benutzerdefinierten Skript - test.py - ist sehr einfach:

from myproj.cli.myproj import main 

main() 
+0

Also dann schreibe dein eigenes Skript. –

+0

Wie würde ich es bereitstellen? Wenn setuptools etwas von "console_scripts" oder "scripts" bereitstellt, wird das Shim generiert, das 'load_entry_point()' verwendet, was langsam ist. – Cera

Antwort

0

Ich habe dieses Problem auch gesehen, wenn Ich installiere ein Projekt von der Quelle mit 'pip install.'. Wenn ich jedoch zuerst eine binäre Raddatei erstelle und dann das Rad installiere, scheint das erzeugte Shim nicht pkg_resources zu verwenden und ist schneller. Ich habe das mit dem Projekt getestet cookie:

https://github.com/audreyr/cookiecutter

Wenn ich dieses Projekt klonen und installieren Sie dann die ursprüngliche Methode verwenden, ‚installiert pip.‘Enthält das erzeugte ausführbare Skript den Import von pkg_resources (und langsam):

#!/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')() 
    ) 

Allerdings, wenn ich die beiden folgenden Befehle:

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

die erzeugte Shim enthält keine pkg_resources (und ist schneller):

Und als ich die letztere Methode unter Windows versuchte, erstellte sie immer noch die EXE-Shim.

Verwandte Themen