54

我希望能够写:

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

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

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

4

4 回答 4

59

冒着反对票的风险,我想建议一个快速破解。请注意,我完全同意应在外部管理依赖项的答案。

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

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
于 2016-04-21T06:45:06.733 回答
40

安装问题不在源代码范围内!

setup.py您可以使用配置在包中正确定义依赖项install_requires

这就是要走的路……安装一些东西ImportError 是一种奇怪和可怕的结果。不要这样做。

于 2011-05-25T07:30:54.913 回答
35
try:
    import foo
except ImportError:
    sys.exit("""You need foo!
                install it from http://pypi.python.org/pypi/foo
                or run pip install foo.""")

不要触摸用户的安装。

于 2011-05-25T07:29:16.930 回答
10

这是我放在一起的解决方案,我称之为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 上,使用我原始答案中的代码可能会更好。

于 2014-09-03T12:12:03.050 回答