¿Cómo instalo automáticamente los módulos faltantes de Python?

Me gustaría poder escribir:

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

¿Cuál es la forma recomendada / idiomática de manejar este escenario?

He visto muchos scripts que simplemente imprimen un error o una advertencia para notificar al usuario sobre el módulo faltante y (a veces) que proporcionan instrucciones sobre cómo instalar. Sin embargo, si sé que el módulo está disponible en PyPI , entonces seguramente podría ir un paso más allá e iniciar el proceso de instalación. ¿No?

¡Los problemas de instalación no están sujetos al código fuente!

Usted define sus dependencias correctamente dentro del setup.py de su paquete usando la configuración install_requires .

Ese es el camino a seguir … instalar algo como resultado de un ImportError es algo extraño y aterrador. No lo hagas

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

No toque la instalación del usuario.

Arriesgando votos negativos, me gustaría sugerir un truco rápido. Tenga en cuenta que estoy completamente de acuerdo con la respuesta aceptada de que las dependencias deben administrarse externamente.

Pero para situaciones en las que es absolutamente necesario piratear algo que actúa como autocontenido, puede intentar algo como a continuación:

 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 

Aquí está la solución que puse juntos y que llamo pyInstall.py . En realidad, comprueba si el módulo está instalado en lugar de confiar en ImportError (en mi opinión, parece más limpio para manejar esto con un if lugar de un try / except ).

Lo he usado en las versiones 2.6 y 2.7 … probablemente funcionaría en versiones anteriores si no quisiera manejar la print como una función … y creo que funcionará en la versión 3.0+, pero he nunca lo he intentado.

Además, como observo en los comentarios de mi función getPip , no creo que esa función en particular funcione en 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]) 

Aquí hay algunos ejemplos de uso:

 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) 

Edición : una forma más multiplataforma de obtener pipPath es:

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

Esto supone que se instalará / se instalará pip en la ruta del sistema. Tiende a ser bastante confiable en plataformas que no son de Windows, pero en Windows puede ser mejor usar el código en mi respuesta original.