Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/psutil/_common.py: 47%

407 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +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. 

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 

10from __future__ import division 

11from __future__ import print_function 

12 

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 

27 

28 

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 

37 

38if sys.version_info >= (3, 4): 

39 import enum 

40else: 

41 enum = None 

42 

43 

44# can't take it from _common.py as this script is imported by setup.py 

45PY3 = sys.version_info[0] == 3 

46PSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG', 0)) 

47_DEFAULT = object() 

48 

49__all__ = [ 

50 # OS constants 

51 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', 

52 'SUNOS', 'WINDOWS', 

53 # connection constants 

54 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', 

55 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', 

56 'CONN_NONE', 'CONN_SYN_RECV', 'CONN_SYN_SENT', 'CONN_TIME_WAIT', 

57 # net constants 

58 'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN', 

59 # process status constants 

60 'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED', 

61 'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED', 

62 'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL', 

63 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED', 

64 # other constants 

65 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', 

66 # named tuples 

67 'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile', 

68 'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart', 

69 'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser', 

70 # utility functions 

71 'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize', 

72 'parse_environ_block', 'path_exists_strict', 'usage_percent', 

73 'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers", 

74 'open_text', 'open_binary', 'cat', 'bcat', 

75 'bytes2human', 'conn_to_ntuple', 'debug', 

76 # shell utils 

77 'hilite', 'term_supports_colors', 'print_color', 

78] 

79 

80 

81# =================================================================== 

82# --- OS constants 

83# =================================================================== 

84 

85 

86POSIX = os.name == "posix" 

87WINDOWS = os.name == "nt" 

88LINUX = sys.platform.startswith("linux") 

89MACOS = sys.platform.startswith("darwin") 

90OSX = MACOS # deprecated alias 

91FREEBSD = sys.platform.startswith(("freebsd", "midnightbsd")) 

92OPENBSD = sys.platform.startswith("openbsd") 

93NETBSD = sys.platform.startswith("netbsd") 

94BSD = FREEBSD or OPENBSD or NETBSD 

95SUNOS = sys.platform.startswith(("sunos", "solaris")) 

96AIX = sys.platform.startswith("aix") 

97 

98 

99# =================================================================== 

100# --- API constants 

101# =================================================================== 

102 

103 

104# Process.status() 

105STATUS_RUNNING = "running" 

106STATUS_SLEEPING = "sleeping" 

107STATUS_DISK_SLEEP = "disk-sleep" 

108STATUS_STOPPED = "stopped" 

109STATUS_TRACING_STOP = "tracing-stop" 

110STATUS_ZOMBIE = "zombie" 

111STATUS_DEAD = "dead" 

112STATUS_WAKE_KILL = "wake-kill" 

113STATUS_WAKING = "waking" 

114STATUS_IDLE = "idle" # Linux, macOS, FreeBSD 

115STATUS_LOCKED = "locked" # FreeBSD 

116STATUS_WAITING = "waiting" # FreeBSD 

117STATUS_SUSPENDED = "suspended" # NetBSD 

118STATUS_PARKED = "parked" # Linux 

119 

120# Process.connections() and psutil.net_connections() 

121CONN_ESTABLISHED = "ESTABLISHED" 

122CONN_SYN_SENT = "SYN_SENT" 

123CONN_SYN_RECV = "SYN_RECV" 

124CONN_FIN_WAIT1 = "FIN_WAIT1" 

125CONN_FIN_WAIT2 = "FIN_WAIT2" 

126CONN_TIME_WAIT = "TIME_WAIT" 

127CONN_CLOSE = "CLOSE" 

128CONN_CLOSE_WAIT = "CLOSE_WAIT" 

129CONN_LAST_ACK = "LAST_ACK" 

130CONN_LISTEN = "LISTEN" 

131CONN_CLOSING = "CLOSING" 

132CONN_NONE = "NONE" 

133 

134# net_if_stats() 

135if enum is None: 

136 NIC_DUPLEX_FULL = 2 

137 NIC_DUPLEX_HALF = 1 

138 NIC_DUPLEX_UNKNOWN = 0 

139else: 

140 class NicDuplex(enum.IntEnum): 

141 NIC_DUPLEX_FULL = 2 

142 NIC_DUPLEX_HALF = 1 

143 NIC_DUPLEX_UNKNOWN = 0 

144 

