From 0f367e9c615f285927f63b9e3cec548741e51ded Mon Sep 17 00:00:00 2001 From: amanusk Date: Thu, 27 Sep 2018 14:28:53 +0300 Subject: [PATCH 1/5] Add parsing for /sys/class/thermal In case no files are found in /sys/class/hwmon, check for temperature indicating sensors in /sys/class/thermal --- psutil/_pslinux.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index df624de32..e4202c892 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1229,6 +1229,49 @@ def sensors_temperatures(): ret[unit_name].append((label, current, high, critical)) + # Indication that no sensors were detected in /sys/class/hwmon/ + if len(basenames) == 0: + basenames = glob.glob('/sys/class/thermal/thermal_zone*') + basenames = sorted(set(basenames)) + + for base in basenames: + try: + path = os.path.join(base, 'temp') + current = float(cat(path)) / 1000.0 + path = os.path.join(base, 'type') + unit_name = cat(path, binary=False) + except (IOError, OSError, ValueError) as err: + warnings.warn("ignoring %r for file %r" % (err, path), + RuntimeWarning) + continue + + trip_paths = glob.glob(base + '/trip_point*') + trip_points = [os.path.basename(p) for p in trip_paths] + trip_points = sorted(set(["_".join(x.split('_')[0:3]) + for x in trip_points])) + critical = None + high = None + for trip_point in trip_points: + path = os.path.join(base, trip_point + "_type") + trip_type = cat(path, fallback='', binary=False) + if trip_type == 'critical': + critical = cat(os.path.join(base, trip_point + "_temp")) + elif trip_type == 'high': + high = cat(os.path.join(base, trip_point + "_temp")) + + if high is not None: + try: + high = float(high) / 1000.0 + except ValueError: + high = None + if critical is not None: + try: + critical = float(critical) / 1000.0 + except ValueError: + critical = None + + ret[unit_name].append(('', current, high, critical)) + return ret From fcbda4bf5749fb94517ceca3a1f61cc0cdb2f286 Mon Sep 17 00:00:00 2001 From: amanusk Date: Fri, 28 Sep 2018 14:08:58 +0300 Subject: [PATCH 2/5] Remove a loop when obtaining the base filenames of trippoints Add Fallback for cat when getting high/critical --- psutil/_pslinux.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index e4202c892..70ef4b3df 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1246,18 +1246,19 @@ def sensors_temperatures(): continue trip_paths = glob.glob(base + '/trip_point*') - trip_points = [os.path.basename(p) for p in trip_paths] - trip_points = sorted(set(["_".join(x.split('_')[0:3]) - for x in trip_points])) + trip_points = set(['_'.join( + os.path.basename(p).split('_')[0:3]) for p in trip_paths]) critical = None high = None for trip_point in trip_points: path = os.path.join(base, trip_point + "_type") trip_type = cat(path, fallback='', binary=False) if trip_type == 'critical': - critical = cat(os.path.join(base, trip_point + "_temp")) + critical = cat(os.path.join(base, trip_point + "_temp"), + Fallback=None) elif trip_type == 'high': - high = cat(os.path.join(base, trip_point + "_temp")) + high = cat(os.path.join(base, trip_point + "_temp"), + Fallback=None) if high is not None: try: From 59c5bc43dec08a5ee96c346919ed62afe4c36c69 Mon Sep 17 00:00:00 2001 From: amanusk Date: Fri, 28 Sep 2018 14:22:13 +0300 Subject: [PATCH 3/5] Change Fallback to fallback --- psutil/_pslinux.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 70ef4b3df..de5c96313 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1230,7 +1230,7 @@ def sensors_temperatures(): ret[unit_name].append((label, current, high, critical)) # Indication that no sensors were detected in /sys/class/hwmon/ - if len(basenames) == 0: + if not basenames: basenames = glob.glob('/sys/class/thermal/thermal_zone*') basenames = sorted(set(basenames)) @@ -1255,10 +1255,10 @@ def sensors_temperatures(): trip_type = cat(path, fallback='', binary=False) if trip_type == 'critical': critical = cat(os.path.join(base, trip_point + "_temp"), - Fallback=None) + fallback=None) elif trip_type == 'high': high = cat(os.path.join(base, trip_point + "_temp"), - Fallback=None) + fallback=None) if high is not None: try: From b83e5deafccf1db0ec5e80e01fbf2af3d9de1947 Mon Sep 17 00:00:00 2001 From: amanusk Date: Fri, 28 Sep 2018 22:27:45 +0300 Subject: [PATCH 4/5] Add test for sensors_temperature The tests check for correct return value when there is not /sys/class/hwmon directory, but /sys/class/thermal/ is present --- psutil/tests/test_linux.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 4e652a9d1..58bbb6923 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1466,6 +1466,8 @@ def test_emulate_eio_error(self): def open_mock(name, *args, **kwargs): if name.endswith("_input"): raise OSError(errno.EIO, "") + if name.endswith("temp"): + raise OSError(errno.EIO, "") else: return orig_open(name, *args, **kwargs) @@ -1489,12 +1491,33 @@ def open_mock(name, *args, **kwargs): return io.BytesIO(b"40000") elif name.endswith('/temp1_crit'): return io.BytesIO(b"50000") + # Files found in /sys/class/thermal/ + elif name.endswith('0_temp'): + return io.BytesIO(b"50000") + elif name.endswith('temp'): + return io.BytesIO(b"30000") + elif name.endswith('0_type'): + return io.StringIO(u("critical")) + elif name.endswith('type'): + return io.StringIO(u("name")) else: return orig_open(name, *args, **kwargs) + def glob_mock(path): + if path == '/sys/class/hwmon/hwmon*/temp*_*': + return [] + elif path == '/sys/class/hwmon/hwmon*/device/temp*_*': + return [] + elif path == '/sys/class/thermal/thermal_zone*': + return ['/sys/class/thermal/thermal_zone0'] + elif path == '/sys/class/thermal/thermal_zone0/trip_point*': + return ['/sys/class/thermal/thermal_zone1/trip_point_0_type', + '/sys/class/thermal/thermal_zone1/trip_point_0_temp'] + orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): + # Test case with /sys/class/hwmon with mock.patch('glob.glob', return_value=['/sys/class/hwmon/hwmon0/temp1']): temp = psutil.sensors_temperatures()['name'][0] @@ -1503,6 +1526,14 @@ def open_mock(name, *args, **kwargs): self.assertEqual(temp.high, 40.0) self.assertEqual(temp.critical, 50.0) + # Test case with only /sys/class/thermal + with mock.patch('glob.glob', create=True, side_effect=glob_mock): + temp = psutil.sensors_temperatures()['name'][0] + self.assertEqual(temp.label, '') + self.assertEqual(temp.current, 30.0) + self.assertEqual(temp.high, 50.0) + self.assertEqual(temp.critical, 50.0) + @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsFans(unittest.TestCase): From b004c11074e58057bad4d20c64cc95ea440043dd Mon Sep 17 00:00:00 2001 From: amanusk Date: Mon, 1 Oct 2018 13:37:25 +0300 Subject: [PATCH 5/5] Split tests for class/hwmon, class/thermal --- psutil/tests/test_linux.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 58bbb6923..cca3b7ee8 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1466,7 +1466,7 @@ def test_emulate_eio_error(self): def open_mock(name, *args, **kwargs): if name.endswith("_input"): raise OSError(errno.EIO, "") - if name.endswith("temp"): + elif name.endswith("temp"): raise OSError(errno.EIO, "") else: return orig_open(name, *args, **kwargs) @@ -1479,7 +1479,7 @@ def open_mock(name, *args, **kwargs): assert m.called self.assertIn("ignoring", str(ws[0].message)) - def test_emulate_data(self): + def test_emulate_class_hwmon(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): return io.StringIO(u("name")) @@ -1491,8 +1491,24 @@ def open_mock(name, *args, **kwargs): return io.BytesIO(b"40000") elif name.endswith('/temp1_crit'): return io.BytesIO(b"50000") - # Files found in /sys/class/thermal/ - elif name.endswith('0_temp'): + else: + return orig_open(name, *args, **kwargs) + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, side_effect=open_mock): + # Test case with /sys/class/hwmon + with mock.patch('glob.glob', + return_value=['/sys/class/hwmon/hwmon0/temp1']): + temp = psutil.sensors_temperatures()['name'][0] + self.assertEqual(temp.label, 'label') + self.assertEqual(temp.current, 30.0) + self.assertEqual(temp.high, 40.0) + self.assertEqual(temp.critical, 50.0) + + def test_emulate_class_thermal(self): + def open_mock(name, *args, **kwargs): + if name.endswith('0_temp'): return io.BytesIO(b"50000") elif name.endswith('temp'): return io.BytesIO(b"30000") @@ -1517,16 +1533,7 @@ def glob_mock(path): orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): - # Test case with /sys/class/hwmon - with mock.patch('glob.glob', - return_value=['/sys/class/hwmon/hwmon0/temp1']): - temp = psutil.sensors_temperatures()['name'][0] - self.assertEqual(temp.label, 'label') - self.assertEqual(temp.current, 30.0) - self.assertEqual(temp.high, 40.0) - self.assertEqual(temp.critical, 50.0) - # Test case with only /sys/class/thermal with mock.patch('glob.glob', create=True, side_effect=glob_mock): temp = psutil.sensors_temperatures()['name'][0] self.assertEqual(temp.label, '')