summaryrefslogtreecommitdiff
path: root/psutil
diff options
context:
space:
mode:
Diffstat (limited to 'psutil')
-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
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):