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
代码如下所示:
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 )
此代码每天运行数百次而没有任何问题。但是如果在同一台机器上运行多个脚本(相同的脚本,但有时来自不同的文件夹),我会收到错误。这些脚本没有执行相同的操作(即:当我收到此错误时,其他脚本没有执行子进程)。
subProcess代码通过输入许多不同的命令行来引发错误。
那么,任何人都知道发生了什么?解释器是否有多个执行问题(在不同的进程中)? 相同的代码通常可以正常工作,如果解释器运行相同(或非常相似)的脚本,则会破解。但是他们通常会执行脚本的不同部分。
我不知所措:在8核机器上使用单个处理器很烦人。
答案 0 :(得分:8)
@eryksun非常好地分析了这个问题的核心,但我认为他的答案中缺少更大的图片(可能是显而易见的事情)。
那么,任何人都知道发生了什么?
您的代码遇到竞争条件。有时,您的子进程的行为与您认为的行为不同。
当OSError: [Errno 22] Invalid argument
尝试写入命名管道时,如果孩子退出,则会引发 communicate
,subprocess
已在您的父管理员之间建立了Popen()
你的孩子过程的标准。
_get_handles()
做了很多工作。在您的情况下,它首先通过_winapi.CreatePipe()
创建三个命名管道(在_execute_child()
中),每个stdin / out / err创建一个。然后,它使用_winapi.CreateProcess()
生成子进程(_execute_child()
)。
Popen()
完成清理程序。请记住:所有这些都发生在Popen()
。
仅在output = process.communicate(input=SPACE_KEY, timeout=600)
返回后,父进程中的Python VM即将继续调用Popen()
鉴于您使用的是多核系统,您的系统可以使用时间片让子进程执行某些工作,而您的Python解释器仍然执行_winapi.CreateProcess()
。< / p>
也就是说,communicate()
之间有一个狭窄的时间窗口(之后子进程做了一些工作)和Python尝试通过WriteFile
写入子进程的stdin (调用Windows {{{1}},正如eryksun很好地解释的那样)。
当您的孩子退出 时间窗口时,您将检索命名错误。
为什么您的子进程比预期更早退出?只有你可以告诉。显然,它总是等待来自stdin的数据。
答案 1 :(得分:5)
以前communicate
在写入流程EPIPE
时仅忽略了stdin
错误。从3.3.5开始,每issue 19612,如果孩子已经退出,它也会忽略EINVAL
(22)(参见Lib/subprocess.py第1199行)。
<强>背景强>
process.communiciate
调用process.stdin.write
,调用io.FileIO.write
,在Windows上调用C运行时_write
,调用Win32 WriteFile
(在这种情况下调用发送到NamedPipe文件系统的NtWriteFile
,可以是IRP_MJ_WRITE
或FastIoWrite
)。
如果后者失败,它会在线程中设置Windows system error code。在这种情况下,基础Windows错误可能是ERROR_NO_DATA
(232),因为子进程已经退出。 C运行时将其映射到errno
EINVAL
的值(22)。然后,由于_write
失败,FileIO.write
会根据OSError
的当前值提出errno
。
<强>附录:强>
如果CRT将ERROR_NO_DATA
映射到EPIPE
,则根本不会出现问题。 Python自己的Windows错误翻译通常遵循CRT,但是根据issue 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
答案 2 :(得分:0)
您正在使用的操作系统的命令(args)可能不正确。尝试对它们进行消毒(仅检查/传递允许的字符)。