diff --git a/psutil/__init__.py b/psutil/__init__.py index 30f45987e..3a503503c 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -85,6 +85,7 @@ from ._common import NoSuchProcess from ._common import TimeoutExpired from ._common import ZombieProcess +from ._common import debug from ._common import memoize_when_activated from ._common import wrap_numbers as _wrap_numbers from ._compat import PY3 as _PY3 @@ -613,8 +614,7 @@ def is_running(self): # time) and that is verified in __eq__. self._pid_reused = self != Process(self.pid) if self._pid_reused: - # remove this PID from `process_iter()` internal cache - _pmap.pop(self.pid, None) + _pids_reused.add(self.pid) raise NoSuchProcess(self.pid) return True except ZombieProcess: @@ -1464,6 +1464,7 @@ def pid_exists(pid): _pmap = {} +_pids_reused = set() def process_iter(attrs=None, ad_value=None): @@ -1501,6 +1502,10 @@ def remove(pid): gone_pids = b - a for pid in gone_pids: remove(pid) + while _pids_reused: + pid = _pids_reused.pop() + debug("refreshing Process instance for reused PID %s" % pid) + remove(pid) try: ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items())) for pid, proc in ls: diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 233584c84..be264ae1d 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1120,10 +1120,10 @@ def ifconfig(nic): except RuntimeError: continue self.assertAlmostEqual( - stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 5 + stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 10 ) self.assertAlmostEqual( - stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 5 + stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 10 ) self.assertAlmostEqual( stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024 diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index 93204fa06..59416592e 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -219,6 +219,7 @@ def test__all__(self): dir_psutil = dir(psutil) for name in dir_psutil: if name in ( + 'debug', 'long', 'tests', 'test', diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 9ddaf9cfe..4f80277b7 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -36,6 +36,7 @@ from psutil._compat import PY3 from psutil._compat import FileNotFoundError from psutil._compat import long +from psutil._compat import redirect_stderr from psutil._compat import super from psutil.tests import APPVEYOR from psutil.tests import CI_TESTING @@ -763,7 +764,6 @@ def test_long_cmdline(self): if ret == []: # https://github.com/giampaolo/psutil/issues/2250 raise unittest.SkipTest("OPENBSD: returned EBUSY") - self.assertEqual(p.cmdline(), cmdline) self.assertEqual(p.cmdline(), cmdline) @@ -1378,6 +1378,11 @@ def test_zombie_process_status_w_exc(self): def test_reused_pid(self): # Emulate a case where PID has been reused by another process. + if PY3: + from io import StringIO + else: + from StringIO import StringIO + subp = self.spawn_testproc() p = psutil.Process(subp.pid) p._ident = (p.pid, p.create_time() + 100) @@ -1385,8 +1390,15 @@ def test_reused_pid(self): list(psutil.process_iter()) self.assertIn(p.pid, psutil._pmap) assert not p.is_running() + # make sure is_running() removed PID from process_iter() # internal cache + with redirect_stderr(StringIO()) as f: + list(psutil.process_iter()) + self.assertIn( + "refreshing Process instance for reused PID %s" % p.pid, + f.getvalue(), + ) self.assertNotIn(p.pid, psutil._pmap) assert p != psutil.Process(subp.pid)