【问题标题】:How to install and import Python modules at runtime?如何在运行时安装和导入 Python 模块?
【发布时间】:2013-06-18 13:21:34
【问题描述】:

我想编写一个脚本来自动设置一个全新的 ubuntu 安装并安装一个基于 django 的应用程序。由于脚本将在新服务器上运行,因此 Python 脚本需要自动安装一些必需的模块。

这是脚本。

#!/usr/bin/env python

import subprocess
import os
import sys

def pip_install(mod):
    print subprocess.check_output("pip install %s" % mod, shell=True)

if __name__ == "__main__":
    if os.getuid() != 0:
        print "Sorry, you need to run the script as root."
        sys.exit()

    try:
        import pexpect
    except:
        pip_install('pexpect') 
        import pexpect        

    # More code here...

pexpect 安装成功,但是下一行import pexpect 安装失败。我认为这是因为在运行时代码不知道新安装的pexpect

如何在运行时安装和导入 Python 模块?我对其他方法持开放态度。

【问题讨论】:

  • 未经测试:pexpect = __import__('pexpect')

标签: python linux subprocess


【解决方案1】:

您可以导入 pip 而不是使用子进程:

import pip

def install(package):
    pip.main(['install', package])

# Example
if __name__ == '__main__':
    try:
        import pexpect
    except ImportError:
        install('pexpect')
        import pexpect

另一个镜头:

import pip

def import_with_auto_install(package):
    try:
        return __import__(package)
    except ImportError:
        pip.main(['install', package])
    return __import__(package)

# Example
if __name__ == '__main__':
    pexpect = import_with_auto_install('pexpect')
    print(pexpect)

[编辑]

您应该考虑将requirements.txt 与 pip 一起使用。好像您正在尝试自动化部署(这很好!),在我的工具带中,我还有 virtualenvwrapper、vagrantansible

这是我的输出:

(test)root@vagrant:~/test# pip uninstall pexpect
Uninstalling pexpect:
  /usr/lib/python-environments/test/lib/python2.6/site-packages/ANSI.py
  /usr/lib/python-environments/test/lib/python2.6/site-packages/ANSI.pyc
  /usr/lib/python-environments/test/lib/python2.6/site-packages/FSM.py
  /usr/lib/python-environments/test/lib/python2.6/site-packages/FSM.pyc
  /usr/lib/python-environments/test/lib/python2.6/site-packages/fdpexpect.py
  /usr/lib/python-environments/test/lib/python2.6/site-packages/fdpexpect.pyc
  /usr/lib/python-environments/test/lib/python2.6/site-packages/pexpect-2.4-py2.6.egg-info
  /usr/lib/python-environments/test/lib/python2.6/site-packages/pexpect.py
  /usr/lib/python-environments/test/lib/python2.6/site-packages/pexpect.pyc
  /usr/lib/python-environments/test/lib/python2.6/site-packages/pxssh.py
  /usr/lib/python-environments/test/lib/python2.6/site-packages/pxssh.pyc
  /usr/lib/python-environments/test/lib/python2.6/site-packages/screen.py
  /usr/lib/python-environments/test/lib/python2.6/site-packages/screen.pyc
Proceed (y/n)? y
  Successfully uninstalled pexpect
(test)root@vagrant:~/test# python test.py
Downloading/unpacking pexpect
  Downloading pexpect-2.4.tar.gz (113Kb): 113Kb downloaded
  Running setup.py egg_info for package pexpect
Installing collected packages: pexpect
  Running setup.py install for pexpect
Successfully installed pexpect
Cleaning up...
<module 'pexpect' from '/usr/lib/python-environments/test/lib/python2.6/site-packages/pexpect.pyc'>
(test)root@vagrant:~/test#

【讨论】:

  • 问题依旧:安装成功,但是导入生成ImportError: no module named pexpect
  • 不,我在新的 ubuntu 安装上编写并运行脚本。它只安装了python-pip 包。
【解决方案2】:

我使用imp 模块解决了我的问题。

#!/usr/bin/env python

import pip
import imp

def install_and_load(package):
    pip.main(['install', package])

    path = '/usr/local/lib/python2.7/dist-packages'
    if path not in sys.path:
        sys.path.append(path)

    f, fname, desc = imp.find_module(package)
    return imp.load(package, f, fname, desc)

if __name__ == "__main__":
    try:
        import pexpect
    except:
        pexpect = install_and_load('pexpect')

    # More code...

实际上代码并不理想,因为我需要对 Python 模块目录进行硬编码。但由于该脚本适用于已知的目标系统,我认为没问题。

【讨论】:

    【解决方案3】:

    我实际上为此目的制作了一个模块 (impstall)

    它真的很容易使用:

    import impstall
    impstall.now('pexpect')
    impstall.now('wx', pipName='wxPython')
    

    Github link for issues/contributions

    【讨论】:

      【解决方案4】:

      对于那些使用大于 10.x 的 pip 版本的用户,pip 没有 main 函数,因此替代方法是使用 import pip._internal as pip 而不是 import pip,例如:

      Paulo的更新答案

      import pip._internal as pip
      
      def install(package):
          pip.main(['install', package])
      
      if __name__ == '__main__':
          try:
              import pexpect
          except ImportError:
              install('pexpect')
              import pexpect
      

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题,但 Google 的搜索都没有帮助。经过几个小时的调试,我发现可能是因为sys.path没有重新加载新的安装目录。

        在我的 Ubuntu Docker 上,我想在 Python3.8(预安装)的运行时 import dns.resolver。我还创建了ubuntu 用户并使用该用户运行所有东西(包括我的 Python 脚本)。

        • 在安装之前,sys.path 没有 /home/ubuntu/.local/lib/python3.8/site-packages,因为我没有安装任何东西。
        • 在使用subprocesspip.main 安装时,它会创建/home/ubuntu/.local/lib/python3.8/site-packages(作为用户安装)。
        • 安装后,sys.path 应刷新以包含此新位置。

        由于sys.pathsite 模块管理,我们应该重新加载它(参考HERE):

            import site
            from importlib import reload
            reload(site)
        

        任何需要的人的完整块:

        import subprocess
        import sys
        
        try:
            import dns.resolver
        except ImportError:
            subprocess.check_call([sys.executable, "-m", "pip", "install", "dnspython"])
            import site
            from importlib import reload
            reload(site)
            import dns.resolver
        

        我不是 Python 开发人员,因此这些代码可以进一步简化。对于像我这样的 DevOps 工程师来说,这可能会在新的 CI/CD 环境等情况下有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-08
          • 1970-01-01
          相关资源
          最近更新 更多