diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2017-02-01 16:54:43 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2017-02-01 16:54:43 +0100 |
commit | 3fb929dbb61f1437c5876de723978f98006a6511 (patch) | |
tree | 3bbdc3d86e3ef16725c1228c10587a06559670da | |
parent | 0f7d22656de9a6c51da3a22d52ab876c36bf74ed (diff) | |
parent | ed0975dec40434b0e40bf8681325ffdaa8c5858d (diff) | |
download | psutil-3fb929dbb61f1437c5876de723978f98006a6511.tar.gz |
merge from master
-rw-r--r-- | HISTORY.rst | 1 | ||||
-rw-r--r-- | README.rst | 13 | ||||
-rw-r--r-- | docs/index.rst | 36 | ||||
-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 | ||||
-rwxr-xr-x | scripts/temperatures.py | 46 |
12 files changed, 211 insertions, 14 deletions
diff --git a/HISTORY.rst b/HISTORY.rst index fb7cddef..2aef89bd 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,6 +8,7 @@ **Enhancements** - 357_: added psutil.Process.cpu_num() (what CPU a process is on). +- 371_: added psutil.sensors_temperatures() (Linux only). - 941_: added psutil.cpu_freq() (CPU frequency). - 956_: cpu_affinity([]) can now be used as an alias to set affinity against all eligible CPUs. @@ -184,10 +184,21 @@ Network Sensors ======= +.. code-block:: python + + >>> import psutil + >>> psutil.sensors_temperatures() + {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)], + 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)], + 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0), + shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0), + shwtemp(label='Core 1', current=52.0, high=100.0, critical=100.0), + shwtemp(label='Core 2', current=45.0, high=100.0, critical=100.0), + shwtemp(label='Core 3', current=47.0, high=100.0, critical=100.0)]} + >>> >>> psutil.sensors_battery() sbattery(percent=93, secsleft=16628, power_plugged=False) - Other system info ================= diff --git a/docs/index.rst b/docs/index.rst index b82efb9b..ec35d769 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -620,6 +620,36 @@ Network Sensors ------- +.. function:: 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``. Example:: + + >>> import psutil + >>> psutil.sensors_temperatures() + {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)], + 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)], + 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0), + shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0), + shwtemp(label='Core 1', current=52.0, high=100.0, critical=100.0), + shwtemp(label='Core 2', current=45.0, high=100.0, critical=100.0), + shwtemp(label='Core 3', current=47.0, high=100.0, critical=100.0)]} + + See also `temperatures.py <https://github.com/giampaolo/psutil/blob/master/scripts/temperatures.py>`__ + for an example application. + + Availability: Linux + + .. versionadded:: 5.1.0 + + .. warning:: + + This API is experimental. Backward incompatible changes may occur if + deemed necessary. + .. function:: sensors_battery() Return battery status information as a namedtuple including the following @@ -650,12 +680,6 @@ Sensors charge = 93%, time left = 4:37:08 See also `battery.py <https://github.com/giampaolo/psutil/blob/master/scripts/battery.py>`__ - for an example application. - - .. warning:: - - This API is experimental. Backward incompatible changes may occur if - deemed necessary. Availability: Linux, Windows, FreeBSD 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): diff --git a/scripts/temperatures.py b/scripts/temperatures.py new file mode 100755 index 00000000..4b14180e --- /dev/null +++ b/scripts/temperatures.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +A clone of 'sensors' utility on Linux printing hardware temperatures. + +$ python scripts/sensors.py +asus + asus 47.0 °C (high = None °C, critical = None °C) + +acpitz + acpitz 47.0 °C (high = 103.0 °C, critical = 103.0 °C) + +coretemp + Physical id 0 54.0 °C (high = 100.0 °C, critical = 100.0 °C) + Core 0 47.0 °C (high = 100.0 °C, critical = 100.0 °C) + Core 1 48.0 °C (high = 100.0 °C, critical = 100.0 °C) + Core 2 47.0 °C (high = 100.0 °C, critical = 100.0 °C) + Core 3 54.0 °C (high = 100.0 °C, critical = 100.0 °C) +""" + +from __future__ import print_function +import sys + +import psutil + + +def main(): + if not hasattr(psutil, "sensors_temperatures"): + sys.exit("platform not supported") + temps = psutil.sensors_temperatures() + for name, entries in temps.items(): + print(name) + for entry in entries: + print(" %-20s %s °C (high = %s °C, critical = %s °C)" % ( + entry.label or name, entry.current, entry.high, + entry.critical)) + print() + + +if __name__ == '__main__': + main() |