Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

371 temperatures #962

Merged
merged 19 commits into from
Feb 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,21 @@ Network
{'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}

Sensors (Linux only)
====================

.. 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)]}

Other system info
=================

Expand Down
35 changes: 35 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,41 @@ Network
.. versionadded:: 3.0.0


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 `sensors.py <https://github.com/giampaolo/psutil/blob/master/scripts/sensors.py>`__
for an example application.

.. warning::

This API is experimental. Backwards incompatible changes may occur if
deemed necessary.

Availability: Linux

.. versionadded:: 5.1.0



Other system info
-----------------

Expand Down
45 changes: 45 additions & 0 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
"net_io_counters", "net_connections", "net_if_addrs", # network
"net_if_stats",
"disk_io_counters", "disk_partitions", "disk_usage", # disk
# "sensors_temperatures", # sensors
"users", "boot_time", # others
]
__all__.extend(_psplatform.__extra__all__)
Expand Down Expand Up @@ -2180,6 +2181,50 @@ def net_if_stats():
return _psplatform.net_if_stats()


# =====================================================================
# --- sensors
# =====================================================================


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")


# =====================================================================
# --- other system related functions
# =====================================================================
Expand Down
3 changes: 3 additions & 0 deletions psutil/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ class NicDuplex(enum.IntEnum):
'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'])

# --- for Process methods

Expand Down
43 changes: 43 additions & 0 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from __future__ import division

import base64
import collections
import errno
import functools
import glob
Expand Down Expand Up @@ -64,6 +65,7 @@

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:
Expand Down Expand Up @@ -1059,6 +1061,47 @@ def disk_partitions(all=False):
return retlist


# =====================================================================
# --- sensors
# =====================================================================


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


# =====================================================================
# --- other system functions
# =====================================================================
Expand Down
6 changes: 6 additions & 0 deletions psutil/arch/windows/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion psutil/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def assert_syntax(self, exe, args=None):
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):
Expand Down Expand Up @@ -469,6 +469,12 @@ def test_winservices(self):
def test_cpu_distribution(self):
self.assert_syntax('cpu_distribution.py')

def test_sensors(self):
if hasattr(psutil, "sensors_temperatures"):
self.assert_stdout('sensors.py')
else:
self.assert_syntax('sensors.py')


# ===================================================================
# --- Unit tests for test utilities.
Expand Down
2 changes: 1 addition & 1 deletion psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1808,10 +1808,10 @@ def cwd(self, ret, proc):
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:
Expand Down
16 changes: 16 additions & 0 deletions psutil/tests/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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
Expand Down Expand Up @@ -755,6 +756,21 @@ def test_os_constants(self):
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)


if __name__ == '__main__':
run_test_module_by_name(__file__)
46 changes: 46 additions & 0 deletions scripts/sensors.py
Original file line number Diff line number Diff line change
@@ -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()