From f240d984c8428dfad417ef516b837e1e7bb2769f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Tue, 26 Feb 2019 15:35:41 +0100 Subject: [PATCH] #1433, #1379: fix parents() method (infinite loop) --- psutil/__init__.py | 27 +++++++++++++++++++++++---- psutil/tests/test_posix.py | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/psutil/__init__.py b/psutil/__init__.py index 70efc5af0..2916764a4 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -45,6 +45,7 @@ from ._common import wrap_numbers as _wrap_numbers from ._compat import long from ._compat import PY3 as _PY3 +from ._compat import lru_cache from ._common import STATUS_DEAD from ._common import STATUS_DISK_SLEEP @@ -396,6 +397,11 @@ def _pprint_secs(secs): return datetime.datetime.fromtimestamp(secs).strftime(fmt) +@lru_cache() +def _first_pid(): + return sorted(pids())[0] + + # ===================================================================== # --- Process class # ===================================================================== @@ -660,11 +666,24 @@ def parents(self): """Return the parents of this process as a list of Process instances. If no parents are known return an empty list. """ + first_pid = _first_pid() parents = [] proc = self.parent() - while proc is not None: - parents.append(proc) - proc = proc.parent() + while True: + if proc is None: + break + elif proc.pid == first_pid: + # Needed because on certain systems such as macOS + # Process(0).ppid() returns 0. + parents.append(proc) + break + else: + par = proc.parent() + if par is None: + break + assert par.pid <= proc.pid, (par.pid, proc.pid) + parents.append(proc) + proc = par return parents def is_running(self): @@ -2475,7 +2494,7 @@ def test(): # pragma: no cover p.info['name'].strip() or '?')) -del memoize, memoize_when_activated, division, deprecated_method +del memoize, memoize_when_activated, division, deprecated_method, lru_cache if sys.version_info[0] < 3: del num, x diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py index 5a8fdc173..955a48348 100755 --- a/psutil/tests/test_posix.py +++ b/psutil/tests/test_posix.py @@ -288,7 +288,7 @@ def call(p, attr): failures = [] ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice', 'send_signal', 'wait', 'children', 'as_dict', - 'memory_info_ex'] + 'memory_info_ex', 'parent', 'parents'] if LINUX and get_kernel_version() < (2, 6, 36): ignored_names.append('rlimit') if LINUX and get_kernel_version() < (2, 6, 23):