2011-05-25 68 views
22

我想能写:如何自动安装缺少的python模块?

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

什么是处理这种情况的建议/惯用方法是什么?

我见过很多脚本只是打印一个错误或警告,通知用户有关缺少的模块和(有时)提供有关如何安装的说明。但是,如果我知道该模块在PyPI上可用,那么我肯定可以进一步启动安装过程。没有?

+4

它在理论上很好,但它在现实中的一个痛苦的屁股。只需让用户自己安装软件包,或者在将软件包创建为依赖项时提供它们。 – 2011-05-25 08:34:27

+2

@JakobBowyer - 你的第一句话总结了人们得到报酬所做的大部分事情 - 如果某件事是一个好主意并且不难做到,那么它就已经完成了,任何人都不需要支付别人去通过PITA。我在工作中付出了努力编写一个脚本,该脚本能够自动部署服务器,并且可以在没有任何用户交互的情况下运行,因此我需要自动处理Python模块尚未安装的情况。现在,世界可以从我的收入中获益 - 我将它记录在下面:http://stackoverflow.com/a/25643988/901641 – ArtOfWarfare 2014-09-03 12:17:01

+0

5 upvotes不会使你的Python风格符合PEP8 – 2017-01-20 16:37:40

回答

21

安装问题不是源代码的主题!

您可以使用install_requires配置在包 的setup.py内正确定义您的相关性。

这就是要走的路......由于ImportError 安装的东西有点怪异和可怕。不要这样做。

+0

尽管setup.py本身需要所需的模块,但这并没有帮助。是否有setup()提供的某种钩子可以允许它通过setup_requires安装setup-time依赖关系,然后在setup()的同一个调用中使用该新安装的依赖关系中定义的命令类,而不会添加过多的样板文件为每个使用该依赖关系的包设置setup.py? – hosford42 2016-05-31 16:00:14

+0

我添加了[一个新问题](http://stackoverflow.com/questions/37551598/providing-a-custom-command-class-to-setup-py-in-a-separately-installed-package)来解决我的问题具体的用例。 – hosford42 2016-05-31 16:59:50

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

请勿触摸用户的安装。

5

下面是我放在一起的解决方案,我称之为pyInstall.py。它实际上检查模块是否安装,而不是依靠ImportError(在我看来,看起来更干净,用if而不是try/except来处理)。

我已经在版本2.6和2.7下使用它...如果我不想处理print作为函数,它可能会在旧版本中工作......我认为它可以在版本3.0+但我从来没有尝试过。

而且,正如我在getPip功能评论请注意,我不认为特定的功能将在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]) 

下面是一些用法示例工作:

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) 

编辑:让pipPath的更多的跨平台的方式是:

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

这使得假设pip将被安装在系统路径上。它在非Windows平台上往往非常可靠,但在Windows上最好在原始答案中使用代码。

5

冒着反对票,我想建议一个快速入侵。请注意,我完全接受了答案,应该从外部管理依赖关系。

但是,你绝对需要破解的东西,就像自包含的情况下,可以尝试一些象下面这样:

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 
+1

os.system已弃用...使用子流程模块 – 2017-01-20 16:26:55