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