Fri*_*igg 12 python windows subprocess python-3.x
Python 3.3.3 Windows 7
Here is the full stack:
Traceback (most recent call last):
File "Blah\MyScript.py", line 578, in Call
output = process.communicate( input=SPACE_KEY, timeout=600 )
File "C:\Python33\lib\subprocess.py", line 928, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "C:\Python33\lib\subprocess.py", line 1202, in _communicate
self.stdin.write(input)
OSError: [Errno 22] Invalid argument
Run Code Online (Sandbox Code Playgroud)
代码如下所示:
process = subprocess.Popen( arguments,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
env=environment )
output = process.communicate( input=SPACE_KEY, timeout=600 )
Run Code Online (Sandbox Code Playgroud)
这段代码每天运行数百次没有问题.但是如果在同一台机器上运行多个脚本(相同的脚本,但有时来自不同的文件夹),我会收到错误.脚本没有执行相同的操作(即:当我收到此错误时,另一个脚本没有执行子进程).
subProcess代码使用许多不同的命令行来引发错误.
那么,任何人都知道发生了什么?解释器是否有多个执行问题(在不同的进程中)?相同的代码通常可以正常工作,如果解释器运行相同(或非常相似)的脚本,则会破解.但它们通常执行脚本的不同部分.
我很茫然:在8核机器上使用单个处理器很烦人.
@eryksun非常好地分析了这个问题的核心,但我认为他的答案中缺少更大的图景(可能是显而易见的事情).
那么,任何人都知道发生了什么?
您的代码遇到竞争条件.有时,您的子进程的行为与您认为的行为不同.
OSError: [Errno 22] Invalid argument在您尝试写入命名管道之前 退出的情况会在您的情况下引发,communicate该管道subprocess已在您的子进程的父级和标准输入之间建立.
Popen()在引擎盖下做了很多.在您的情况下,它首先创建三个命名管道(in _get_handles()),via _winapi.CreatePipe(),每个stdin/out/err一个.然后,它使用生成子进程(in _execute_child())_winapi.CreateProcess().
_execute_child()完成清理程序.记住:所有这一切都发生在Popen().
只有在Popen()返回之后,父进程中的Python VM才会继续调用output = process.communicate(input=SPACE_KEY, timeout=600)
假设你是一个多核系统上,你的系统有可让孩子过程中做了一些工作,而你的Python解释器仍然是时间片内的执行Popen().
也就是说,有一个狭窄的时间窗口之间_winapi.CreateProcess()和Python试图通过写孩子的标准输入(后子进程做了一些工作)communicate()(这使得对Windows的一个电话WriteFile,因为eryksun很好地解释).
当您的孩子在该时间窗口退出时,您将检索命名错误.
为什么您的子进程比预期更早退出?只有你可以告诉.显然,它并不总是等待来自stdin的数据.
以前communicate只EPIPE在写入进程时忽略了错误stdin.从3.3.5开始,每期19612,EINVAL如果孩子已经退出,它也会忽略(22)(参见Lib/subprocess.py第1199行).
背景:
process.communiciate调用process.stdin.write,调用io.FileIO.write,在Windows上调用C运行库_write,调用Win32 WriteFile(在本例中调用NtWriteFile,调用,调用NamedPipe文件系统,作为IRP_MJ_WRITE或FastIoWrite).
如果后者失败,它会在线程中设置Windows 系统错误代码.在这种情况下,基础Windows错误可能是ERROR_NO_DATA(232),因为子进程已经退出.C运行时将其映射到errno值EINVAL(22).然后由于_write失败,根据当前值FileIO.write加注.OSErrorerrno
附录:
如果CRT改为映射ERROR_NO_DATA到,则根本不存在问题EPIPE.Python自己的Windows错误翻译通常遵循CRT,但是根据问题13063,它映射ERROR_NO_DATA到EPIPE(32)是一个例外.因此,如果孩子已经退出,就_winapi.WriteFile加注BrokenPipeError.
以下示例EINVAL在子进程已退出的情况下复制错误.它还显示了_winapi.WriteFile(3.3.3源链接)将如何映射此错误EPIPE.IMO,这应该被认为是微软CRT中的一个错误.
>>> cmd = 'reg query hkcu'
>>> process = Popen(cmd, stdin=PIPE, stdout=PIPE, universal_newlines=True)
>>> process.stdin.write(' ')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument
>>> hstdin = msvcrt.get_osfhandle(process.stdin.fileno())
>>> _winapi.WriteFile(hstdin, b' ')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
BrokenPipeError: [WinError 232] The pipe is being closed
Run Code Online (Sandbox Code Playgroud)