145 globals().update(NicDuplex.__members__) 

146 

147# sensors_battery() 

148if enum is None: 

149 POWER_TIME_UNKNOWN = -1 

150 POWER_TIME_UNLIMITED = -2 

151else: 

152 class BatteryTime(enum.IntEnum): 

153 POWER_TIME_UNKNOWN = -1 

154 POWER_TIME_UNLIMITED = -2 

155 

156 globals().update(BatteryTime.__members__) 

157 

158# --- others 

159 

160ENCODING = sys.getfilesystemencoding() 

161if not PY3: 

162 ENCODING_ERRS = "replace" 

163else: 

164 try: 

165 ENCODING_ERRS = sys.getfilesystemencodeerrors() # py 3.6 

166 except AttributeError: 

167 ENCODING_ERRS = "surrogateescape" if POSIX else "replace" 

168 

169 

170# =================================================================== 

171# --- namedtuples 

172# =================================================================== 

173 

174# --- for system functions 

175 

176# psutil.swap_memory() 

177sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin', 

178 'sout']) 

179# psutil.disk_usage() 

180sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent']) 

181# psutil.disk_io_counters() 

182sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', 

183 'read_bytes', 'write_bytes', 

184 'read_time', 'write_time']) 

185# psutil.disk_partitions() 

186sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts', 

187 'maxfile', 'maxpath']) 

188# psutil.net_io_counters() 

189snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv', 

190 'packets_sent', 'packets_recv', 

191 'errin', 'errout', 

192 'dropin', 'dropout']) 

193# psutil.users() 

194suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid']) 

195# psutil.net_connections() 

196sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr', 

197 'status', 'pid']) 

198# psutil.net_if_addrs() 

199snicaddr = namedtuple('snicaddr', 

200 ['family', 'address', 'netmask', 'broadcast', 'ptp']) 

201# psutil.net_if_stats() 

202snicstats = namedtuple('snicstats', 

203 ['isup', 'duplex', 'speed', 'mtu', 'flags']) 

204# psutil.cpu_stats() 

205scpustats = namedtuple( 

206 'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']) 

207# psutil.cpu_freq() 

208scpufreq = namedtuple('scpufreq', ['current', 'min', 'max']) 

209# psutil.sensors_temperatures() 

210shwtemp = namedtuple( 

211 'shwtemp', ['label', 'current', 'high', 'critical']) 

212# psutil.sensors_battery() 

213sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) 

214# psutil.sensors_fans() 

215sfan = namedtuple('sfan', ['label', 'current']) 

216 

217# --- for Process methods 

218 

219# psutil.Process.cpu_times() 

220pcputimes = namedtuple('pcputimes', 

221 ['user', 'system', 'children_user', 'children_system']) 

222# psutil.Process.open_files() 

223popenfile = namedtuple('popenfile', ['path', 'fd']) 

224# psutil.Process.threads() 

225pthread = namedtuple('pthread', ['id', 'user_time', 'system_time']) 

226# psutil.Process.uids() 

227puids = namedtuple('puids', ['real', 'effective', 'saved']) 

228# psutil.Process.gids() 

229pgids = namedtuple('pgids', ['real', 'effective', 'saved']) 

230# psutil.Process.io_counters() 

231pio = namedtuple('pio', ['read_count', 'write_count', 

232 'read_bytes', 'write_bytes']) 

233# psutil.Process.ionice() 

234pionice = namedtuple('pionice', ['ioclass', 'value']) 

235# psutil.Process.ctx_switches() 

236pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary']) 

237# psutil.Process.connections() 

238pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr', 

239 'status']) 

240 

241# psutil.connections() and psutil.Process.connections() 

242addr = namedtuple('addr', ['ip', 'port']) 

243 

244 

245# =================================================================== 

246# --- Process.connections() 'kind' parameter mapping 

247# =================================================================== 

248 

249 

250conn_tmap = { 

251 "all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), 

252 "tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]), 

253 "tcp4": ([AF_INET], [SOCK_STREAM]), 

254 "udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]), 

255 "udp4": ([AF_INET], [SOCK_DGRAM]), 

256 "inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), 

257 "inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]), 

258 "inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), 

259} 

260 

261if AF_INET6 is not None: 

262 conn_tmap.update({ 

263 "tcp6": ([AF_INET6], [SOCK_STREAM]), 

264 "udp6": ([AF_INET6], [SOCK_DGRAM]), 

265 }) 

