summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2017-02-01 16:54:43 +0100
committerGiampaolo Rodola <g.rodola@gmail.com>2017-02-01 16:54:43 +0100
commit3fb929dbb61f1437c5876de723978f98006a6511 (patch)
tree3bbdc3d86e3ef16725c1228c10587a06559670da
parent0f7d22656de9a6c51da3a22d52ab876c36bf74ed (diff)
parented0975dec40434b0e40bf8681325ffdaa8c5858d (diff)
downloadpsutil-3fb929dbb61f1437c5876de723978f98006a6511.tar.gz
merge from master
-rw-r--r--HISTORY.rst1
-rw-r--r--README.rst13
-rw-r--r--docs/index.rst36
-rw-r--r--psutil/__init__.py42
-rw-r--r--psutil/_common.py3
-rw-r--r--psutil/_pslinux.py38
-rw-r--r--psutil/arch/windows/services.c6
-rwxr-xr-xpsutil/tests/test_memory_leaks.py6
-rwxr-xr-xpsutil/tests/test_misc.py16
-rwxr-xr-xpsutil/tests/test_process.py2
-rwxr-xr-xpsutil/tests/test_system.py16
-rwxr-xr-xscripts/temperatures.py46
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.
diff --git a/README.rst b/README.rst
index 25203843..ddcdbbf6 100644
--- a/README.rst
+++ b/README.rst
@@ -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()