gpt4 book ai didi

python - 在 Python 中拦截 subprocess.Popen 调用

转载 作者:太空宇宙 更新时间:2023-11-03 13:05:13 27 4
gpt4 key购买 nike

我正在为遗留的 Python 脚本编写功能测试,这样我就可以对其进行一行更改而不会因恐惧而瘫痪。 ;)

有问题的脚本使用 subprocess.Popen 调用 wget(1) 来下载然后解析的 XML 文件:

def download_files():
os.mkdir(FEED_DIR)
os.chdir(FEED_DIR)

wget_process = Popen(
["wget", "--quiet", "--output-document", "-", "ftp://foo.com/bar.tar"],
stdout=PIPE
)
tar_process = Popen(["tar", "xf", "-"], stdin=wget_process.stdout)
stdout, stderr = tar_process.communicate()

显然,最好修改脚本以使用 HTTP 库而不是执行 wget,但正如我所说,它是一个遗留脚本,因此我需要尽量减少更改并完全专注于业务需求,与XML文件的获取方式无关。

对我来说显而易见的解决方案是拦截对 subprocess.Popen 的调用并返回我自己的测试 XML。 Intercept method calls in Python演示了如何使用 setattr 来执行此操作,但我一定遗漏了一些东西:

Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> object.__getattribute__(subprocess, 'Popen')
<class 'subprocess.Popen'>
>>> attr = object.__getattribute__(subprocess, 'Popen')
>>> hasattr(attr, '__call__')
True
>>> def foo(): print('foo')
...
>>> foo
<function foo at 0x7f8e3ced3c08>
>>> foo()
foo
>>> setattr(subprocess, '__call__', foo)
>>> getattr(subprocess, '__call__')
<function foo at 0x7f8e3ced3c08>
>>> subprocess.Popen([ r"tail", "-n 1", "x.txt" ], stdout = subprocess.PIPE)
<subprocess.Popen object at 0x7f8e3ced9cd0>
>>> tail: cannot open `x.txt' for reading: No such file or directory

如您所见,真正的 subprocess.Popen 被调用,尽管属性设置正确(至少对于我大部分未受过训练的人来说)。这只是在交互式 Python 中运行它的结果,还是我应该期望将此类代码放入我的测试脚本中得到相同的结果:

class MockProcess:
def __init__(self, output):
self.output = output

def stderr(): pass
def stdout(): return self.output

def communicate():
return stdout, stderr


# Runs script, returning output
#
def run_agent():
real_popen = getattr(subprocess.Popen, '__call__')
try:
setattr(subprocess.Popen, '__call__', lambda *ignored: MockProcess('<foo bar="baz" />')
)
return real_popen(['myscript.py'], stdout = subprocess.PIPE).communicate()[0]
finally:
setattr(subprocess.Popen, '__call__', real_popen)

最佳答案

我的方法有几个问题:

我没有意识到 args 在 Python 中很神奇,也没有意识到我也需要 kwargs。

我正在替换 subprocess.Popen.__call__,而我应该替换 subprocess.Popen 本身。

最重要的是,替换 Popen 显然只会影响当前进程,而不是我的代码想要为脚本执行的新进程。新的 run_agent 方法应该如下所示:

def run_agent():
real_popen = getattr(subprocess, 'Popen')
try:
setattr(subprocess, 'Popen', lambda *args, **kwargs: MockProcess('<foo bar="baz" />')
imp.load_module(
MY_SCRIPT.replace('.py', '').replace('.', '_'),
file(SCRIPT_DIR),
MY_SCRIPT,
('.py', 'r', imp.PY_SOURCE)
)
finally:
setattr(subprocess.Popen, '__call__', real_popen)

我在互动环节中有错字。它应该是:

Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> setattr(subprocess, 'Popen', lambda *args, **kwargs: [1,2])
>>> subprocess.Popen([1], stdout=1)
[1, 2]

关于python - 在 Python 中拦截 subprocess.Popen 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5166851/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com