2014-05-15 58 views
12

Python的無效參數3.3.3 Windows 7的OSERROR:[錯誤22]在子

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) 

此代碼運行數百,一日2次沒有問題。但是,如果多個腳本在同一臺計算機上運行(相同的腳本,但有時來自不同的文件夾),則會出現此錯誤。腳本不執行相同的操作(即:當我收到此錯誤時,其他腳本不執行子進程)。

subProcess代碼引發了許多不同的命令行輸入給它的錯誤。

那麼,任何人都有一個想法,發生了什麼?解釋器是否有多個執行的問題(在不同的進程中)? 相同的代碼通常可以很好地工作,如果解釋器運行相同(或非常相似)的腳本,則會出現問題。但他們通常執行腳本的不同部分。

我很茫然:在8核心機器上使用單個處理器很煩人。

+0

什麼是論據? –

+0

你在運行什麼操作系統?它看起來像窗戶,但因爲這似乎是環境,你應該在你的問題中包括這一點。 – b4hand

+0

檢查沒有文件名衝突,即腳本不競爭相同的資源。你是否從同一個Python腳本啓動多個子進程?你使用'threading'模塊嗎?嘗試[創建最小完整代碼示例](http://stackoverflow.com/help/mcve),以解決您的問題 – jfs

回答

0

對於您正在使用的操作系統,命令(args)可能不正確。嘗試清理它們(檢查/只通過允許的字符)。

+1

它每天工作數百次,args不是真正的動態,它只是一個元組,因此封閉函數可以採用實際的參數。沒有理由消毒,我完全控制了腳本中的命令。 – Frigg

4

此前communicate只寫入進程stdin時忽略EPIPE錯誤。如果孩子已經離開(參見Lib/subprocess.py第1199行),則從3.3.5開始,根據issue 19612,它也忽略EINVAL(22)。

背景:

process.communiciate呼叫process.stdin.write,它調用io.FileIO.write,其上Windows調用C運行時_write,它調用的Win32 WriteFile(在這種情況下調用NtWriteFile,其分派給所述NamedPipe文件系統,如任一IRP_MJ_WRITEFastIoWrite)。

如果後者失敗,它會在該線程中設置Windows system error code。在這種情況下,基本的Windows錯誤可能是ERROR_NO_DATA(232),因爲子進程已經退出。 C運行時將其映射到errnoEINVAL(22)。然後由於_write失敗,FileIO.write根據當前值errno提高OSError


附錄:

就不會有過在所有如果CRT,而不是被映射到​​一個EPIPE問題。 Python自己的Windows錯誤轉換通常遵循CRT,但是根據issue 13063,它將​​映射到EPIPE(32)是個例外。因此,如果孩子已經退出,_winapi.WriteFile增加BrokenPipeError

下面的示例複製了EINVAL錯誤,因爲子進程已退出。它還顯示_winapi.WriteFile(3.3.3源鏈接)如何將此錯誤映射到EPIPE。國際海事組織,這應該被認爲是微軟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 
+0

+1 [EINVAL問題](http://bugs.python.org/issue19612)。問題13063似乎不相關 - 在這種情況下調用'winerror_to_errno()'?我沒有看到'process.stdin.write()'使用'WriteFile()'。 'FileIO.write()'調用'write(2)'(甚至在Windows上) - 是否通過Windows上的WriteFile實現了'write(2)'? – jfs

+0

@ J.F.Sebastian,在第一段中,我說'FileIO.write'基於'errno'引發,所以當然不會使用Python的Windows錯誤轉換。第二段和示例顯示了在這種情況下,子進程模塊希望爲C er運行時設置「errno」,以使'communic'能夠看到'EPIPE'錯誤,它已被設計爲忽略。相反,問題19612的解決方案爲'EINVAL'引入了特殊處理,該處理檢查子進程是否已退出。 – eryksun

+0

@ J.F.Sebastian,在這種情況下,C運行時'_write'不會調用'WriteFile',並將生成的'ERROR_NO_DATA'轉換爲'EINVAL'' errno'。如果文件描述符在控制檯句柄的Unicode模式下,'_write'將改爲調用'WriteConsoleW'。無論如何,Python 3的io模塊不能很好地處理'_O_U16TEXT'模式,因爲'FileIO'不能確保寫入'sizeof(wchar_t)'的倍數,這會導致CRT檢測錯誤。 – eryksun

8

@eryksun很好的分析了問題的核心,但我認爲更大的圖片(明顯的事情,可能)在他的回答中缺少。

因此,任何人都有一個想法,發生了什麼?

您的代碼患有競爭條件。有時你的孩子的過程不像你認爲的那樣行爲。

OSError: [Errno 22] Invalid argument在你的情況下,當孩子退出communicate試圖寫入命名管道,這subprocess有你的父母和你的孩子的過程的標準輸入之間建立提高。

Popen()在引擎蓋上做了很多。在你的情況下,它首先通過_winapi.CreatePipe()創建三個命名管道(在_get_handles()),每個stdin/out/err一個。然後,它使用_winapi.CreateProcess()衍生子進程(在_execute_child()中)。

_execute_child()完成清理程序。請記住:所有這些都發生在Popen()之內。

只有Popen()後的回報,你的Python VM父過程大約用output = process.communicate(input=SPACE_KEY, timeout=600)

調用進行假設你是一個多核系統上,你的系統有可讓孩子過程中的時間片做一些工作,而你的Python解釋器仍然在執行Popen()

也就是說,有窄時間窗_winapi.CreateProcess()之間(在此之後,子進程做了一些工作)和Python試圖通過communicate()(這使得對Windows的WriteFile打電話寫信給孩子的標準輸入,如eryksun很好地解釋)。

當你的孩子退出時間窗口你檢索命名錯誤。

爲什麼你的孩子的過程比預期的更早退出?只有你可以告訴。很顯然,它並不總是等待來自stdin的數據。

+1

Frigg在評論中提到,它正在啓動的程序如'reg.exe',實際上並不等待從標準輸入讀取。即使在調用'process.stdin.write('')'之前相對較長的延遲,我也不能重現錯誤。 – eryksun