Backport r81155.
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 36d7818..0656c84 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -675,12 +675,12 @@
return data.decode(encoding)
- def __del__(self, sys=sys):
+ def __del__(self, _maxsize=sys.maxsize, _active=_active):
if not self._child_created:
# We didn't get to successfully create a child process.
return
# 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:
# Child is still running, keep us alive until we can wait on it.
_active.append(self)
@@ -883,13 +883,20 @@
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
- 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(_subprocess.WaitForSingleObject(self._handle, 0) ==
- _subprocess.WAIT_OBJECT_0):
- self.returncode = _subprocess.GetExitCodeProcess(self._handle)
+ if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
+ self.returncode = _GetExitCodeProcess(self._handle)
return self.returncode
@@ -1150,25 +1157,35 @@
raise child_exception
- def _handle_exitstatus(self, sts):
- if os.WIFSIGNALED(sts):
- self.returncode = -os.WTERMSIG(sts)
- elif os.WIFEXITED(sts):
- self.returncode = os.WEXITSTATUS(sts)
+ def _handle_exitstatus(self, sts, _WIFSIGNALED=os.WIFSIGNALED,
+ _WTERMSIG=os.WTERMSIG, _WIFEXITED=os.WIFEXITED,
+ _WEXITSTATUS=os.WEXITSTATUS):
+ # This method is called (indirectly) by __del__, so it cannot
+ # refer to anything outside of its local scope."""
+ if _WIFSIGNALED(sts):
+ self.returncode = -_WTERMSIG(sts)
+ elif _WIFEXITED(sts):
+ self.returncode = _WEXITSTATUS(sts)
else:
# Should never happen
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
- 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:
try:
- pid, sts = os.waitpid(self.pid, os.WNOHANG)
+ pid, sts = _waitpid(self.pid, _WNOHANG)
if pid == self.pid:
self._handle_exitstatus(sts)
- except os.error:
+ except _os_error:
if _deadstate is not None:
self.returncode = _deadstate
return self.returncode
diff --git a/Misc/NEWS b/Misc/NEWS
index e42566f..68bbf31 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,9 @@
Library
-------
+- Issue #5099: subprocess.Popen.__del__ no longer references global objects
+ to prevent issues during interpreter shutdown.
+
- Issue #8681: Make the zlib module's error messages more informative when
the zlib itself doesn't give any detailed explanation.