【问题标题】:Mock a shell command in a script called from Python subprocess.Popen()在从 Python subprocess.Popen() 调用的脚本中模拟 shell 命令
【发布时间】:2023-04-04 06:50:02
【问题描述】:

我有一种情况,我需要使用我为单元测试编写的 shell 脚本 (mock_pip) 模拟可执行文件 (pip),因此我使用 subprocess 模块来访问 shell。我尝试了很多方法,例如:

subprocess.Popen("alias pip='/some/dir/mock_pip'", shell=True) # Create alias
subprocess.Popen("pip", shell=True) # Try to use new alias, but still points towards real pip instead

subprocess.Popen("alias pip='/some/dir/mock_pip'"; "pip", shell=True)
# Once again it uses the real pip instead of mock

我什至通过更改我的主目录中的~/.bashrc 文件(也在使用子进程进行单元测试期间)尝试了这个method,但这也不起作用。
我想问题是子进程在调用每个命令后都会擦除环境,这意味着当我尝试调用它时我的别名不存在。

如何在从我的 Python 进程启动的 bash 脚本中使用 mock_pip 而不是 pip

【问题讨论】:

  • 您发布的代码包含语法错误。也许你可以在设置shell=True 时让它工作,但我想它不会适用于不同的 popens。为什么不创建一个调用适当 pip 的 python 函数?
  • subprocess 不会擦除环境,操作系统会。将别名添加到.bashrc,但别名通常不会在脚本中扩展,它们是支持的噩梦,通常被认为是不好的做法(首选函数),你必须shopt -s expand_aliases(同样,在.bashrc)。
  • @syntonym 对不起,我忘了校对我的代码,但我确实有 shell=True。你是什​​么意思创建一个函数来调用适当的 pip 呢?
  • 您可以将更新后的环境与 PATH 更改放在您的 subprocess.Popen() 参数列表本身中。 subprocess.Popen(..., env={'PATH': '/directory/with/new/stub:' + os.environ['PATH']})
  • 顺便说一句,我建议修改您的问题,以便询问您真正有兴趣了解什么——即。从 Python 的subprocess.Popen 调用时如何模拟 shell 命令,而不是关于别名持久性。

标签: python bash shell subprocess


【解决方案1】:

如果您的目标是提供 pip 的模拟实现,您可以通过以下几种方式实现:

  • 将其生成为导出函数
  • 将 PATH 值设置为指向包含包装器的目录作为可执行脚本

前者是特定于 shell 版本的,所以我们先介绍后者:

subprocess.Popen('pip', env={'PATH': '/path/to/mock/dir:' + os.environ['PATH']})

...您的/path/to/mock/dir 是一个带有pip 可执行文件的位置,可执行您想要的操作。


后者,用于后 shellshock 上游 bash 版本(以及与上游到达的导出函数的协议兼容的 shellshock 修复的先前版本的 bash):

env = dict(os.environ)
env['BASH_FUNC_pip%%'] = '() { mock_pip "$@"; }'
subprocess.Popen(['bash', '-c', 'pip'], shell=False, env=env)

注意这里的shell=False,带有显式的['bash', '-c', ...]——确保它是bash(将遵循导出的函数)而不是调用的默认shell sh

【讨论】:

  • 我将路径设置为我的模拟 pip 所在的目录,它现在使用模拟而不是真正的 pip。但是仍然存在一个问题(我可能应该在前面提到这一点,但认为它不会影响任何事情),但是我试图模拟的 pip 在 bash 脚本中..(再次抱歉)所以基本上我有脚本mpp' which contains pip` 命令,这些是我要模拟的命令。我尝试了subprocess.Popen('mpp', env={'PATH': '/path/to/mock/dir:' + os.environ['PATH']}),但它使用真正的点子(位于 bin 中)而不是我的模拟点子。对不起,如果这令人困惑..
  • 嗯。除非干扰(例如正在执行修改继承的 PATH 的其他内容),否则这应该可以工作。如果你的脚本运行declare -p PATH,它会先打印模拟目录的值吗?如果那个脚本运行type pip >&2,结果是什么?
  • declare -p PATH 实际上确实为我提供了 mock 目录的路径,因此与 subprocess 中的 env 键设置的完全相同。至于type pip >&2,它直接指向我创建的模拟pip 脚本/path/to/mock/dir/pip。尽管如此,当我运行我的mpp 脚本时,它使用了真正的点子,我可以确认,因为我的模拟点子有一个不会更新的计数器。但是当我通过subprocess 运行一个简单的pip 命令时,模拟确实会被调用,因为计数器实际上会更新。
  • ...那些declare -p PATHtype pip 命令直接在mpp 调用真正的pip 之前?下一步可能是运行 mppset -x 并检查实际命令。
  • 哈哈哈我发现了问题所在,我给出的mpp 命令跳过了 pip 命令,因为它没有得到正确的参数,所以它根本就没有调用 pip。我尝试了其他一些命令,它确实调用了模拟。非常感谢您帮助我度过难关!
猜你喜欢
  • 2017-04-13
  • 2014-01-15
  • 1970-01-01
  • 2019-11-10
  • 2012-04-01
  • 2011-10-25
  • 2018-02-27
  • 1970-01-01
  • 2021-09-26
相关资源
最近更新 更多