sysmon_pytk._common
Shared functions used throughout the application.
1# SPDX-FileCopyrightText: © 2024 Stacey Adams <stacey.belle.rose@gmail.com> 2# SPDX-License-Identifier: MIT 3 4""" 5Shared functions used throughout the application. 6""" 7 8from __future__ import annotations 9 10import platform 11import re 12import subprocess # nosec B404 13import time 14from socket import AF_INET 15 16import psutil 17 18DISK_ALERT_LEVEL = 80 19"""Percent of disk usage at which the meter shows red.""" 20 21DISK_WARN_LEVEL = 60 22"""Percent of disk usage at which the meter shows yellow.""" 23 24REFRESH_INTERVAL = 750 25"""Number of milliseconds between widget refreshes.""" 26 27INTERNAL_PAD = 12 28"""Standard widget padding, in pixels.""" 29 30BYTE_SYMBOLS = ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB") 31 32# SPDX-SnippetBegin 33# SPDX-SnippetName bytes2human function 34# http://code.google.com/p/pyftpdlib/source/browse/trunk/test/bench.py 35# SPDX-FileCopyrightText: © 2007-2013 Giampaolo Rodola' <g.rodola@gmail.com> 36# SPDX-License-Identifier: MIT 37 38 39def bytes2human(num: int, precision: int = 1) -> str: 40 """ 41 Convert a byte count to a human-readable format, with a given precision. 42 43 Parameters 44 ---------- 45 num : int 46 The number to format. 47 precision : int, optional 48 The number of decimals to use for display. 49 50 Returns 51 ------- 52 str 53 The number in human-readable format. 54 55 Examples 56 -------- 57 >>> bytes2human(10000) 58 '9.8KiB' 59 >>> bytes2human(100001221) 60 '95.4MiB' 61 """ 62 # originally taken from pyftpdlib, then rewritten 63 prefix = {} 64 for idx, symb in enumerate(BYTE_SYMBOLS[1:]): 65 prefix[symb] = 1 << (idx+1)*10 66 for symbol in reversed(BYTE_SYMBOLS[1:]): 67 if num >= prefix[symbol]: 68 value = float(num) / prefix[symbol] 69 return f"{value:.{precision}f}{symbol}" 70 return f"{num:.{precision}f}{BYTE_SYMBOLS[0]}" 71 72# SPDX-SnippetEnd 73 74 75def bytes2whole(num: int) -> str: 76 """ 77 Convert a byte count to a human-readable format, with no decimal. 78 79 Parameters 80 ---------- 81 num : int 82 The number to format. 83 84 Returns 85 ------- 86 str 87 The number in human-readable format. 88 89 Examples 90 -------- 91 >>> bytes2whole(10000) 92 '10KiB' 93 >>> bytes2whole(100001221) 94 '95MiB' 95 """ 96 return bytes2human(num, precision=0) 97 98 99def digits(numstr: str) -> list[int]: 100 """ 101 Return a list of numbers in a string. 102 103 Parameters 104 ---------- 105 numstr : str 106 A string containing various numbers. 107 108 Returns 109 ------- 110 List[int] 111 A list of integers extracted from the string. 112 113 Examples 114 -------- 115 >>> digits("") 116 [] 117 >>> digits("8.5MiB") 118 [8, 5] 119 >> digits("13. Notes vol. 22, pp. 585-588, 1996.") 120 [13, 22, 585, 588, 1996] 121 """ 122 return [int(s) for s in re.findall(r"\d+", numstr)] 123 124 125def cpu_temp() -> float: 126 """ 127 Return the CPU temperature in degrees Celsius. 128 129 Returns 130 ------- 131 float 132 The current CPU temperature. 133 134 Examples 135 -------- 136 >>> cpu_temp() 137 41.0 138 """ 139 temps = psutil.sensors_temperatures() 140 key = "coretemp" if "coretemp" in temps else next(iter(temps)) 141 return temps[key][0].current 142 143 144def net_addr() -> str: 145 """ 146 Get the first non-loopback network address (IPv4). 147 148 Returns 149 ------- 150 str 151 The discovered network address. 152 """ 153 addresses = psutil.net_if_addrs() 154 for nic, addr_list in addresses.items(): 155 if nic != "lo": 156 addr = [addr.address for addr in addr_list if addr.family == AF_INET] 157 return addr[0] if len(addr) > 0 else "" 158 return "" 159 160 161def system_uptime() -> str: 162 """ 163 Return the system uptime in a human-readable format. 164 165 Returns 166 ------- 167 str 168 The current system uptime in a human-readable format. 169 170 Examples 171 -------- 172 >>> system_uptime() # for a system just rebooted 173 "47 sec" 174 >>> system_uptime() # five minutes later 175 "5m 47s" 176 >>> system_uptime() # 2 hours later 177 "2h 5m 47s" 178 >>> system_uptime() # 8 days later 179 "8d 2h 5m" 180 """ 181 uptime = time.time() - psutil.boot_time() 182 uptime_minutes, uptime_seconds = divmod(uptime, 60) 183 uptime_hours, uptime_minutes = divmod(uptime_minutes, 60) 184 uptime_days, uptime_hours = divmod(uptime_hours, 24) 185 if uptime_days: 186 return f"{uptime_days:.0f}d {uptime_hours:.0f}h {uptime_minutes:.0f}m" 187 if uptime_hours: 188 return f"{uptime_hours:.0f}h {uptime_minutes:.0f}m {uptime_seconds:.0f}s" 189 if uptime_minutes: 190 return f"{uptime_minutes:.0f}m {uptime_seconds:.0f}s" 191 return f"{uptime_seconds:.0f} sec" 192 193 194def disk_usage(mountpoint: str) -> str: 195 """ 196 Return the disk usage of the provided mount point in a human-readable format. 197 198 Parameters 199 ---------- 200 mountpoint: str 201 a filesystem path belonging to the desired mount point. 202 203 Returns 204 ------- 205 str 206 A human-readable string of the disk usage. 207 208 Example 209 ------- 210 >>> disk_usage("/") 211 "32GiB/199GiB 17%" 212 """ 213 diskinfo = psutil.disk_usage(mountpoint) 214 used_fmt = bytes2whole(diskinfo.used) 215 total_fmt = bytes2whole(diskinfo.total) 216 # if formatted numbers are less then 10, use single decimal place rather than zero decimals 217 if digits(used_fmt)[0] < 10: # noqa: PLR2004 218 used_fmt = bytes2human(diskinfo.used) 219 if digits(total_fmt)[0] < 10: # noqa: PLR2004 220 total_fmt = bytes2human(diskinfo.total) 221 return f"{used_fmt}/{total_fmt} {round(diskinfo.percent)}%" 222 223 224def get_processor_name() -> str: 225 """ 226 Get the full processor name of the computer. 227 """ 228 if platform.system() == "Windows": 229 return _get_processor_name_windows() 230 if platform.system() == "Darwin": 231 return _get_processor_name_darwin() 232 if platform.system() == "Linux": 233 return _get_processor_name_linux() 234 return "" 235 236 237def _get_processor_name_windows() -> str: 238 return platform.processor() 239 240 241def _get_processor_name_darwin() -> str: 242 return subprocess.check_output( # nosec B603 243 ["/usr/sbin/sysctl", "-n", "machdep.cpu.brand_string"] 244 ).decode("utf-8").strip() 245 246 247def _get_processor_name_linux() -> str: 248 all_info = subprocess.check_output( # nosec B603 249 ["/usr/bin/cat", "/proc/cpuinfo"] 250 ).decode().strip() 251 for line in all_info.split("\n"): 252 if "model name" in line: 253 return re.sub(r".*model name.*:", "", line, count=1) 254 return ""
DISK_ALERT_LEVEL =
80
Percent of disk usage at which the meter shows red.
DISK_WARN_LEVEL =
60
Percent of disk usage at which the meter shows yellow.
REFRESH_INTERVAL =
750
Number of milliseconds between widget refreshes.
INTERNAL_PAD =
12
Standard widget padding, in pixels.
def
bytes2human(num: int, precision: int = 1) -> str:
40def bytes2human(num: int, precision: int = 1) -> str: 41 """ 42 Convert a byte count to a human-readable format, with a given precision. 43 44 Parameters 45 ---------- 46 num : int 47 The number to format. 48 precision : int, optional 49 The number of decimals to use for display. 50 51 Returns 52 ------- 53 str 54 The number in human-readable format. 55 56 Examples 57 -------- 58 >>> bytes2human(10000) 59 '9.8KiB' 60 >>> bytes2human(100001221) 61 '95.4MiB' 62 """ 63 # originally taken from pyftpdlib, then rewritten 64 prefix = {} 65 for idx, symb in enumerate(BYTE_SYMBOLS[1:]): 66 prefix[symb] = 1 << (idx+1)*10 67 for symbol in reversed(BYTE_SYMBOLS[1:]): 68 if num >= prefix[symbol]: 69 value = float(num) / prefix[symbol] 70 return f"{value:.{precision}f}{symbol}" 71 return f"{num:.{precision}f}{BYTE_SYMBOLS[0]}"
Convert a byte count to a human-readable format, with a given precision.
Parameters
- num (int): The number to format.
- precision (int, optional): The number of decimals to use for display.
Returns
- str: The number in human-readable format.
Examples
>>> bytes2human(10000)
'9.8KiB'
>>> bytes2human(100001221)
'95.4MiB'
def
bytes2whole(num: int) -> str:
76def bytes2whole(num: int) -> str: 77 """ 78 Convert a byte count to a human-readable format, with no decimal. 79 80 Parameters 81 ---------- 82 num : int 83 The number to format. 84 85 Returns 86 ------- 87 str 88 The number in human-readable format. 89 90 Examples 91 -------- 92 >>> bytes2whole(10000) 93 '10KiB' 94 >>> bytes2whole(100001221) 95 '95MiB' 96 """ 97 return bytes2human(num, precision=0)
Convert a byte count to a human-readable format, with no decimal.
Parameters
- num (int): The number to format.
Returns
- str: The number in human-readable format.
Examples
>>> bytes2whole(10000)
'10KiB'
>>> bytes2whole(100001221)
'95MiB'
def
digits(numstr: str) -> list[int]:
100def digits(numstr: str) -> list[int]: 101 """ 102 Return a list of numbers in a string. 103 104 Parameters 105 ---------- 106 numstr : str 107 A string containing various numbers. 108 109 Returns 110 ------- 111 List[int] 112 A list of integers extracted from the string. 113 114 Examples 115 -------- 116 >>> digits("") 117 [] 118 >>> digits("8.5MiB") 119 [8, 5] 120 >> digits("13. Notes vol. 22, pp. 585-588, 1996.") 121 [13, 22, 585, 588, 1996] 122 """ 123 return [int(s) for s in re.findall(r"\d+", numstr)]
Return a list of numbers in a string.
Parameters
- numstr (str): A string containing various numbers.
Returns
- List[int]: A list of integers extracted from the string.
Examples
>>> digits("")
[]
>>> digits("8.5MiB")
[8, 5]
>> digits("13. Notes vol. 22, pp. 585-588, 1996.")
[13, 22, 585, 588, 1996]
def
cpu_temp() -> float:
126def cpu_temp() -> float: 127 """ 128 Return the CPU temperature in degrees Celsius. 129 130 Returns 131 ------- 132 float 133 The current CPU temperature. 134 135 Examples 136 -------- 137 >>> cpu_temp() 138 41.0 139 """ 140 temps = psutil.sensors_temperatures() 141 key = "coretemp" if "coretemp" in temps else next(iter(temps)) 142 return temps[key][0].current
Return the CPU temperature in degrees Celsius.
Returns
- float: The current CPU temperature.
Examples
>>> cpu_temp()
41.0
def
net_addr() -> str:
145def net_addr() -> str: 146 """ 147 Get the first non-loopback network address (IPv4). 148 149 Returns 150 ------- 151 str 152 The discovered network address. 153 """ 154 addresses = psutil.net_if_addrs() 155 for nic, addr_list in addresses.items(): 156 if nic != "lo": 157 addr = [addr.address for addr in addr_list if addr.family == AF_INET] 158 return addr[0] if len(addr) > 0 else "" 159 return ""
Get the first non-loopback network address (IPv4).
Returns
- str: The discovered network address.
def
system_uptime() -> str:
162def system_uptime() -> str: 163 """ 164 Return the system uptime in a human-readable format. 165 166 Returns 167 ------- 168 str 169 The current system uptime in a human-readable format. 170 171 Examples 172 -------- 173 >>> system_uptime() # for a system just rebooted 174 "47 sec" 175 >>> system_uptime() # five minutes later 176 "5m 47s" 177 >>> system_uptime() # 2 hours later 178 "2h 5m 47s" 179 >>> system_uptime() # 8 days later 180 "8d 2h 5m" 181 """ 182 uptime = time.time() - psutil.boot_time() 183 uptime_minutes, uptime_seconds = divmod(uptime, 60) 184 uptime_hours, uptime_minutes = divmod(uptime_minutes, 60) 185 uptime_days, uptime_hours = divmod(uptime_hours, 24) 186 if uptime_days: 187 return f"{uptime_days:.0f}d {uptime_hours:.0f}h {uptime_minutes:.0f}m" 188 if uptime_hours: 189 return f"{uptime_hours:.0f}h {uptime_minutes:.0f}m {uptime_seconds:.0f}s" 190 if uptime_minutes: 191 return f"{uptime_minutes:.0f}m {uptime_seconds:.0f}s" 192 return f"{uptime_seconds:.0f} sec"
Return the system uptime in a human-readable format.
Returns
- str: The current system uptime in a human-readable format.
Examples
>>> system_uptime() # for a system just rebooted
"47 sec"
>>> system_uptime() # five minutes later
"5m 47s"
>>> system_uptime() # 2 hours later
"2h 5m 47s"
>>> system_uptime() # 8 days later
"8d 2h 5m"
def
disk_usage(mountpoint: str) -> str:
195def disk_usage(mountpoint: str) -> str: 196 """ 197 Return the disk usage of the provided mount point in a human-readable format. 198 199 Parameters 200 ---------- 201 mountpoint: str 202 a filesystem path belonging to the desired mount point. 203 204 Returns 205 ------- 206 str 207 A human-readable string of the disk usage. 208 209 Example 210 ------- 211 >>> disk_usage("/") 212 "32GiB/199GiB 17%" 213 """ 214 diskinfo = psutil.disk_usage(mountpoint) 215 used_fmt = bytes2whole(diskinfo.used) 216 total_fmt = bytes2whole(diskinfo.total) 217 # if formatted numbers are less then 10, use single decimal place rather than zero decimals 218 if digits(used_fmt)[0] < 10: # noqa: PLR2004 219 used_fmt = bytes2human(diskinfo.used) 220 if digits(total_fmt)[0] < 10: # noqa: PLR2004 221 total_fmt = bytes2human(diskinfo.total) 222 return f"{used_fmt}/{total_fmt} {round(diskinfo.percent)}%"
Return the disk usage of the provided mount point in a human-readable format.
Parameters
- mountpoint (str): a filesystem path belonging to the desired mount point.
Returns
- str: A human-readable string of the disk usage.
Example
>>> disk_usage("/")
"32GiB/199GiB 17%"
def
get_processor_name() -> str:
225def get_processor_name() -> str: 226 """ 227 Get the full processor name of the computer. 228 """ 229 if platform.system() == "Windows": 230 return _get_processor_name_windows() 231 if platform.system() == "Darwin": 232 return _get_processor_name_darwin() 233 if platform.system() == "Linux": 234 return _get_processor_name_linux() 235 return ""
Get the full processor name of the computer.