web-dev-qa-db-fra.com

Exécutez une post-installation du script en Python avec l'aide de distutils/setuptools

J'essaie d'ajouter une tâche de post-installation à Python distutils comme décrit dans Comment étendre distutils avec un simple script de post-installation? . La tâche est supposée exécuter un script Python dans le répertoire lib installé . Ce script génère des modules Python supplémentaires requis par le paquet installé.

Ma première tentative est la suivante:

from distutils.core import setup
from distutils.command.install import install

class post_install(install):
    def run(self):
        install.run(self)
        from subprocess import call
        call(['python', 'scriptname.py'],
             cwd=self.install_lib + 'packagename')

setup(
 ...
 cmdclass={'install': post_install},
)

Cette approche fonctionne, mais pour autant que je sache, elle présente deux inconvénients:

  1. Si l'utilisateur a utilisé un interpréteur Python autre que celui extrait de PATH, le script de post-installation sera exécuté avec un interpréteur différent , ce qui pourrait poser problème.
  2. Ce n'est pas sûr contre la marche à sec, etc., auquel je pourrais peut-être remédier en l'enveloppant dans une fonction et en l'appelant avec distutils.cmd.Command.execute.

Comment pourrais-je améliorer ma solution? Existe-t-il un moyen/une pratique recommandée pour ce faire? J'aimerais éviter de créer une autre dépendance si possible.

28
kynan

La façon de remédier à ces carences est la suivante:

  1. Obtenez le chemin complet de l'interpréteur Python exécutant setup.py à partir de sys.executable.
  2. Les classes héritées de distutils.cmd.Command (comme distutils.command.install.install que nous utilisons ici) implémentent la méthode execute, qui exécute une fonction donnée de manière "sans danger", c'est-à-dire en respectant l'indicateur d'exécution à sec.

    Notez cependant que l'option --dry-run est actuellement cassée et ne fonctionne pas comme prévu de toute façon.

J'ai fini avec la solution suivante:

import os, sys
from distutils.core import setup
from distutils.command.install import install as _install


def _post_install(dir):
    from subprocess import call
    call([sys.executable, 'scriptname.py'],
         cwd=os.path.join(dir, 'packagename'))


class install(_install):
    def run(self):
        _install.run(self)
        self.execute(_post_install, (self.install_lib,),
                     msg="Running post install task")


setup(
    ...
    cmdclass={'install': install},
)

Notez que j’utilise le nom de classe install pour ma classe dérivée car c’est ce que python setup.py --help-commands utilisera.

34
kynan

Je pense que le moyen le plus simple d'effectuer la post-installation et de respecter les exigences consiste à décorer l'appel à setup(...):

from setup tools import setup


def _post_install(setup):
    def _post_actions():
        do_things()
    _post_actions()
    return setup

setup = _post_install(
    setup(
        name='NAME',
        install_requires=['...
    )
)

Ceci fonctionnera setup() lors de la déclaration de setup. Une fois l’installation des exigences terminée, il lancera la fonction _post_install(), qui exécutera la fonction interne _post_actions().

1
Mbm