Una libreria interna che utilizza in gran parte il sottoprocesso.Popen() ha iniziato a non eseguire i test automatici quando è stato aggiornato da Python 2.7.3 a Python 2.7.5. Questa libreria è utilizzata in un ambiente con thread. Dopo aver eseguito il debug del problema, sono riuscito a creare un breve script Python che dimostra l'errore visto nei test non riusciti.Popen del sottoprocesso chiude gli script di stderr/stderr utilizzati in un'altra discussione quando si verificano errori di popen
Questo è lo script (chiamato "threadedsubprocess.py"):
import time
import threading
import subprocess
def subprocesscall():
p = subprocess.Popen(
['ls', '-l'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
time.sleep(2) # simulate the Popen call takes some time to complete.
out, err = p.communicate()
print 'succeeding command in thread:', threading.current_thread().ident
def failingsubprocesscall():
try:
p = subprocess.Popen(
['thiscommandsurelydoesnotexist'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except Exception as e:
print 'failing command:', e, 'in thread:', threading.current_thread().ident
print 'main thread is:', threading.current_thread().ident
subprocesscall_thread = threading.Thread(target=subprocesscall)
subprocesscall_thread.start()
failingsubprocesscall()
subprocesscall_thread.join()
Nota: questo script non esce con un IOError quando correva da Python 2.7.3. Fallisce almeno il 50% delle volte quando viene eseguito da Python 2.7.5 (entrambi sulla stessa VM Ubuntu 12.04 64 bit).
L'errore che viene generato su Python 2.7.5 è questo:
/opt/python/2.7.5/bin/python ./threadedsubprocess.py
main thread is: 139899583563520
failing command: [Errno 2] No such file or directory 139899583563520
Exception in thread Thread-1:
Traceback (most recent call last):
File "/opt/python/2.7.5/lib/python2.7/threading.py", line 808, in __bootstrap_inner
self.run()
File "/opt/python/2.7.5/lib/python2.7/threading.py", line 761, in run
self.__target(*self.__args, **self.__kwargs)
File "./threadedsubprocess.py", line 13, in subprocesscall
out, err = p.communicate()
File "/opt/python/2.7.5/lib/python2.7/subprocess.py", line 806, in communicate
return self._communicate(input)
File "/opt/python/2.7.5/lib/python2.7/subprocess.py", line 1379, in _communicate
self.stdin.close()
IOError: [Errno 9] Bad file descriptor
close failed in file object destructor:
IOError: [Errno 9] Bad file descriptor
Se si confronta il modulo sottoprocesso da Python 2.7.3 a Python 2.7.5 vedo l'Popen() 's __init __() chiama infatti ora chiude esplicitamente i descrittori di file stdin, stdout e stderr nel caso in cui l'esecuzione del comando non riesca in qualche modo. Questa sembra essere una correzione intenzionale applicata in Python 2.7.4 per prevenire la perdita dei descrittori di file (http://hg.python.org/cpython/file/ab05e7dd2788/Misc/NEWS#l629).
Il diff tra Python 2.7.3 e Python 2.7.5 che sembra essere rilevante per questo problema è nel Popen __init __():
@@ -671,12 +702,33 @@
c2pread, c2pwrite,
errread, errwrite) = self._get_handles(stdin, stdout, stderr)
- self._execute_child(args, executable, preexec_fn, close_fds,
- cwd, env, universal_newlines,
- startupinfo, creationflags, shell,
- p2cread, p2cwrite,
- c2pread, c2pwrite,
- errread, errwrite)
+ try:
+ self._execute_child(args, executable, preexec_fn, close_fds,
+ cwd, env, universal_newlines,
+ startupinfo, creationflags, shell,
+ p2cread, p2cwrite,
+ c2pread, c2pwrite,
+ errread, errwrite)
+ except Exception:
+ # Preserve original exception in case os.close raises.
+ exc_type, exc_value, exc_trace = sys.exc_info()
+
+ to_close = []
+ # Only close the pipes we created.
+ if stdin == PIPE:
+ to_close.extend((p2cread, p2cwrite))
+ if stdout == PIPE:
+ to_close.extend((c2pread, c2pwrite))
+ if stderr == PIPE:
+ to_close.extend((errread, errwrite))
+
+ for fd in to_close:
+ try:
+ os.close(fd)
+ except EnvironmentError:
+ pass
+
+ raise exc_type, exc_value, exc_trace
penso di avere tre domande:
1) È vero che dovrebbe essere possibile utilizzare principalmente il sottoprocesso.Popen, con PIPE per stdin, stdout e stderr, in un ambiente con thread?
2) Come impedire che i descrittori di file per stdin, stdout e stderr vengano chiusi quando il Popen() non riesce in uno dei thread?
3) Sto facendo qualcosa di sbagliato qui?
cosa interessante è che questo è stato risolto in Python 3.4+ – Cukic0d