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