Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/psutil/_common.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
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="green", 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') | |
789 colors[None] = '29' | |
790 try: | |
791 color = colors[color] | |
792 except KeyError: | |
793 raise ValueError("invalid color %r; choose between %s" % ( | |
794 list(colors.keys()))) | |
795 attr.append(color) | |
796 if bold: | |
797 attr.append('1') | |
798 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) | |
799 | |
800 | |
801 def print_color(s, color="green", bold=False, file=sys.stdout): | |
802 """Print a colorized version of string.""" | |
803 if not term_supports_colors(): | |
804 print(s, file=file) | |
805 elif POSIX: | |
806 print(hilite(s, color, bold), file=file) | |
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) | |
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) | |
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), | |
843 file=sys.stderr) | |
844 else: | |
845 def debug(msg): | |
846 pass |