From e5a081128e85d229af6499bb39b708ae15720358 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 19 May 2017 20:03:29 +0200 Subject: [PATCH] Fix #1055, fix #1085, fix #1087. - no longer cache cpu_count() return value in Process.cpu_percent() - in Process.cpu_percent(), guard against cpu_count() returning None and assume 1 instead - add test cases --- HISTORY.rst | 4 +++- psutil/__init__.py | 7 +------ psutil/tests/test_process.py | 6 ++++++ psutil/tests/test_system.py | 12 ++++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index a85aacb56..d540ae69e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -50,7 +50,9 @@ - 1047_: [Windows] Process username(): memory leak in case exception is thrown. - 1048_: [Windows] users()'s host field report an invalid IP address. - 1050_: [Windows] Process.memory_maps memory() leaks memory. -- 1055_: cpu_count() is no longer cached. +- 1055_: cpu_count() is no longer cached; this is useful on systems such as + Linux where CPUs can be disabled at runtime. This also reflects on + Process.cpu_percent() which no longer uses the cache. - 1058_: fixed Python warnings. - 1062_: disk_io_counters() and net_io_counters() raise TypeError if no disks or NICs are installed on the system. diff --git a/psutil/__init__.py b/psutil/__init__.py index c393ecc3a..fc45abf16 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -209,7 +209,6 @@ POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED POWER_TIME_UNKNOWN = _common.POWER_TIME_UNKNOWN _TOTAL_PHYMEM = None -_NUM_CPUS = None _timer = getattr(time, 'monotonic', time.time) @@ -1043,9 +1042,7 @@ def cpu_percent(self, interval=None): blocking = interval is not None and interval > 0.0 if interval is not None and interval < 0: raise ValueError("interval is not positive (got %r)" % interval) - # TODO: rarely cpu_count() may return None, meaning this will - # break. It's probably wise to fall back to 1. - num_cpus = _NUM_CPUS or cpu_count() + num_cpus = cpu_count() or 1 def timer(): return _timer() * num_cpus @@ -1645,10 +1642,8 @@ def cpu_count(logical=True): >>> psutil.cpu_count.cache_clear() """ - global _NUM_CPUS if logical: ret = _psplatform.cpu_count_logical() - _NUM_CPUS = ret else: ret = _psplatform.cpu_count_physical() return ret if ret >= 1 else None diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 3410ec0b3..cab5a2fee 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -246,6 +246,12 @@ def test_cpu_percent(self): with self.assertRaises(ValueError): p.cpu_percent(interval=-1) + def test_cpu_percent_numcpus_none(self): + # See: https://github.com/giampaolo/psutil/issues/1087 + with mock.patch('psutil.cpu_count', return_value=None) as m: + psutil.Process().cpu_percent() + assert m.called + def test_cpu_times(self): times = psutil.Process().cpu_times() assert (times.user > 0.0) or (times.system > 0.0), times diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index fed7a222a..e93bb6b52 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -270,6 +270,18 @@ def test_cpu_count(self): self.assertGreaterEqual(physical, 1) self.assertGreaterEqual(logical, physical) + def test_cpu_count_none(self): + # https://github.com/giampaolo/psutil/issues/1085 + for val in (-1, 0, None): + with mock.patch('psutil._psplatform.cpu_count_logical', + return_value=val) as m: + self.assertIsNone(psutil.cpu_count()) + assert m.called + with mock.patch('psutil._psplatform.cpu_count_physical', + return_value=val) as m: + self.assertIsNone(psutil.cpu_count(logical=False)) + assert m.called + def test_cpu_times(self): # Check type, value >= 0, str(). total = 0