266 

267if AF_UNIX is not None: 

268 conn_tmap.update({ 

269 "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), 

270 }) 

271 

272 

273# ===================================================================== 

274# --- Exceptions 

275# ===================================================================== 

276 

277 

278class Error(Exception): 

279 """Base exception class. All other psutil exceptions inherit 

280 from this one. 

281 """ 

282 __module__ = 'psutil' 

283 

284 def _infodict(self, attrs): 

285 info = collections.OrderedDict() 

286 for name in attrs: 

287 value = getattr(self, name, None) 

288 if value: 

289 info[name] = value 

290 elif name == "pid" and value == 0: 

291 info[name] = value 

292 return info 

293 

294 def __str__(self): 

295 # invoked on `raise Error` 

296 info = self._infodict(("pid", "ppid", "name")) 

297 if info: 

298 details = "(%s)" % ", ".join( 

299 ["%s=%r" % (k, v) for k, v in info.items()]) 

300 else: 

301 details = None 

302 return " ".join([x for x in (getattr(self, "msg", ""), details) if x]) 

303 

304 def __repr__(self): 

305 # invoked on `repr(Error)` 

306 info = self._infodict(("pid", "ppid", "name", "seconds", "msg")) 

307 details = ", ".join(["%s=%r" % (k, v) for k, v in info.items()]) 

308 return "psutil.%s(%s)" % (self.__class__.__name__, details) 

309 

310 

311class NoSuchProcess(Error): 

312 """Exception raised when a process with a certain PID doesn't 

313 or no longer exists. 

314 """ 

315 __module__ = 'psutil' 

316 

317 def __init__(self, pid, name=None, msg=None): 

318 Error.__init__(self) 

319 self.pid = pid 

320 self.name = name 

321 self.msg = msg or "process no longer exists" 

322 

323 

324class ZombieProcess(NoSuchProcess): 

325 """Exception raised when querying a zombie process. This is 

326 raised on macOS, BSD and Solaris only, and not always: depending 

327 on the query the OS may be able to succeed anyway. 

328 On Linux all zombie processes are querable (hence this is never 

329 raised). Windows doesn't have zombie processes. 

330 """ 

331 __module__ = 'psutil' 

332 

333 def __init__(self, pid, name=None, ppid=None, msg=None): 

334 NoSuchProcess.__init__(self, pid, name, msg) 

335 self.ppid = ppid 

336 self.msg = msg or "PID still exists but it's a zombie" 

337 

338 

339class AccessDenied(Error): 

340 """Exception raised when permission to perform an action is denied.""" 

341 __module__ = 'psutil' 

342 

343 def __init__(self, pid=None, name=None, msg=None): 

344 Error.__init__(self) 

345 self.pid = pid 

346 self.name = name 

347 self.msg = msg or "" 

348 

349 

350class TimeoutExpired(Error): 

351 """Raised on Process.wait(timeout) if timeout expires and process 

352 is still alive. 

353 """ 

354 __module__ = 'psutil' 

355 

356 def __init__(self, seconds, pid=None, name=None): 

357 Error.__init__(self) 

358 self.seconds = seconds 

359 self.pid = pid 

360 self.name = name 

361 self.msg = "timeout after %s seconds" % seconds 

362 

363 

364# =================================================================== 

365# --- utils 

366# =================================================================== 

367 

368 

369def usage_percent(used, total, round_=None): 

370 """Calculate percentage usage of 'used' against 'total'.""" 

371 try: 

372 ret = (float(used) / total) * 100 

373 except ZeroDivisionError: 

374 return 0.0 

375 else: 

376 if round_ is not None: 

377 ret = round(ret, round_) 

378 return ret 

379 

380 

381def memoize(fun): 

382 """A simple memoize decorator for functions supporting (hashable) 

383 positional arguments. 

384 It also provides a cache_clear() function for clearing the cache: 

385 

386 >>> @memoize 

387 ... def foo() 

388 ... return 1 

389 ... 

390 >>> foo() 

391 1 

392 >>> foo.cache_clear() 

393 >>> 

394 """ 

395 @functools.wraps(fun) 

396 def wrapper(*args, **kwargs): 

397 key = (args, frozenset(sorted(kwargs.items()))) 

398 try: 

399 return cache[key] 

400 except KeyError: 

