web-dev-qa-db-fra.com

Comment installer automatiquement les modules python) manquants?

Je voudrais pouvoir écrire:

try:
    import foo
except ImportError:
    install_the_module("foo")

Quelle est la manière recommandée/idiomatique de gérer ce scénario?

J'ai vu beaucoup de scripts simplement imprimer une erreur ou un avertissement informant l'utilisateur du module manquant et (parfois) fournissant des instructions sur la façon d'installer. Cependant, si je sais que le module est disponible sur PyPI , alors je pourrais sûrement aller plus loin et lancer le processus d'installation. Non?

33
j b

Les problèmes d'installation ne sont pas soumis au code source!

Vous définissez correctement vos dépendances dans le setup.py de votre colis en utilisant le install_requires configuration.

C'est la voie à suivre ... installer quelque chose à la suite d'un ImportError est assez étrange et effrayant. Ne le fais pas.

26
Andreas Jung
try:
    import foo
except ImportError:
    sys.exit("""You need foo!
                install it from http://pypi.python.org/pypi/foo
                or run pip install foo.""")

Ne touchez pas l'installation de l'utilisateur.

22
CharlesB

Risquant des votes négatifs, je voudrais suggérer un hack rapide. Veuillez noter que je suis totalement d'accord avec la réponse acceptée que les dépendances doivent être gérées en externe.

Mais pour les situations où vous devez absolument pirater quelque chose qui agit comme autonome, vous pouvez essayer quelque chose comme ci-dessous:

import os

try:
  import requests
except ImportError:
  print "Trying to Install required module: requests\n"
  os.system('python -m pip install requests')
# -- above lines try to install requests module if not present
# -- if all went well, import required module again ( for global access)
import requests
19
ring bearer

Voici la solution que j'ai élaborée et que j'appelle pyInstall.py. Il vérifie en fait si le module est installé plutôt que de s'appuyer sur ImportError (il semble juste plus propre, à mon avis, de gérer cela avec un if plutôt qu'avec un try/except).

Je l'ai utilisé sous les versions 2.6 et 2.7 ... cela fonctionnerait probablement dans les anciennes versions si je ne voulais pas gérer print en tant que fonction ... et je pense que cela fonctionnera dans la version 3.0 + mais je ne l'ai jamais essayé.

De plus, comme je le note dans les commentaires de ma fonction getPip, je ne pense pas que cette fonction particulière fonctionnera sous OS X.

from __future__ import print_function
from subprocess import call

def installPip(log=print):
    """
    Pip is the standard package manager for Python. Starting with Python 3.4
    it's included in the default installation, but older versions may need to
    download and install it. This code should pretty cleanly do just that.
    """
    log("Installing pip, the standard Python Package Manager, first")
    from os     import remove
    from urllib import urlretrieve
    urlretrieve("https://bootstrap.pypa.io/get-pip.py", "get-pip.py")
    call(["python", "get-pip.py"])

    # Clean up now...
    remove("get-pip.py")

def getPip(log=print):
    """
    Pip is the standard package manager for Python.
    This returns the path to the pip executable, installing it if necessary.
    """
    from os.path import isfile, join
    from sys     import prefix
    # Generate the path to where pip is or will be installed... this has been
    # tested and works on Windows, but will likely need tweaking for other OS's.
    # On OS X, I seem to have pip at /usr/local/bin/pip?
    pipPath = join(prefix, 'Scripts', 'pip.exe')

    # Check if pip is installed, and install it if it isn't.
    if not isfile(pipPath):
        installPip(log)
        if not isfile(pipPath):
            raise("Failed to find or install pip!")
    return pipPath

def installIfNeeded(moduleName, nameOnPip=None, notes="", log=print):
    """ Installs a Python library using pip, if it isn't already installed. """
    from pkgutil import iter_modules

    # Check if the module is installed
    if moduleName not in [Tuple_[1] for Tuple_ in iter_modules()]:
        log("Installing " + moduleName + notes + " Library for Python")
        call([getPip(log), "install", nameOnPip if nameOnPip else moduleName])

Voici quelques exemples d'utilisation:

from datetime  import datetime
from pyInstall import installIfNeeded

# I like to have my messages timestamped so I can get an idea of how long they take.
def log(message):
    print(datetime.now().strftime("%a %b %d %H:%M:%S") + " - " + str(message))

# The name fabric doesn't really convey to the end user why the module is needed,
# so I include a very quick note that it's used for SSH.
installIfNeeded("fabric", notes = " (ssh)", log = log)

# SoftLayer is actually named softlayer on pip.
installIfNeeded("SoftLayer", "softlayer", log = log)

Edit: Un moyen plus multiplateforme d'obtenir pipPath est:

from subprocess import Popen, PIPE
Finder = Popen(['where' if isWindows() else 'which', 'pip'], stdout = PIPE, stderr = PIPE)
pipPath = Finder.communicate()[0].strip()

Cela suppose que pip est/sera installé sur le chemin du système. Il a tendance à être assez fiable sur les plates-formes non Windows, mais sous Windows, il peut être préférable d'utiliser le code dans ma réponse d'origine.

6
ArtOfWarfare