Commit 84df1e6c authored by Brett Cannon's avatar Brett Cannon

Merged revisions 81154 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r81154 | brett.cannon | 2010-05-13 17:21:48 -0700 (Thu, 13 May 2010) | 15 lines

  subprocess.Popen.__del__ referenced global objects, which is a no-no thanks to
  interpreter shutdown semantics. Same issue goes for the methods that __del__
  called. Now all the methods capture the global objects it needs as default
  values to private parameters (could have stuck them on the class object itself,
  but since the objects have nothing directly to do with the class that seemed
  wrong).

  There is no test as making one that works is hard. This patch was
  verified against a consistently failing test in Mercurial's test suite, though,
  so it has been tested in some regard.

  Closes issue #5099. Thanks to Mary Stern for the bug report and Gabriel
  Genellina for writing another patch for the same issue and attempting to write
  a test.
........
parent bbb2d492
...@@ -356,7 +356,6 @@ class CalledProcessError(Exception): ...@@ -356,7 +356,6 @@ class CalledProcessError(Exception):
if mswindows: if mswindows:
from _subprocess import CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP
import threading import threading
import msvcrt import msvcrt
import _subprocess import _subprocess
...@@ -394,6 +393,7 @@ __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", ...@@ -394,6 +393,7 @@ __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput",
"getoutput", "check_output", "CalledProcessError"] "getoutput", "check_output", "CalledProcessError"]
if mswindows: if mswindows:
from _subprocess import CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP
__all__.extend(["CREATE_NEW_CONSOLE", "CREATE_NEW_PROCESS_GROUP"]) __all__.extend(["CREATE_NEW_CONSOLE", "CREATE_NEW_PROCESS_GROUP"])
try: try:
MAXFD = os.sysconf("SC_OPEN_MAX") MAXFD = os.sysconf("SC_OPEN_MAX")
...@@ -698,12 +698,12 @@ class Popen(object): ...@@ -698,12 +698,12 @@ class Popen(object):
return data.decode(encoding) return data.decode(encoding)
def __del__(self, sys=sys): def __del__(self, _maxsize=sys.maxsize, _active=_active):
if not self._child_created: if not self._child_created:
# We didn't get to successfully create a child process. # We didn't get to successfully create a child process.
return return
# In case the child hasn't been waited on, check if it's done. # In case the child hasn't been waited on, check if it's done.
self._internal_poll(_deadstate=sys.maxsize) self._internal_poll(_deadstate=_maxsize)
if self.returncode is None and _active is not None: if self.returncode is None and _active is not None:
# Child is still running, keep us alive until we can wait on it. # Child is still running, keep us alive until we can wait on it.
_active.append(self) _active.append(self)
...@@ -907,13 +907,20 @@ class Popen(object): ...@@ -907,13 +907,20 @@ class Popen(object):
errwrite.Close() errwrite.Close()
def _internal_poll(self, _deadstate=None): def _internal_poll(self, _deadstate=None,
_WaitForSingleObject=WaitForSingleObject,
_WAIT_OBJECT_0=WAIT_OBJECT_0,
_GetExitCodeProcess=GetExitCodeProcess):
"""Check if child process has terminated. Returns returncode """Check if child process has terminated. Returns returncode
attribute.""" attribute.
This method is called by __del__, so it can only refer to objects
in its local scope.
"""
if self.returncode is None: if self.returncode is None:
if(_subprocess.WaitForSingleObject(self._handle, 0) == if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
_subprocess.WAIT_OBJECT_0): self.returncode = _GetExitCodeProcess(self._handle)
self.returncode = _subprocess.GetExitCodeProcess(self._handle)
return self.returncode return self.returncode
...@@ -1252,25 +1259,35 @@ class Popen(object): ...@@ -1252,25 +1259,35 @@ class Popen(object):
raise child_exception_type(err_msg) raise child_exception_type(err_msg)
def _handle_exitstatus(self, sts): def _handle_exitstatus(self, sts, _WIFSIGNALED=os.WIFSIGNALED,
if os.WIFSIGNALED(sts): _WTERMSIG=os.WTERMSIG, _WIFEXITED=os.WIFEXITED,
self.returncode = -os.WTERMSIG(sts) _WEXITSTATUS=os.WEXITSTATUS):
elif os.WIFEXITED(sts): # This method is called (indirectly) by __del__, so it cannot
self.returncode = os.WEXITSTATUS(sts) # refer to anything outside of its local scope."""
if _WIFSIGNALED(sts):
self.returncode = -_WTERMSIG(sts)
elif _WIFEXITED(sts):
self.returncode = _WEXITSTATUS(sts)
else: else:
# Should never happen # Should never happen
raise RuntimeError("Unknown child exit status!") raise RuntimeError("Unknown child exit status!")
def _internal_poll(self, _deadstate=None): def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid,
_WNOHANG=os.WNOHANG, _os_error=os.error):
"""Check if child process has terminated. Returns returncode """Check if child process has terminated. Returns returncode
attribute.""" attribute.
This method is called by __del__, so it cannot reference anything
outside of the local scope (nor can any methods it calls).
"""
if self.returncode is None: if self.returncode is None:
try: try:
pid, sts = os.waitpid(self.pid, os.WNOHANG) pid, sts = _waitpid(self.pid, _WNOHANG)
if pid == self.pid: if pid == self.pid:
self._handle_exitstatus(sts) self._handle_exitstatus(sts)
except os.error: except _os_error:
if _deadstate is not None: if _deadstate is not None:
self.returncode = _deadstate self.returncode = _deadstate
return self.returncode return self.returncode
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment