diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2017-01-27 01:12:45 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2017-01-27 01:12:45 +0100 |
commit | 90b261aef4c94e364596f07711565ea250f3a10e (patch) | |
tree | 22c31d3666490ebce9c214312c660f0dc692cb60 | |
parent | 78f8a41eb902c144dd31a9e4bafe63c25fea3c76 (diff) | |
download | psutil-90b261aef4c94e364596f07711565ea250f3a10e.tar.gz |
#955: add power_plugged info
-rw-r--r-- | docs/index.rst | 16 | ||||
-rw-r--r-- | psutil/_common.py | 6 | ||||
-rw-r--r-- | psutil/_pslinux.py | 27 | ||||
-rw-r--r-- | psutil/_pswindows.py | 3 | ||||
-rwxr-xr-x | psutil/tests/test_linux.py | 57 | ||||
-rwxr-xr-x | psutil/tests/test_system.py | 9 |
6 files changed, 95 insertions, 23 deletions
diff --git a/docs/index.rst b/docs/index.rst index af44c516..f9ee7ee4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -627,10 +627,11 @@ Sensors - **percent**: battery power left as a percentage. - **secsleft**: (rough approximation) number of seconds left before the - battery run out of power; this may be set to - :data:`psutil.POWER_TIME_UNKNOWN <psutil.POWER_TIME_UNKNOWN>` - or :data:`psutil.POWER_TIME_UNLIMITED <psutil.POWER_TIME_UNLIMITED>` in - case the remaining time cannot be determined or is unlimited. + battery run out of power. If the AC power cable is connected this will be + set to :data:`psutil.POWER_TIME_UNLIMITED <psutil.POWER_TIME_UNLIMITED>`. + If it can't be determined it will be set to + :data:`psutil.POWER_TIME_UNKNOWN <psutil.POWER_TIME_UNKNOWN>`. + - **power_plugged**: ``True`` if the AC power cable is connected. If no battery is installed this function will return ``None``. Example:: @@ -641,10 +642,15 @@ Sensors ... >>> batt = psutil.sensors_battery() >>> batt - sbattery(percent=93, secsleft=16628) + sbattery(percent=93, secsleft=16628, power_plugged=False) >>> print("charge = %s%%, time left = %s" % (batt.percent, secs2hours(batt.secsleft))) charge = 93%, time left = 4:37:08 + .. warning:: + + This API is experimental. Backward incompatible changes may occur if + deemed necessary. + Availability: Linux, Windows .. versionadded:: 5.1.0 diff --git a/psutil/_common.py b/psutil/_common.py index b28af999..89dc1617 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -126,11 +126,11 @@ if enum is None: POWER_TIME_UNKNOWN = -1 POWER_TIME_UNLIMITED = -2 else: - class Power(enum.IntEnum): + class BatteryTime(enum.IntEnum): POWER_TIME_UNKNOWN = -1 POWER_TIME_UNLIMITED = -2 - globals().update(NicDuplex.__members__) + globals().update(BatteryTime.__members__) # =================================================================== @@ -170,7 +170,7 @@ scpustats = namedtuple( # psutil.cpu_freq() scpufreq = namedtuple('scpufreq', ['current', 'min', 'max']) # psutil.sensors_battery() -sbattery = namedtuple('sbattery', ['percent', 'secsleft']) +sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) # --- for Process methods diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 5be7f759..fec47be5 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1065,18 +1065,25 @@ def disk_partitions(all=False): def sensors_battery(): - root = "/sys/class/power_supply/BAT0/" - if not os.path.exists(root.rstrip('/')): + root = "/sys/class/power_supply/BAT0" + if not os.path.exists(root): return None - energy_now = int(cat(root + "energy_now")) - power_now = int(cat(root + "power_now")) - percent = int(cat(root + "capacity")) - try: - secsleft = int(energy_now / power_now * 3600) - except ZeroDivisionError: - secsleft = _common.POWER_TIME_UNKNOWN - return _common.sbattery(percent, secsleft) + power_plugged = \ + cat("/sys/class/power_supply/AC0/online", fallback=b"0") == b"1" + energy_now = int(cat(root + "/energy_now")) + power_now = int(cat(root + "/power_now")) + percent = int(cat(root + "/capacity")) + + if power_plugged: + secsleft = _common.POWER_TIME_UNLIMITED + else: + try: + secsleft = int(energy_now / power_now * 3600) + except ZeroDivisionError: + secsleft = _common.POWER_TIME_UNKNOWN + + return _common.sbattery(percent, secsleft, power_plugged) # ===================================================================== diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 80faec91..51e2efab 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -384,7 +384,8 @@ def sensors_battery(): elif secsleft == -1: secsleft = _common.POWER_TIME_UNKNOWN - return _common.sbattery(percent, secsleft) + # TODO: implement power_plugged + return _common.sbattery(percent, secsleft, False) # ===================================================================== diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 54d5e251..a00bdae6 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1024,12 +1024,67 @@ class TestMisc(unittest.TestCase): finally: t.stop() - def test_sensors_battery_percent(self): + +class TestSensorsBattery(unittest.TestCase): + + def test_percent(self): out = sh("acpi -b") acpi_value = int(out.split(",")[1].strip().replace('%', '')) psutil_value = psutil.sensors_battery().percent self.assertAlmostEqual(acpi_value, psutil_value, delta=1) + def test_power_plugged(self): + out = sh("acpi -b") + plugged = "Charging" in out.split('\n')[0] + self.assertEqual(psutil.sensors_battery().power_plugged, plugged) + + def test_emulate_power_plugged(self): + # Pretend the AC power cable is connected. + def open_mock(name, *args, **kwargs): + if name.startswith("/sys/class/power_supply/AC0/online"): + return io.BytesIO(b"1") + else: + return orig_open(name, *args, **kwargs) + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, side_effect=open_mock) as m: + self.assertEqual(psutil.sensors_battery().power_plugged, True) + self.assertEqual( + psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED) + assert m.called + + def test_emulate_power_not_plugged(self): + # Pretend the AC power cable is not connected. + def open_mock(name, *args, **kwargs): + if name.startswith("/sys/class/power_supply/AC0/online"): + return io.BytesIO(b"0") + else: + return orig_open(name, *args, **kwargs) + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, side_effect=open_mock) as m: + self.assertEqual(psutil.sensors_battery().power_plugged, False) + self.assertGreaterEqual(psutil.sensors_battery().secsleft, 0) + assert m.called + + def test_emulate_power_undetermined(self): + # Pretend we can't know whether the AC power cable not + # connected (assert fallback to False). + def open_mock(name, *args, **kwargs): + if name.startswith("/sys/class/power_supply/AC0/online"): + raise IOError(errno.ENOENT, "") + else: + return orig_open(name, *args, **kwargs) + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, side_effect=open_mock) as m: + self.assertEqual(psutil.sensors_battery().power_plugged, False) + self.assertGreaterEqual(psutil.sensors_battery().secsleft, 0) + assert m.called + # ===================================================================== # test process diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index cf33d140..36b1b11c 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -761,12 +761,15 @@ class TestSystemAPIs(unittest.TestCase): ret = psutil.sensors_battery() if ret is None: return # no battery - if ret.percent is not None: - self.assertGreaterEqual(ret.percent, 0) - self.assertLessEqual(ret.percent, 100) + self.assertGreaterEqual(ret.percent, 0) + self.assertLessEqual(ret.percent, 100) if ret.secsleft not in (psutil.POWER_TIME_UNKNOWN, psutil.POWER_TIME_UNLIMITED): self.assertGreaterEqual(ret.secsleft, 0) + else: + if ret.secsleft == psutil.POWER_TIME_UNLIMITED: + self.assertTrue(ret.power_plugged) + self.assertIsInstance(ret.power_plugged, bool) if __name__ == '__main__': |