diff options
Diffstat (limited to 'psutil')
-rw-r--r-- | psutil/__init__.py | 42 | ||||
-rw-r--r-- | psutil/_common.py | 3 | ||||
-rw-r--r-- | psutil/_pslinux.py | 38 | ||||
-rw-r--r-- | psutil/arch/windows/services.c | 6 | ||||
-rwxr-xr-x | psutil/tests/test_memory_leaks.py | 6 | ||||
-rwxr-xr-x | psutil/tests/test_misc.py | 16 | ||||
-rwxr-xr-x | psutil/tests/test_process.py | 2 | ||||
-rwxr-xr-x | psutil/tests/test_system.py | 16 |
8 files changed, 122 insertions, 7 deletions
diff --git a/psutil/__init__.py b/psutil/__init__.py index f630b67b..9054c615 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -187,7 +187,7 @@ __all__ = [ "net_io_counters", "net_connections", "net_if_addrs", # network "net_if_stats", "disk_io_counters", "disk_partitions", "disk_usage", # disk - # "sensors_battery", # sensors + # "sensors_temperatures", "sensors_battery", # sensors "users", "boot_time", # others ] __all__.extend(_psplatform.__extra__all__) @@ -2190,6 +2190,46 @@ def net_if_stats(): # ===================================================================== +# Linux +if hasattr(_psplatform, "sensors_temperatures"): + + def sensors_temperatures(fahrenheit=False): + """Return hardware temperatures. Each entry is a namedtuple + representing a certain hardware sensor (it may be a CPU, an + hard disk or something else, depending on the OS and its + configuration). + All temperatures are expressed in celsius unless *fahrenheit* + is set to True. + """ + def to_fahrenheit(n): + return (float(n) * 9 / 5) + 32 + + ret = collections.defaultdict(list) + rawdict = _psplatform.sensors_temperatures() + + for name, values in rawdict.items(): + while values: + label, current, high, critical = values.pop(0) + if fahrenheit: + current = to_fahrenheit(current) + if high is not None: + high = to_fahrenheit(high) + if critical is not None: + critical = to_fahrenheit(critical) + + if high and not critical: + critical = high + elif critical and not high: + high = critical + + ret[name].append( + _common.shwtemp(label, current, high, critical)) + + return dict(ret) + + __all__.append("sensors_temperatures") + + # Linux, Windows, FreeBSD if hasattr(_psplatform, "sensors_battery"): diff --git a/psutil/_common.py b/psutil/_common.py index 89dc1617..3b68b6d3 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -169,6 +169,9 @@ scpustats = namedtuple( 'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']) # psutil.cpu_freq() scpufreq = namedtuple('scpufreq', ['current', 'min', 'max']) +# psutil.sensors_temperatures() +shwtemp = namedtuple( + 'shwtemp', ['label', 'current', 'high', 'critical']) # psutil.sensors_battery() sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 4e834497..0d5f6ecb 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -7,6 +7,7 @@ from __future__ import division import base64 +import collections import errno import functools import glob @@ -65,6 +66,7 @@ POWER_SUPPLY_PATH = "/sys/class/power_supply" HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid()) HAS_PRLIMIT = hasattr(cext, "linux_prlimit") +_DEFAULT = object() # RLIMIT_* constants, not guaranteed to be present on all kernels if HAS_PRLIMIT: @@ -1065,6 +1067,42 @@ def disk_partitions(all=False): # ===================================================================== +if os.path.exists('/sys/class/hwmon'): + + def sensors_temperatures(): + """Return hardware (CPU and others) temperatures as a dict + including hardware name, label, current, max and critical + temperatures. + + Implementation notes: + - /sys/class/hwmon looks like the most recent interface to + retrieve this info, and this implementation relies on it + only (old distros will probably use something else) + - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon + - /sys/class/thermal/thermal_zone* is another one but it's more + difficult to parse + """ + ret = collections.defaultdict(list) + basenames = sorted(set( + [x.split('_')[0] for x in + glob.glob('/sys/class/hwmon/hwmon*/temp*_*')])) + for base in basenames: + unit_name = cat(os.path.join(os.path.dirname(base), 'name')) + label = cat(base + '_label', fallback='') + current = float(cat(base + '_input')) / 1000.0 + high = cat(base + '_max', fallback=None) + critical = cat(base + '_crit', fallback=None) + + if high is not None: + high = float(high) / 1000.0 + if critical is not None: + critical = float(critical) / 1000.0 + + ret[unit_name].append((label, current, high, critical)) + + return ret + + def sensors_battery(): root = os.path.join(POWER_SUPPLY_PATH, "BAT0") if not os.path.exists(root): diff --git a/psutil/arch/windows/services.c b/psutil/arch/windows/services.c index cb85afb5..26e58225 100644 --- a/psutil/arch/windows/services.c +++ b/psutil/arch/windows/services.c @@ -382,6 +382,12 @@ psutil_winservice_query_descr(PyObject *self, PyObject *args) { bytesNeeded = 0; QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &bytesNeeded); + if (GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { + // Also services.msc fails in the same manner, so we return an + // empty string. + CloseServiceHandle(hService); + return Py_BuildValue("s", ""); + } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { PyErr_SetFromWindowsErr(0); goto error; diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py index f025530f..5adb2b5e 100755 --- a/psutil/tests/test_memory_leaks.py +++ b/psutil/tests/test_memory_leaks.py @@ -575,6 +575,12 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_users(self): self.execute(psutil.users) + @unittest.skipUnless(hasattr(psutil, "sensors_temperatures"), + "platform not supported") + @skip_if_linux() + def test_sensors_temperatures(self): + self.execute(psutil.users) + if WINDOWS: # --- win services diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index fa777ef4..1d7d71ce 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -388,7 +388,7 @@ class TestScripts(unittest.TestCase): src = f.read() ast.parse(src) - def test_check_presence(self): + def test_coverage(self): # make sure all example scripts have a test method defined meths = dir(self) for name in os.listdir(SCRIPTS_DIR): @@ -469,11 +469,17 @@ class TestScripts(unittest.TestCase): def test_cpu_distribution(self): self.assert_syntax('cpu_distribution.py') - @unittest.skipUnless(hasattr(psutil, "sensors_battery") and - psutil.sensors_battery() is not None, - "no battery") + def test_temperatures(self): + if hasattr(psutil, "sensors_temperatures"): + self.assert_stdout('temperatures.py') + else: + self.assert_syntax('temperatures.py') + def test_battery(self): - self.assert_stdout('battery.py') + if hasattr(psutil, "sensors_battery"): + self.assert_stdout('battery.py') + else: + self.assert_syntax('battery.py') # =================================================================== diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index db86290b..74ae9330 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -1808,10 +1808,10 @@ class TestFetchAllProcesses(unittest.TestCase): try: st = os.stat(ret) except OSError as err: - # directory has been removed in mean time if WINDOWS and err.errno in \ psutil._psplatform.ACCESS_DENIED_SET: pass + # directory has been removed in mean time elif err.errno != errno.ENOENT: raise else: diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index 22c53e8b..01648380 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -29,6 +29,7 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._compat import long +from psutil._compat import unicode from psutil.tests import AF_INET6 from psutil.tests import APPVEYOR from psutil.tests import check_net_address @@ -755,6 +756,21 @@ class TestSystemAPIs(unittest.TestCase): for name in names: self.assertIs(getattr(psutil, name), False, msg=name) + @unittest.skipUnless(hasattr(psutil, "sensors_temperatures"), + "platform not suported") + def test_sensors_temperatures(self): + temps = psutil.sensors_temperatures() + for name, entries in temps.items(): + self.assertIsInstance(name, (str, unicode)) + for entry in entries: + self.assertIsInstance(entry.label, (str, unicode)) + if entry.current is not None: + self.assertGreaterEqual(entry.current, 0) + if entry.high is not None: + self.assertGreaterEqual(entry.high, 0) + if entry.critical is not None: + self.assertGreaterEqual(entry.critical, 0) + @unittest.skipUnless(LINUX or WINDOWS or FREEBSD, "platform not supported") def test_sensors_battery(self): |