【问题标题】:Python calling subprocess that requires no virtual environmentPython调用不需要虚拟环境的子进程
【发布时间】:2019-09-08 09:36:08
【问题描述】:

我有一个 Python 3.6 脚本,它使用子进程调用第三方工具。

main_script.py:

#!/usr/bin/env python
import subprocess
result = subprocess.run(['third-party-tool', '-arg1'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

问题是,main_script.py 必须在虚拟环境中运行,而third-party-tool 必须在任何虚拟环境中都没有运行。

我对@9​​87654327@ 了解不多,除了它在我的路上。在虚拟环境处于活动状态时调用它会导致它阻塞并稍后引发异常。我不知道它是使用默认的 python 二进制文件还是它启动自己的虚拟环境并在那里做一些事情。它不是 Python 脚本,但显然以某种方式调用了它。

如何让子进程退出我的虚拟环境并在默认 shell 环境中运行命令?

我研究了几个类似的问题:

【问题讨论】:

    标签: python subprocess virtual environment


    【解决方案1】:

    感谢您的帮助,nullUser;您的解决方案是对我的问题的简洁正确的答案。

    但是,当我试用它时,我的第三方工具现在由于其他(未知)原因而失败。可能还有其他一些我不知道的环境变量在新的 shell 中丢失了。幸运的是,我找到了一个替代解决方案,我将分享给其他正在苦苦挣扎的人。

    我的解决方案

    据我所知,进入虚拟环境对我的环境的唯一区别是向我的 PATH 变量添加新路径,并添加变量 VIRTUAL_ENV。

    我可以通过创建我的环境的副本来复制外部虚拟环境的行为,其中我:

    • 删除该 VIRTUAL_ENV 环境变量并
    • 从 PATH 中删除 python 前缀。

    示例

    my_script.py

    my_script.py 实现我的解决方案:

    #!/usr/bin/env python
    import subprocess, os, sys
    
    env = os.environ.copy()
    if hasattr(sys,'real_prefix'):
        # If in virtual environment, gotta forge a copy of the environment, where we:
        # Delete the VIRTUAL_ENV variable.
        del(env['VIRTUAL_ENV'])
    
        # Delete the "/home/me/.python_venv/main/bin:" from the front of my PATH variable.
        orig_path = env['PATH']
        virtual_env_prefix = sys.prefix + '/bin:'
        env['PATH'] = orig_path.replace(virtual_env_prefix, '')
    
    # Pass the environment into the third party tool, modified if and when required.
    subprocess.run(['./third-party-tool'], shell=False, env=env)
    

    第三方工具

    third-party-tool 被模拟为一个脚本,告诉您它是否在虚拟环境中并打印出环境变量。在此示例中,third-party-tool 是 Python 脚本,但一般情况下可能不是。

    #!/usr/bin/env python
    # third-party-tool
    import sys, os
    in_venv = hasattr(sys, 'real_prefix')
    print('This is third-party Tool and you {} in a virtual environment.'.format("ARE" if in_venv else "ARE NOT"))
    os.system('env')
    

    测试

    现在我尝试从外部虚拟环境、内部虚拟环境和虚拟环境中的 python 脚本调用第三方工具,捕获输出。

    [me@host ~]$ ./third-party-tool > without_venv.txt
    # Now I activate virtual environment
    (main) [me@host ~]$ ./third-party-tool > within_venv.txt
    (main) [me@host ~]$ ./my_script.py > within_venv_from_python.txt
    

    注意:输出如下所示: 这是第三方工具,您不在虚拟环境中。 (继续打印出 KEY=VALUE 环境变量列表)

    我使用我最喜欢的差异工具并比较输出。 within_venv_from_python.txtwithout_venv.txt 相同,这是一个好兆头(在这两种情况下,third-party-tool 使用相同的环境变量运行,并表明它不在矩阵中)。实施此解决方案后,我实际的第三方工具似乎正在工作。

    【讨论】:

      【解决方案2】:

      来自子流程的文档:

      https://docs.python.org/3/library/subprocess.html

      接受的参数是

      subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None,
          capture_output=False, shell=False, cwd=None, timeout=None, check=False,
          encoding=None, errors=None, text=None, env=None, universal_newlines=None)
      

      特别是,

      如果env不是None,它必须是一个定义新进程的环境变量的映射;这些用于代替继承当前进程环境的默认行为。它直接传递给 Popen。

      因此,传递一个空字典 env={}(从空环境开始)并使用 bash --login(作为登录 shell 运行,读取 env 默认值)应该可以解决问题。

      subprocess.run(['bash', '--login', '-c', '/full/path/to/third-party-tool', '-arg1'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={})
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-29
        • 1970-01-01
        • 2019-06-13
        • 2019-09-24
        相关资源
        最近更新 更多