401 ret = cache[key] = fun(*args, **kwargs) 

402 return ret 

403 

404 def cache_clear(): 

405 """Clear cache.""" 

406 cache.clear() 

407 

408 cache = {} 

409 wrapper.cache_clear = cache_clear 

410 return wrapper 

411 

412 

413def memoize_when_activated(fun): 

414 """A memoize decorator which is disabled by default. It can be 

415 activated and deactivated on request. 

416 For efficiency reasons it can be used only against class methods 

417 accepting no arguments. 

418 

419 >>> class Foo: 

420 ... @memoize 

421 ... def foo() 

422 ... print(1) 

423 ... 

424 >>> f = Foo() 

425 >>> # deactivated (default) 

426 >>> foo() 

427 1 

428 >>> foo() 

429 1 

430 >>> 

431 >>> # activated 

432 >>> foo.cache_activate(self) 

433 >>> foo() 

434 1 

435 >>> foo() 

436 >>> foo() 

437 >>> 

438 """ 

439 @functools.wraps(fun) 

440 def wrapper(self): 

441 try: 

442 # case 1: we previously entered oneshot() ctx 

443 ret = self._cache[fun] 

444 except AttributeError: 

445 # case 2: we never entered oneshot() ctx 

446 return fun(self) 

447 except KeyError: 

448 # case 3: we entered oneshot() ctx but there's no cache 

449 # for this entry yet 

450 ret = fun(self) 

451 try: 

452 self._cache[fun] = ret 

453 except AttributeError: 

454 # multi-threading race condition, see: 

455 # https://github.com/giampaolo/psutil/issues/1948 

456 pass 

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 

476def 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 

491def 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 

507def 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 

520def 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 

548def 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 

561def 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 

574def 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 

593def 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 

611class _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] = collections.defaultdict(int) 

628 self.reminder_keys[name] = collections.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 

698def 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() 

708wrap_numbers.cache_clear = _wn.cache_clear 

709wrap_numbers.cache_info = _wn.cache_info 

710 

711 

712# The read buffer size for open() builtin. This (also) dictates how 

713# much data we read(2) when iterating over file lines as in: 

714# >>> with open(file) as f: 

715# ... for line in f: 

716# ... ... 

717# Default per-line buffer size for binary files is 1K. For text files 

718# is 8K. We use a bigger buffer (32K) in order to have more consistent 

719# results when reading /proc pseudo files on Linux, see: 

720# https://github.com/giampaolo/psutil/issues/2050 

721# On Python 2 this also speeds up the reading of big files: 

722# (namely /proc/{pid}/smaps and /proc/net/*): 

723# https://github.com/giampaolo/psutil/issues/708 

724FILE_READ_BUFFER_SIZE = 32 * 1024 

725 

726 

727def open_binary(fname): 

728 return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE) 

729 

730 

731def open_text(fname): 

732 """On Python 3 opens a file in text mode by using fs encoding and 

733 a proper en/decoding errors handler. 

734 On Python 2 this is just an alias for open(name, 'rt'). 

735 """ 

736 if not PY3: 

737 return open(fname, "rt", buffering=FILE_READ_BUFFER_SIZE) 

738 

739 # See: 

740 # https://github.com/giampaolo/psutil/issues/675 

741 # https://github.com/giampaolo/psutil/pull/733 

742 fobj = open(fname, "rt", buffering=FILE_READ_BUFFER_SIZE, 

743 encoding=ENCODING, errors=ENCODING_ERRS) 

744 try: 

745 # Dictates per-line read(2) buffer size. Defaults is 8k. See: 

746 # https://github.com/giampaolo/psutil/issues/2050#issuecomment-1013387546 

747 fobj._CHUNK_SIZE = FILE_READ_BUFFER_SIZE 

748 except AttributeError: 

749 pass 

750 except Exception: 

751 fobj.close() 

752 raise 

753 

754 return fobj 

755 

756 

757def cat(fname, fallback=_DEFAULT, _open=open_text): 

758 """Read entire file content and return it as a string. File is 

759 opened in text mode. If specified, `fallback` is the value 

760 returned in case of error, either if the file does not exist or 

761 it can't be read(). 

762 """ 

763 if fallback is _DEFAULT: 

764 with _open(fname) as f: 

765 return f.read() 

766 else: 

767 try: 

768 with _open(fname) as f: 

769 return f.read() 

