Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/psutil/_common.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """Common objects shared by __init__.py and _ps*.py modules.""" | |
| 6 | |
| 7 # Note: this module is imported by setup.py so it should not import | |
| 8 # psutil or third-party modules. | |
| 9 | |
| 10 from __future__ import division, print_function | |
| 11 | |
| 12 import contextlib | |
| 13 import errno | |
| 14 import functools | |
| 15 import os | |
| 16 import socket | |
| 17 import stat | |
| 18 import sys | |
| 19 import threading | |
| 20 import warnings | |
| 21 from collections import defaultdict | |
| 22 from collections import namedtuple | |
| 23 from socket import AF_INET | |
| 24 from socket import SOCK_DGRAM | |
| 25 from socket import SOCK_STREAM | |
| 26 | |
| 27 try: | |
| 28 from socket import AF_INET6 | |
| 29 except ImportError: | |
| 30 AF_INET6 = None | |
| 31 try: | |
| 32 from socket import AF_UNIX | |
| 33 except ImportError: | |
| 34 AF_UNIX = None | |
| 35 | |
| 36 if sys.version_info >= (3, 4): | |
| 37 import enum | |
| 38 else: | |
| 39 enum = None | |
| 40 | |
| 41 | |
| 42 # can't take it from _common.py as this script is imported by setup.py | |
| 43 PY3 = sys.version_info[0] == 3 | |
| 44 | |
| 45 __all__ = [ | |
| 46 # OS constants | |
| 47 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', | |
| 48 'SUNOS', 'WINDOWS', | |
| 49 # connection constants | |
| 50 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', | |
| 51 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', | |
| 52 'CONN_NONE', 'CONN_SYN_RECV', 'CONN_SYN_SENT', 'CONN_TIME_WAIT', | |
| 53 # net constants | |
| 54 'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN', | |
| 55 # process status constants | |
| 56 'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED', | |
| 57 'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED', | |
| 58 'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL', | |
| 59 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED', | |
| 60 # other constants | |
| 61 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', | |
| 62 # named tuples | |
| 63 'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile', | |
| 64 'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart', | |
| 65 'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser', | |
| 66 # utility functions | |
| 67 'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize', | |
| 68 'parse_environ_block', 'path_exists_strict', 'usage_percent', | |
| 69 'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers", | |
| 70 'bytes2human', 'conn_to_ntuple', 'debug', | |
| 71 # shell utils | |
| 72 'hilite', 'term_supports_colors', 'print_color', | |
| 73 ] | |
| 74 | |
| 75 | |
| 76 # =================================================================== | |
| 77 # --- OS constants | |
| 78 # =================================================================== | |
| 79 | |
| 80 | |
| 81 POSIX = os.name == "posix" | |
| 82 WINDOWS = os.name == "nt" | |
| 83 LINUX = sys.platform.startswith("linux") | |
| 84 MACOS = sys.platform.startswith("darwin") | |
| 85 OSX = MACOS # deprecated alias | |
| 86 FREEBSD = sys.platform.startswith("freebsd") | |
| 87 OPENBSD = sys.platform.startswith("openbsd") | |
| 88 NETBSD = sys.platform.startswith("netbsd") | |
| 89 BSD = FREEBSD or OPENBSD or NETBSD | |
| 90 SUNOS = sys.platform.startswith(("sunos", "solaris")) | |
| 91 AIX = sys.platform.startswith("aix") | |
| 92 | |
| 93 | |
| 94 # =================================================================== | |
| 95 # --- API constants | |
| 96 # =================================================================== | |
| 97 | |
| 98 | |
| 99 # Process.status() | |
| 100 STATUS_RUNNING = "running" | |
| 101 STATUS_SLEEPING = "sleeping" | |
| 102 STATUS_DISK_SLEEP = "disk-sleep" | |
| 103 STATUS_STOPPED = "stopped" | |
| 104 STATUS_TRACING_STOP = "tracing-stop" | |
| 105 STATUS_ZOMBIE = "zombie" | |
| 106 STATUS_DEAD = "dead" | |
| 107 STATUS_WAKE_KILL = "wake-kill" | |
| 108 STATUS_WAKING = "waking" | |
| 109 STATUS_IDLE = "idle" # Linux, macOS, FreeBSD | |
| 110 STATUS_LOCKED = "locked" # FreeBSD | |
| 111 STATUS_WAITING = "waiting" # FreeBSD | |
| 112 STATUS_SUSPENDED = "suspended" # NetBSD | |
| 113 STATUS_PARKED = "parked" # Linux | |
| 114 | |
| 115 # Process.connections() and psutil.net_connections() | |
| 116 CONN_ESTABLISHED = "ESTABLISHED" | |
| 117 CONN_SYN_SENT = "SYN_SENT" | |
| 118 CONN_SYN_RECV = "SYN_RECV" | |
| 119 CONN_FIN_WAIT1 = "FIN_WAIT1" | |
| 120 CONN_FIN_WAIT2 = "FIN_WAIT2" | |
| 121 CONN_TIME_WAIT = "TIME_WAIT" | |
| 122 CONN_CLOSE = "CLOSE" | |
| 123 CONN_CLOSE_WAIT = "CLOSE_WAIT" | |
| 124 CONN_LAST_ACK = "LAST_ACK" | |
| 125 CONN_LISTEN = "LISTEN" | |
| 126 CONN_CLOSING = "CLOSING" | |
| 127 CONN_NONE = "NONE" | |
| 128 | |
| 129 # net_if_stats() | |
| 130 if enum is None: | |
| 131 NIC_DUPLEX_FULL = 2 | |
| 132 NIC_DUPLEX_HALF = 1 | |
| 133 NIC_DUPLEX_UNKNOWN = 0 | |
| 134 else: | |
| 135 class NicDuplex(enum.IntEnum): | |
| 136 NIC_DUPLEX_FULL = 2 | |
| 137 NIC_DUPLEX_HALF = 1 | |
| 138 NIC_DUPLEX_UNKNOWN = 0 | |
| 139 | |
| 140 globals().update(NicDuplex.__members__) | |
| 141 | |
| 142 # sensors_battery() | |
| 143 if enum is None: | |
| 144 POWER_TIME_UNKNOWN = -1 | |
| 145 POWER_TIME_UNLIMITED = -2 | |
| 146 else: | |
| 147 class BatteryTime(enum.IntEnum): | |
| 148 POWER_TIME_UNKNOWN = -1 | |
| 149 POWER_TIME_UNLIMITED = -2 | |
| 150 | |
| 151 globals().update(BatteryTime.__members__) | |
| 152 | |
| 153 # --- others | |
| 154 | |
| 155 ENCODING = sys.getfilesystemencoding() | |
| 156 if not PY3: | |
| 157 ENCODING_ERRS = "replace" | |
| 158 else: | |
| 159 try: | |
| 160 ENCODING_ERRS = sys.getfilesystemencodeerrors() # py 3.6 | |
| 161 except AttributeError: | |
| 162 ENCODING_ERRS = "surrogateescape" if POSIX else "replace" | |
| 163 | |
| 164 | |
| 165 # =================================================================== | |
| 166 # --- namedtuples | |
| 167 # =================================================================== | |
| 168 | |
| 169 # --- for system functions | |
| 170 | |
| 171 # psutil.swap_memory() | |
| 172 sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin', | |
| 173 'sout']) | |
| 174 # psutil.disk_usage() | |
| 175 sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent']) | |
| 176 # psutil.disk_io_counters() | |
| 177 sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', | |
| 178 'read_bytes', 'write_bytes', | |
| 179 'read_time', 'write_time']) | |
| 180 # psutil.disk_partitions() | |
| 181 sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts']) | |
| 182 # psutil.net_io_counters() | |
| 183 snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv', | |
| 184 'packets_sent', 'packets_recv', | |
| 185 'errin', 'errout', | |
| 186 'dropin', 'dropout']) | |
| 187 # psutil.users() | |
| 188 suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid']) | |
| 189 # psutil.net_connections() | |
| 190 sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr', | |
| 191 'status', 'pid']) | |
| 192 # psutil.net_if_addrs() | |
| 193 snicaddr = namedtuple('snicaddr', | |
| 194 ['family', 'address', 'netmask', 'broadcast', 'ptp']) | |
| 195 # psutil.net_if_stats() | |
| 196 snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu']) | |
| 197 # psutil.cpu_stats() | |
| 198 scpustats = namedtuple( | |
| 199 'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']) | |
| 200 # psutil.cpu_freq() | |
| 201 scpufreq = namedtuple('scpufreq', ['current', 'min', 'max']) | |
| 202 # psutil.sensors_temperatures() | |
| 203 shwtemp = namedtuple( | |
| 204 'shwtemp', ['label', 'current', 'high', 'critical']) | |
| 205 # psutil.sensors_battery() | |
| 206 sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) | |
| 207 # psutil.sensors_fans() | |
| 208 sfan = namedtuple('sfan', ['label', 'current']) | |
| 209 | |
| 210 # --- for Process methods | |
| 211 | |
| 212 # psutil.Process.cpu_times() | |
| 213 pcputimes = namedtuple('pcputimes', | |
| 214 ['user', 'system', 'children_user', 'children_system']) | |
| 215 # psutil.Process.open_files() | |
| 216 popenfile = namedtuple('popenfile', ['path', 'fd']) | |
| 217 # psutil.Process.threads() | |
| 218 pthread = namedtuple('pthread', ['id', 'user_time', 'system_time']) | |
| 219 # psutil.Process.uids() | |
| 220 puids = namedtuple('puids', ['real', 'effective', 'saved']) | |
| 221 # psutil.Process.gids() | |
| 222 pgids = namedtuple('pgids', ['real', 'effective', 'saved']) | |
| 223 # psutil.Process.io_counters() | |
| 224 pio = namedtuple('pio', ['read_count', 'write_count', | |
| 225 'read_bytes', 'write_bytes']) | |
| 226 # psutil.Process.ionice() | |
| 227 pionice = namedtuple('pionice', ['ioclass', 'value']) | |
| 228 # psutil.Process.ctx_switches() | |
| 229 pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary']) | |
| 230 # psutil.Process.connections() | |
| 231 pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr', | |
| 232 'status']) | |
| 233 | |
| 234 # psutil.connections() and psutil.Process.connections() | |
| 235 addr = namedtuple('addr', ['ip', 'port']) | |
| 236 | |
| 237 | |
| 238 # =================================================================== | |
| 239 # --- Process.connections() 'kind' parameter mapping | |
| 240 # =================================================================== | |
| 241 | |
| 242 | |
| 243 conn_tmap = { | |
| 244 "all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), | |
| 245 "tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]), | |
| 246 "tcp4": ([AF_INET], [SOCK_STREAM]), | |
| 247 "udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]), | |
| 248 "udp4": ([AF_INET], [SOCK_DGRAM]), | |
| 249 "inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), | |
| 250 "inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]), | |
| 251 "inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), | |
| 252 } | |
| 253 | |
| 254 if AF_INET6 is not None: | |
| 255 conn_tmap.update({ | |
| 256 "tcp6": ([AF_INET6], [SOCK_STREAM]), | |
| 257 "udp6": ([AF_INET6], [SOCK_DGRAM]), | |
| 258 }) | |
| 259 | |
| 260 if AF_UNIX is not None: | |
| 261 conn_tmap.update({ | |
| 262 "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), | |
| 263 }) | |
| 264 | |
| 265 | |
| 266 # ===================================================================== | |
| 267 # --- Exceptions | |
| 268 # ===================================================================== | |
| 269 | |
| 270 | |
| 271 class Error(Exception): | |
| 272 """Base exception class. All other psutil exceptions inherit | |
| 273 from this one. | |
| 274 """ | |
| 275 __module__ = 'psutil' | |
| 276 | |
| 277 def __init__(self, msg=""): | |
| 278 Exception.__init__(self, msg) | |
| 279 self.msg = msg | |
| 280 | |
| 281 def __repr__(self): | |
| 282 ret = "psutil.%s %s" % (self.__class__.__name__, self.msg) | |
| 283 return ret.strip() | |
| 284 | |
| 285 __str__ = __repr__ | |
| 286 | |
| 287 | |
| 288 class NoSuchProcess(Error): | |
| 289 """Exception raised when a process with a certain PID doesn't | |
| 290 or no longer exists. | |
| 291 """ | |
| 292 __module__ = 'psutil' | |
| 293 | |
| 294 def __init__(self, pid, name=None, msg=None): | |
| 295 Error.__init__(self, msg) | |
| 296 self.pid = pid | |
| 297 self.name = name | |
| 298 self.msg = msg | |
| 299 if msg is None: | |
| 300 if name: | |
| 301 details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) | |
| 302 else: | |
| 303 details = "(pid=%s)" % self.pid | |
| 304 self.msg = "process no longer exists " + details | |
| 305 | |
| 306 def __path__(self): | |
| 307 return 'xxx' | |
| 308 | |
| 309 | |
| 310 class ZombieProcess(NoSuchProcess): | |
| 311 """Exception raised when querying a zombie process. This is | |
| 312 raised on macOS, BSD and Solaris only, and not always: depending | |
| 313 on the query the OS may be able to succeed anyway. | |
| 314 On Linux all zombie processes are querable (hence this is never | |
| 315 raised). Windows doesn't have zombie processes. | |
| 316 """ | |
| 317 __module__ = 'psutil' | |
| 318 | |
| 319 def __init__(self, pid, name=None, ppid=None, msg=None): | |
| 320 NoSuchProcess.__init__(self, msg) | |
| 321 self.pid = pid | |
| 322 self.ppid = ppid | |
| 323 self.name = name | |
| 324 self.msg = msg | |
| 325 if msg is None: | |
| 326 args = ["pid=%s" % pid] | |
| 327 if name: | |
| 328 args.append("name=%s" % repr(self.name)) | |
| 329 if ppid: | |
| 330 args.append("ppid=%s" % self.ppid) | |
| 331 details = "(%s)" % ", ".join(args) | |
| 332 self.msg = "process still exists but it's a zombie " + details | |
| 333 | |
| 334 | |
| 335 class AccessDenied(Error): | |
| 336 """Exception raised when permission to perform an action is denied.""" | |
| 337 __module__ = 'psutil' | |
| 338 | |
| 339 def __init__(self, pid=None, name=None, msg=None): | |
| 340 Error.__init__(self, msg) | |
| 341 self.pid = pid | |
| 342 self.name = name | |
| 343 self.msg = msg | |
| 344 if msg is None: | |
| 345 if (pid is not None) and (name is not None): | |
| 346 self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) | |
| 347 elif (pid is not None): | |
| 348 self.msg = "(pid=%s)" % self.pid | |
| 349 else: | |
| 350 self.msg = "" | |
| 351 | |
| 352 | |
| 353 class TimeoutExpired(Error): | |
| 354 """Raised on Process.wait(timeout) if timeout expires and process | |
| 355 is still alive. | |
| 356 """ | |
| 357 __module__ = 'psutil' | |
| 358 | |
| 359 def __init__(self, seconds, pid=None, name=None): | |
| 360 Error.__init__(self, "timeout after %s seconds" % seconds) | |
| 361 self.seconds = seconds | |
| 362 self.pid = pid | |
| 363 self.name = name | |
| 364 if (pid is not None) and (name is not None): | |
| 365 self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) | |
| 366 elif (pid is not None): | |
| 367 self.msg += " (pid=%s)" % self.pid | |
| 368 | |
| 369 | |
| 370 # =================================================================== | |
| 371 # --- utils | |
| 372 # =================================================================== | |
| 373 | |
| 374 | |
| 375 def usage_percent(used, total, round_=None): | |
| 376 """Calculate percentage usage of 'used' against 'total'.""" | |
| 377 try: | |
| 378 ret = (float(used) / total) * 100 | |
| 379 except ZeroDivisionError: | |
| 380 return 0.0 | |
| 381 else: | |
| 382 if round_ is not None: | |
| 383 ret = round(ret, round_) | |
| 384 return ret | |
| 385 | |
| 386 | |
| 387 def memoize(fun): | |
| 388 """A simple memoize decorator for functions supporting (hashable) | |
| 389 positional arguments. | |
| 390 It also provides a cache_clear() function for clearing the cache: | |
| 391 | |
| 392 >>> @memoize | |
| 393 ... def foo() | |
| 394 ... return 1 | |
| 395 ... | |
| 396 >>> foo() | |
| 397 1 | |
| 398 >>> foo.cache_clear() | |
| 399 >>> | |
| 400 """ | |
| 401 @functools.wraps(fun) | |
| 402 def wrapper(*args, **kwargs): | |
| 403 key = (args, frozenset(sorted(kwargs.items()))) | |
| 404 try: | |
| 405 return cache[key] | |
| 406 except KeyError: | |
| 407 ret = cache[key] = fun(*args, **kwargs) | |
| 408 return ret | |
| 409 | |
| 410 def cache_clear(): | |
| 411 """Clear cache.""" | |
| 412 cache.clear() | |
| 413 | |
| 414 cache = {} | |
| 415 wrapper.cache_clear = cache_clear | |
| 416 return wrapper | |
| 417 | |
| 418 | |
| 419 def memoize_when_activated(fun): | |
| 420 """A memoize decorator which is disabled by default. It can be | |
| 421 activated and deactivated on request. | |
| 422 For efficiency reasons it can be used only against class methods | |
| 423 accepting no arguments. | |
| 424 | |
| 425 >>> class Foo: | |
| 426 ... @memoize | |
| 427 ... def foo() | |
| 428 ... print(1) | |
| 429 ... | |
| 430 >>> f = Foo() | |
| 431 >>> # deactivated (default) | |
| 432 >>> foo() | |
| 433 1 | |
| 434 >>> foo() | |
| 435 1 | |
| 436 >>> | |
| 437 >>> # activated | |
| 438 >>> foo.cache_activate(self) | |
| 439 >>> foo() | |
| 440 1 | |
| 441 >>> foo() | |
| 442 >>> foo() | |
| 443 >>> | |
| 444 """ | |
| 445 @functools.wraps(fun) | |
| 446 def wrapper(self): | |
| 447 try: | |
| 448 # case 1: we previously entered oneshot() ctx | |
| 449 ret = self._cache[fun] | |
| 450 except AttributeError: | |
| 451 # case 2: we never entered oneshot() ctx | |
| 452 return fun(self) | |
| 453 except KeyError: | |
| 454 # case 3: we entered oneshot() ctx but there's no cache | |
| 455 # for this entry yet | |
| 456 ret = self._cache[fun] = fun(self) | |
| 457 return ret | |
| 458 | |
| 459 def cache_activate(proc): | |
| 460 """Activate cache. Expects a Process instance. Cache will be | |
| 461 stored as a "_cache" instance attribute.""" | |
| 462 proc._cache = {} | |
| 463 | |
| 464 def cache_deactivate(proc): | |
| 465 """Deactivate and clear cache.""" | |
| 466 try: | |
| 467 del proc._cache | |
| 468 except AttributeError: | |
| 469 pass | |
| 470 | |
| 471 wrapper.cache_activate = cache_activate | |
| 472 wrapper.cache_deactivate = cache_deactivate | |
| 473 return wrapper | |
| 474 | |
| 475 | |
| 476 def isfile_strict(path): | |
| 477 """Same as os.path.isfile() but does not swallow EACCES / EPERM | |
| 478 exceptions, see: | |
| 479 http://mail.python.org/pipermail/python-dev/2012-June/120787.html | |
| 480 """ | |
| 481 try: | |
| 482 st = os.stat(path) | |
| 483 except OSError as err: | |
| 484 if err.errno in (errno.EPERM, errno.EACCES): | |
| 485 raise | |
| 486 return False | |
| 487 else: | |
| 488 return stat.S_ISREG(st.st_mode) | |
| 489 | |
| 490 | |
| 491 def path_exists_strict(path): | |
| 492 """Same as os.path.exists() but does not swallow EACCES / EPERM | |
| 493 exceptions, see: | |
| 494 http://mail.python.org/pipermail/python-dev/2012-June/120787.html | |
| 495 """ | |
| 496 try: | |
| 497 os.stat(path) | |
| 498 except OSError as err: | |
| 499 if err.errno in (errno.EPERM, errno.EACCES): | |
| 500 raise | |
| 501 return False | |
| 502 else: | |
| 503 return True | |
| 504 | |
| 505 | |
| 506 @memoize | |
| 507 def supports_ipv6(): | |
| 508 """Return True if IPv6 is supported on this platform.""" | |
| 509 if not socket.has_ipv6 or AF_INET6 is None: | |
| 510 return False | |
| 511 try: | |
| 512 sock = socket.socket(AF_INET6, socket.SOCK_STREAM) | |
| 513 with contextlib.closing(sock): | |
| 514 sock.bind(("::1", 0)) | |
| 515 return True | |
| 516 except socket.error: | |
| 517 return False | |
| 518 | |
| 519 | |
| 520 def parse_environ_block(data): | |
| 521 """Parse a C environ block of environment variables into a dictionary.""" | |
| 522 # The block is usually raw data from the target process. It might contain | |
| 523 # trailing garbage and lines that do not look like assignments. | |
| 524 ret = {} | |
| 525 pos = 0 | |
| 526 | |
| 527 # localize global variable to speed up access. | |
| 528 WINDOWS_ = WINDOWS | |
| 529 while True: | |
| 530 next_pos = data.find("\0", pos) | |
| 531 # nul byte at the beginning or double nul byte means finish | |
| 532 if next_pos <= pos: | |
| 533 break | |
| 534 # there might not be an equals sign | |
| 535 equal_pos = data.find("=", pos, next_pos) | |
| 536 if equal_pos > pos: | |
| 537 key = data[pos:equal_pos] | |
| 538 value = data[equal_pos + 1:next_pos] | |
| 539 # Windows expects environment variables to be uppercase only | |
| 540 if WINDOWS_: | |
| 541 key = key.upper() | |
| 542 ret[key] = value | |
| 543 pos = next_pos + 1 | |
| 544 | |
| 545 return ret | |
| 546 | |
| 547 | |
| 548 def sockfam_to_enum(num): | |
| 549 """Convert a numeric socket family value to an IntEnum member. | |
| 550 If it's not a known member, return the numeric value itself. | |
| 551 """ | |
| 552 if enum is None: | |
| 553 return num | |
| 554 else: # pragma: no cover | |
| 555 try: | |
| 556 return socket.AddressFamily(num) | |
| 557 except ValueError: | |
| 558 return num | |
| 559 | |
| 560 | |
| 561 def socktype_to_enum(num): | |
| 562 """Convert a numeric socket type value to an IntEnum member. | |
| 563 If it's not a known member, return the numeric value itself. | |
| 564 """ | |
| 565 if enum is None: | |
| 566 return num | |
| 567 else: # pragma: no cover | |
| 568 try: | |
| 569 return socket.SocketKind(num) | |
| 570 except ValueError: | |
| 571 return num | |
| 572 | |
| 573 | |
| 574 def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None): | |
| 575 """Convert a raw connection tuple to a proper ntuple.""" | |
| 576 if fam in (socket.AF_INET, AF_INET6): | |
| 577 if laddr: | |
| 578 laddr = addr(*laddr) | |
| 579 if raddr: | |
| 580 raddr = addr(*raddr) | |
| 581 if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6): | |
| 582 status = status_map.get(status, CONN_NONE) | |
| 583 else: | |
| 584 status = CONN_NONE # ignore whatever C returned to us | |
| 585 fam = sockfam_to_enum(fam) | |
| 586 type_ = socktype_to_enum(type_) | |
| 587 if pid is None: | |
| 588 return pconn(fd, fam, type_, laddr, raddr, status) | |
| 589 else: | |
| 590 return sconn(fd, fam, type_, laddr, raddr, status, pid) | |
| 591 | |
| 592 | |
| 593 def deprecated_method(replacement): | |
| 594 """A decorator which can be used to mark a method as deprecated | |
| 595 'replcement' is the method name which will be called instead. | |
| 596 """ | |
| 597 def outer(fun): | |
| 598 msg = "%s() is deprecated and will be removed; use %s() instead" % ( | |
| 599 fun.__name__, replacement) | |
| 600 if fun.__doc__ is None: | |
| 601 fun.__doc__ = msg | |
| 602 | |
| 603 @functools.wraps(fun) | |
| 604 def inner(self, *args, **kwargs): | |
| 605 warnings.warn(msg, category=DeprecationWarning, stacklevel=2) | |
| 606 return getattr(self, replacement)(*args, **kwargs) | |
| 607 return inner | |
| 608 return outer | |
| 609 | |
| 610 | |
| 611 class _WrapNumbers: | |
| 612 """Watches numbers so that they don't overflow and wrap | |
| 613 (reset to zero). | |
| 614 """ | |
| 615 | |
| 616 def __init__(self): | |
| 617 self.lock = threading.Lock() | |
| 618 self.cache = {} | |
| 619 self.reminders = {} | |
| 620 self.reminder_keys = {} | |
| 621 | |
| 622 def _add_dict(self, input_dict, name): | |
| 623 assert name not in self.cache | |
| 624 assert name not in self.reminders | |
| 625 assert name not in self.reminder_keys | |
| 626 self.cache[name] = input_dict | |
| 627 self.reminders[name] = defaultdict(int) | |
| 628 self.reminder_keys[name] = defaultdict(set) | |
| 629 | |
| 630 def _remove_dead_reminders(self, input_dict, name): | |
| 631 """In case the number of keys changed between calls (e.g. a | |
| 632 disk disappears) this removes the entry from self.reminders. | |
| 633 """ | |
| 634 old_dict = self.cache[name] | |
| 635 gone_keys = set(old_dict.keys()) - set(input_dict.keys()) | |
| 636 for gone_key in gone_keys: | |
| 637 for remkey in self.reminder_keys[name][gone_key]: | |
| 638 del self.reminders[name][remkey] | |
| 639 del self.reminder_keys[name][gone_key] | |
| 640 | |
| 641 def run(self, input_dict, name): | |
| 642 """Cache dict and sum numbers which overflow and wrap. | |
| 643 Return an updated copy of `input_dict` | |
| 644 """ | |
| 645 if name not in self.cache: | |
| 646 # This was the first call. | |
| 647 self._add_dict(input_dict, name) | |
| 648 return input_dict | |
| 649 | |
| 650 self._remove_dead_reminders(input_dict, name) | |
| 651 | |
| 652 old_dict = self.cache[name] | |
| 653 new_dict = {} | |
| 654 for key in input_dict.keys(): | |
| 655 input_tuple = input_dict[key] | |
| 656 try: | |
| 657 old_tuple = old_dict[key] | |
| 658 except KeyError: | |
| 659 # The input dict has a new key (e.g. a new disk or NIC) | |
| 660 # which didn't exist in the previous call. | |
| 661 new_dict[key] = input_tuple | |
| 662 continue | |
| 663 | |
| 664 bits = [] | |
| 665 for i in range(len(input_tuple)): | |
| 666 input_value = input_tuple[i] | |
| 667 old_value = old_tuple[i] | |
| 668 remkey = (key, i) | |
| 669 if input_value < old_value: | |
| 670 # it wrapped! | |
| 671 self.reminders[name][remkey] += old_value | |
| 672 self.reminder_keys[name][key].add(remkey) | |
| 673 bits.append(input_value + self.reminders[name][remkey]) | |
| 674 | |
| 675 new_dict[key] = tuple(bits) | |
| 676 | |
| 677 self.cache[name] = input_dict | |
| 678 return new_dict | |
| 679 | |
| 680 def cache_clear(self, name=None): | |
| 681 """Clear the internal cache, optionally only for function 'name'.""" | |
| 682 with self.lock: | |
| 683 if name is None: | |
| 684 self.cache.clear() | |
| 685 self.reminders.clear() | |
| 686 self.reminder_keys.clear() | |
| 687 else: | |
| 688 self.cache.pop(name, None) | |
| 689 self.reminders.pop(name, None) | |
| 690 self.reminder_keys.pop(name, None) | |
| 691 | |
| 692 def cache_info(self): | |
| 693 """Return internal cache dicts as a tuple of 3 elements.""" | |
| 694 with self.lock: | |
| 695 return (self.cache, self.reminders, self.reminder_keys) | |
| 696 | |
| 697 | |
| 698 def wrap_numbers(input_dict, name): | |
| 699 """Given an `input_dict` and a function `name`, adjust the numbers | |
| 700 which "wrap" (restart from zero) across different calls by adding | |
| 701 "old value" to "new value" and return an updated dict. | |
| 702 """ | |
| 703 with _wn.lock: | |
| 704 return _wn.run(input_dict, name) | |
| 705 | |
| 706 | |
| 707 _wn = _WrapNumbers() | |
| 708 wrap_numbers.cache_clear = _wn.cache_clear | |
| 709 wrap_numbers.cache_info = _wn.cache_info | |
| 710 | |
| 711 | |
| 712 def open_binary(fname, **kwargs): | |
| 713 return open(fname, "rb", **kwargs) | |
| 714 | |
| 715 | |
| 716 def open_text(fname, **kwargs): | |
| 717 """On Python 3 opens a file in text mode by using fs encoding and | |
| 718 a proper en/decoding errors handler. | |
| 719 On Python 2 this is just an alias for open(name, 'rt'). | |
| 720 """ | |
| 721 if PY3: | |
| 722 # See: | |
| 723 # https://github.com/giampaolo/psutil/issues/675 | |
| 724 # https://github.com/giampaolo/psutil/pull/733 | |
| 725 kwargs.setdefault('encoding', ENCODING) | |
| 726 kwargs.setdefault('errors', ENCODING_ERRS) | |
| 727 return open(fname, "rt", **kwargs) | |
| 728 | |
| 729 | |
| 730 def bytes2human(n, format="%(value).1f%(symbol)s"): | |
| 731 """Used by various scripts. See: | |
| 732 http://goo.gl/zeJZl | |
| 733 | |
| 734 >>> bytes2human(10000) | |
| 735 '9.8K' | |
| 736 >>> bytes2human(100001221) | |
| 737 '95.4M' | |
| 738 """ | |
| 739 symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') | |
| 740 prefix = {} | |
| 741 for i, s in enumerate(symbols[1:]): | |
| 742 prefix[s] = 1 << (i + 1) * 10 | |
| 743 for symbol in reversed(symbols[1:]): | |
| 744 if n >= prefix[symbol]: | |
| 745 value = float(n) / prefix[symbol] | |
| 746 return format % locals() | |
| 747 return format % dict(symbol=symbols[0], value=n) | |
| 748 | |
| 749 | |
| 750 def get_procfs_path(): | |
| 751 """Return updated psutil.PROCFS_PATH constant.""" | |
| 752 return sys.modules['psutil'].PROCFS_PATH | |
| 753 | |
| 754 | |
| 755 if PY3: | |
| 756 def decode(s): | |
| 757 return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) | |
| 758 else: | |
| 759 def decode(s): | |
| 760 return s | |
| 761 | |
| 762 | |
| 763 # ===================================================================== | |
| 764 # --- shell utils | |
| 765 # ===================================================================== | |
| 766 | |
| 767 | |
| 768 @memoize | |
| 769 def term_supports_colors(file=sys.stdout): | |
| 770 if os.name == 'nt': | |
| 771 return True | |
| 772 try: | |
| 773 import curses | |
| 774 assert file.isatty() | |
| 775 curses.setupterm() | |
| 776 assert curses.tigetnum("colors") > 0 | |
| 777 except Exception: | |
| 778 return False | |
| 779 else: | |
| 780 return True | |
| 781 | |
| 782 | |
| 783 def hilite(s, color=None, bold=False): | |
| 784 """Return an highlighted version of 'string'.""" | |
| 785 if not term_supports_colors(): | |
| 786 return s | |
| 787 attr = [] | |
| 788 colors = dict(green='32', red='91', brown='33', yellow='93', blue='34', | |
| 789 violet='35', lightblue='36', grey='37', darkgrey='30') | |
| 790 colors[None] = '29' | |
| 791 try: | |
| 792 color = colors[color] | |
| 793 except KeyError: | |
| 794 raise ValueError("invalid color %r; choose between %s" % ( | |
| 795 list(colors.keys()))) | |
| 796 attr.append(color) | |
| 797 if bold: | |
| 798 attr.append('1') | |
| 799 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) | |
| 800 | |
| 801 | |
| 802 def print_color(s, color=None, bold=False, file=sys.stdout): | |
| 803 """Print a colorized version of string.""" | |
| 804 if not term_supports_colors(): | |
| 805 print(s, file=file) # NOQA | |
| 806 elif POSIX: | |
| 807 print(hilite(s, color, bold), file=file) # NOQA | |
| 808 else: | |
| 809 import ctypes | |
| 810 | |
| 811 DEFAULT_COLOR = 7 | |
| 812 GetStdHandle = ctypes.windll.Kernel32.GetStdHandle | |
| 813 SetConsoleTextAttribute = \ | |
| 814 ctypes.windll.Kernel32.SetConsoleTextAttribute | |
| 815 | |
| 816 colors = dict(green=2, red=4, brown=6, yellow=6) | |
| 817 colors[None] = DEFAULT_COLOR | |
| 818 try: | |
| 819 color = colors[color] | |
| 820 except KeyError: | |
| 821 raise ValueError("invalid color %r; choose between %r" % ( | |
| 822 color, list(colors.keys()))) | |
| 823 if bold and color <= 7: | |
| 824 color += 8 | |
| 825 | |
| 826 handle_id = -12 if file is sys.stderr else -11 | |
| 827 GetStdHandle.restype = ctypes.c_ulong | |
| 828 handle = GetStdHandle(handle_id) | |
| 829 SetConsoleTextAttribute(handle, color) | |
| 830 try: | |
| 831 print(s, file=file) # NOQA | |
| 832 finally: | |
| 833 SetConsoleTextAttribute(handle, DEFAULT_COLOR) | |
| 834 | |
| 835 | |
| 836 if bool(os.getenv('PSUTIL_DEBUG', 0)): | |
| 837 import inspect | |
| 838 | |
| 839 def debug(msg): | |
| 840 """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" | |
| 841 fname, lineno, func_name, lines, index = inspect.getframeinfo( | |
| 842 inspect.currentframe().f_back) | |
| 843 print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), # NOQA | |
| 844 file=sys.stderr) | |
| 845 else: | |
| 846 def debug(msg): | |
| 847 pass |
