web-dev-qa-db-fra.com

distutils: Comment passer un paramètre défini par l'utilisateur à setup.py?

Veuillez me demander comment passer un paramètre défini par l'utilisateur à partir de la ligne de commande et du fichier de configuration setup.cfg au script setup.py de distutils. Je veux écrire un script setup.py, qui accepte les paramètres spécifiques à mon package. Par exemple:

python setup.py install -foo myfoo

Merci,
Mher

59
Mher

Comme Setuptools/Distuils sont horriblement documentés, j'ai eu du mal à trouver la réponse à cela moi-même. Mais finalement, je suis tombé sur l'exemple this . En outre, this une question similaire a été utile. Fondamentalement, une commande personnalisée avec une option ressemblerait à:

from distutils.core import setup, Command

class InstallCommand(Command):
    description = "Installs the foo."
    user_options = [
        ('foo=', None, 'Specify the foo to bar.'),
    ]
    def initialize_options(self):
        self.foo = None
    def finalize_options(self):
        assert self.foo in (None, 'myFoo', 'myFoo2'), 'Invalid foo!'
    def run(self):
        install_all_the_things()

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

Voici une solution très simple, tout ce que vous avez à faire est de filtrer sys.argv Et de le gérer vous-même avant d'appeler distutils setup(..). Quelque chose comme ça:

if "--foo" in sys.argv:
    do_foo_stuff()
    sys.argv.remove("--foo")
...
setup(..)

La documentation sur la façon de le faire avec des distutils est terrible, j'ai finalement trouvé celui-ci: le guide des auto-stoppeurs pour l'emballage , qui utilise sdist et son user_options. Je trouve la référence extension distutils pas particulièrement utile.