770 except (IOError, OSError): 

771 return fallback 

772 

773 

774def bcat(fname, fallback=_DEFAULT): 

775 """Same as above but opens file in binary mode.""" 

776 return cat(fname, fallback=fallback, _open=open_binary) 

777 

778 

779def bytes2human(n, format="%(value).1f%(symbol)s"): 

780 """Used by various scripts. See: 

781 http://goo.gl/zeJZl 

782 

783 >>> bytes2human(10000) 

784 '9.8K' 

785 >>> bytes2human(100001221) 

786 '95.4M' 

787 """ 

788 symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') 

789 prefix = {} 

790 for i, s in enumerate(symbols[1:]): 

791 prefix[s] = 1 << (i + 1) * 10 

792 for symbol in reversed(symbols[1:]): 

793 if n >= prefix[symbol]: 

794 value = float(n) / prefix[symbol] 

795 return format % locals() 

796 return format % dict(symbol=symbols[0], value=n) 

797 

798 

799def get_procfs_path(): 

800 """Return updated psutil.PROCFS_PATH constant.""" 

801 return sys.modules['psutil'].PROCFS_PATH 

802 

803 

804if PY3: 

805 def decode(s): 

806 return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) 

807else: 

808 def decode(s): 

809 return s 

810 

811 

812# ===================================================================== 

813# --- shell utils 

814# ===================================================================== 

815 

816 

817@memoize 

818def term_supports_colors(file=sys.stdout): # pragma: no cover 

819 if os.name == 'nt': 

820 return True 

821 try: 

822 import curses 

823 assert file.isatty() 

824 curses.setupterm() 

825 assert curses.tigetnum("colors") > 0 

826 except Exception: 

827 return False 

828 else: 

829 return True 

830 

831 

832def hilite(s, color=None, bold=False): # pragma: no cover 

833 """Return an highlighted version of 'string'.""" 

834 if not term_supports_colors(): 

835 return s 

836 attr = [] 

837 colors = dict(green='32', red='91', brown='33', yellow='93', blue='34', 

838 violet='35', lightblue='36', grey='37', darkgrey='30') 

839 colors[None] = '29' 

840 try: 

841 color = colors[color] 

842 except KeyError: 

843 raise ValueError("invalid color %r; choose between %s" % ( 

844 list(colors.keys()))) 

845 attr.append(color) 

846 if bold: 

847 attr.append('1') 

848 return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) 

849 

850 

851def print_color( 

852 s, color=None, bold=False, file=sys.stdout): # pragma: no cover 

853 """Print a colorized version of string.""" 

854 if not term_supports_colors(): 

855 print(s, file=file) # NOQA 

856 elif POSIX: 

857 print(hilite(s, color, bold), file=file) # NOQA 

858 else: 

859 import ctypes 

860 

861 DEFAULT_COLOR = 7 

862 GetStdHandle = ctypes.windll.Kernel32.GetStdHandle 

863 SetConsoleTextAttribute = \ 

864 ctypes.windll.Kernel32.SetConsoleTextAttribute 

865 

866 colors = dict(green=2, red=4, brown=6, yellow=6) 

867 colors[None] = DEFAULT_COLOR 

868 try: 

869 color = colors[color] 

870 except KeyError: 

871 raise ValueError("invalid color %r; choose between %r" % ( 

872 color, list(colors.keys()))) 

873 if bold and color <= 7: 

874 color += 8 

875 

876 handle_id = -12 if file is sys.stderr else -11 

877 GetStdHandle.restype = ctypes.c_ulong 

878 handle = GetStdHandle(handle_id) 

879 SetConsoleTextAttribute(handle, color) 

880 try: 

881 print(s, file=file) # NOQA 

882 finally: 

883 SetConsoleTextAttribute(handle, DEFAULT_COLOR) 

884 

885 

886def debug(msg): 

887 """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" 

888 if PSUTIL_DEBUG: 

889 import inspect 

890 fname, lineno, func_name, lines, index = inspect.getframeinfo( 

891 inspect.currentframe().f_back) 

892 if isinstance(msg, Exception): 

893 if isinstance(msg, (OSError, IOError, EnvironmentError)): 

894 # ...because str(exc) may contain info about the file name 

895 msg = "ignoring %s" % msg 

896 else: 

897 msg = "ignoring %r" % msg 

898 print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), # NOQA 

899 file=sys.stderr)