Python 调用不需要虚拟环境的子进程

5
我有一个使用subprocess调用第三方工具的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
我并不了解 third-party-tool,除了它在我的路径上。在我激活虚拟环境时调用它会导致它卡住并在后面抛出异常。我不知道它是否使用默认的 Python 二进制文件,还是自己启动虚拟环境并在其中执行操作。它不是一个 Python 脚本,但显然会以某种方式调用一个脚本。 如何告诉 subprocess 退出我的虚拟环境并在默认 shell 环境中运行命令? 我查看了几个类似的问题:
2个回答

4

来自subprocess文档:

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={})

2

感谢您的帮助,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)

第三方工具

第三方工具被模拟为一个脚本,告诉您它是否在虚拟环境中并打印环境变量。在这个例子中,第三方工具是一个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

注意:输出如下所示: 这是第三方工具,你不在虚拟环境中。 (继续打印一系列键值对的环境变量列表)
我使用我最喜欢的差异工具并比较输出。 within_venv_from_python.txtwithout_venv.txt相同,这是一个好兆头(在这两种情况下,third-party-tool运行时具有相同的环境变量,并指示它不在矩阵中)。实施此解决方案后,我的实际第三方工具似乎正在工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接