Bien que cela ressemble à la façon "correcte" de le faire avec des distutils (au moins la seule que j'ai pu trouver qui soit vaguement documentée). Je n'ai rien trouvé sur les commutateurs --with Et --without Mentionnés dans l'autre réponse.

Le problème avec cette solution distutils est qu'elle est bien trop impliquée pour ce que je recherche (ce qui peut aussi être le cas pour vous). Ajouter des dizaines de lignes et sous-classer sdist est juste mal pour moi.

26
totaam

Oui, nous sommes en 2015 et la documentation pour ajouter des commandes et des options dans setuptools et distutils est encore largement manquante.

Après quelques heures frustrantes, j'ai trouvé le code suivant pour ajouter une option personnalisée à la commande install de setup.py:

from setuptools.command.install import install


class InstallCommand(install):
    user_options = install.user_options + [
        ('custom_option=', None, 'Path to something')
    ]

    def initialize_options(self):
        install.initialize_options(self)
        self.custom_option = None

    def finalize_options(self):
        #print('The custom option for install is ', self.custom_option)
        install.finalize_options(self)

    def run(self):
        global my_custom_option
        my_custom_option = self.custom_option
        install.run(self)  # OR: install.do_Egg_install(self)

Il convient de mentionner que install.run () vérifie s'il est appelé "nativement" ou s'il a été corrigé:

if not self._called_from_setup(inspect.currentframe()):
    orig.install.run(self)
else:
    self.do_Egg_install()

À ce stade, vous enregistrez votre commande avec setup:

setup(
    cmdclass={
        'install': InstallCommand,
    },
    :
16
Ronen Botzer

Vous ne pouvez pas vraiment transmettre de paramètres personnalisés au script. Cependant, les choses suivantes sont possibles et pourraient résoudre votre problème:

  • les fonctionnalités optionnelles peuvent être activées à l'aide de --with-featurename, les fonctionnalités standard peuvent être désactivées à l'aide de --without-featurename. [AFAIR cela nécessite des setuptools]
  • vous pouvez utiliser des variables d'environnement, celles-ci nécessitent cependant d'être set sous Windows alors que leur préfixe fonctionne sous linux/OS X (FOO=bar python setup.py).
  • vous pouvez étendre les distutils avec votre propre cmd_classes qui peut implémenter de nouvelles fonctionnalités. Ils sont également chaînables, vous pouvez donc l'utiliser pour changer des variables dans votre script. (python setup.py foo install) exécutera la commande foo avant d'exécuter install.

J'espère que cela aide en quelque sorte. D'une manière générale, je suggère de fournir un peu plus d'informations sur ce que devrait exactement faire votre paramètre supplémentaire, peut-être existe-t-il une meilleure solution.

11
Armin Ronacher

Peut-être que vous êtes un programmeur non chevronné comme moi qui a toujours eu du mal après avoir lu toutes les réponses ci-dessus. Ainsi, vous pourriez trouver un autre exemple potentiellement utile ( et pour répondre aux commentaires dans les réponses précédentes sur la saisie des arguments de ligne de commande ):

class RunClientCommand(Command):
    """
    A command class to runs the client GUI.
    """

    description = "runs client gui"

    # The format is (long option, short option, description).
    user_options = [
        ('socket=', None, 'The socket of the server to connect (e.g. '127.0.0.1:8000')',
    ]

    def initialize_options(self):
        """
        Sets the default value for the server socket.

        The method is responsible for setting default values for
        all the options that the command supports.

        Option dependencies should not be set here.
        """
        self.socket = '127.0.0.1:8000'

    def finalize_options(self):
        """
        Overriding a required abstract method.

        The method is responsible for setting and checking the 
        final values and option dependencies for all the options 
        just before the method run is executed.

        In practice, this is where the values are assigned and verified.
        """
        pass

    def run(self):
        """
        Semantically, runs 'python src/client/view.py SERVER_SOCKET' on the
        command line.
        """
        print(self.socket)
        errno = subprocess.call([sys.executable, 'src/client/view.py ' + self.socket])
        if errno != 0:
            raise SystemExit("Unable to run client GUI!")

setup(
    # Some other omitted details
    cmdclass={
        'runClient': RunClientCommand,
    },

Ce qui précède est testé et à partir d'un code que j'ai écrit. J'ai également inclus des docstrings légèrement plus détaillés pour rendre les choses plus faciles à comprendre.

Quant à la ligne de commande: python setup.py runClient --socket=127.0.0.1:7777. Une double vérification rapide à l'aide d'instructions print montre qu'en effet l'argument correct est récupéré par la méthode run.

Autres ressources que j'ai trouvées utiles ( plus et plus exemples):

commandes distutils personnalisées

https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html

5
OthmanEmpire

J'ai utilisé avec succès une solution de contournement pour utiliser une solution similaire à la suggestion de totaam. J'ai fini par faire apparaître mes arguments supplémentaires dans la liste sys.argv:

import sys
from distutils.core import setup
foo = 0
if '--foo' in sys.argv:
    index = sys.argv.index('--foo')
    sys.argv.pop(index)  # Removes the '--foo'
    foo = sys.argv.pop(index)  # Returns the element after the '--foo'
# The foo is now ready to use for the setup
setup(...)

Une validation supplémentaire pourrait être ajoutée pour s'assurer que les entrées sont bonnes, mais c'est comme ça que je l'ai fait

4
Jodin

Un moyen rapide et facile similaire à celui donné par totaam serait d'utiliser argparse pour saisir l'argument -foo et laisser les arguments restants pour l'appel à distutils.setup (). Utiliser argparse pour cela serait préférable à une itération via sys.argv manuellement à mon humble avis. Par exemple, ajoutez ceci au début de votre setup.py:

argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('--foo', help='required foo argument', required=True)
args, unknown = argparser.parse_known_args()
sys.argv = [sys.argv[0]] + unknown

Le add_help=False l'argument signifie que vous pouvez toujours obtenir l'aide régulière de setup.py en utilisant -h (à condition de --foo est donné).

1
andrew

Pour être entièrement compatible avec les deux python setup.py install et pip install . vous devez utiliser des variables d'environnement car pip option --install-option= est buggé:

  1. pip --install-option fuites sur les lignes
  2. Déterminez ce qui doit être fait pour - (installer | global) -option avec roues
  3. pip ne nommant pas correctement les roues abi

Ceci est un exemple complet n'utilisant pas le --install-option:

import os
environment_variable_name = 'MY_ENVIRONMENT_VARIABLE'
environment_variable_value = os.environ.get( environment_variable_name, None )

if environment_variable_value is not None:
    sys.stderr.write( "Using '%s=%s' environment variable!\n" % (
            environment_variable_name, environment_variable_value ) )

setup(
        name = 'packagename',
        version = '1.0.0',
        ...
)

Ensuite, vous pouvez l'exécuter comme ceci sous Linux:

MY_ENVIRONMENT_VARIABLE=1 pip install .
MY_ENVIRONMENT_VARIABLE=1 pip install -e .
MY_ENVIRONMENT_VARIABLE=1 python setup.py install
MY_ENVIRONMENT_VARIABLE=1 python setup.py develop

Mais, si vous êtes sous Windows, exécutez-le comme ceci:

set "MY_ENVIRONMENT_VARIABLE=1" && pip install .
set "MY_ENVIRONMENT_VARIABLE=1" && pip install -e .
set "MY_ENVIRONMENT_VARIABLE=1" && python setup.py install
set "MY_ENVIRONMENT_VARIABLE=1" && python setup.py develop

Références:

  1. Comment obtenir des arguments passés à setup.py à partir de pip avec '--install-option'?
  2. Passer des arguments de ligne de commande à l'installation de pip
  3. Passer le chemin de la bibliothèque comme argument de ligne de commande à setup.py
0
user