Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/psutil/__init__.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

947 statements  

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"""psutil is a cross-platform library for retrieving information on 

6running processes and system utilization (CPU, memory, disks, network, 

7sensors) in Python. Supported platforms: 

8 

9 - Linux 

10 - Windows 

11 - macOS 

12 - FreeBSD 

13 - OpenBSD 

14 - NetBSD 

15 - Sun Solaris 

16 - AIX 

17 

18Supported Python versions are cPython 3.6+ and PyPy. 

19""" 

20 

21import collections 

22import contextlib 

23import datetime 

24import functools 

25import os 

26import signal 

27import socket 

28import subprocess 

29import sys 

30import threading 

31import time 

32 

33 

34try: 

35 import pwd 

36except ImportError: 

37 pwd = None 

38 

39from . import _common 

40from ._common import AIX 

41from ._common import BSD 

42from ._common import CONN_CLOSE 

43from ._common import CONN_CLOSE_WAIT 

44from ._common import CONN_CLOSING 

45from ._common import CONN_ESTABLISHED 

46from ._common import CONN_FIN_WAIT1 

47from ._common import CONN_FIN_WAIT2 

48from ._common import CONN_LAST_ACK 

49from ._common import CONN_LISTEN 

50from ._common import CONN_NONE 

51from ._common import CONN_SYN_RECV 

52from ._common import CONN_SYN_SENT 

53from ._common import CONN_TIME_WAIT 

54from ._common import FREEBSD 

55from ._common import LINUX 

56from ._common import MACOS 

57from ._common import NETBSD 

58from ._common import NIC_DUPLEX_FULL 

59from ._common import NIC_DUPLEX_HALF 

60from ._common import NIC_DUPLEX_UNKNOWN 

61from ._common import OPENBSD 

62from ._common import OSX # deprecated alias 

63from ._common import POSIX 

64from ._common import POWER_TIME_UNKNOWN 

65from ._common import POWER_TIME_UNLIMITED 

66from ._common import STATUS_DEAD 

67from ._common import STATUS_DISK_SLEEP 

68from ._common import STATUS_IDLE 

69from ._common import STATUS_LOCKED 

70from ._common import STATUS_PARKED 

71from ._common import STATUS_RUNNING 

72from ._common import STATUS_SLEEPING 

73from ._common import STATUS_STOPPED 

74from ._common import STATUS_TRACING_STOP 

75from ._common import STATUS_WAITING 

76from ._common import STATUS_WAKING 

77from ._common import STATUS_ZOMBIE 

78from ._common import SUNOS 

79from ._common import WINDOWS 

80from ._common import AccessDenied 

81from ._common import Error 

82from ._common import NoSuchProcess 

83from ._common import TimeoutExpired 

84from ._common import ZombieProcess 

85from ._common import debug 

86from ._common import memoize_when_activated 

87from ._common import wrap_numbers as _wrap_numbers 

88 

89 

90if LINUX: 

91 # This is public API and it will be retrieved from _pslinux.py 

92 # via sys.modules. 

93 PROCFS_PATH = "/proc" 

94 

95 from . import _pslinux as _psplatform 

96 from ._pslinux import IOPRIO_CLASS_BE # noqa: F401 

97 from ._pslinux import IOPRIO_CLASS_IDLE # noqa: F401 

98 from ._pslinux import IOPRIO_CLASS_NONE # noqa: F401 

99 from ._pslinux import IOPRIO_CLASS_RT # noqa: F401 

100 

101elif WINDOWS: 

102 from . import _pswindows as _psplatform 

103 from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS # noqa: F401 

104 from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS # noqa: F401 

105 from ._psutil_windows import HIGH_PRIORITY_CLASS # noqa: F401 

106 from ._psutil_windows import IDLE_PRIORITY_CLASS # noqa: F401 

107 from ._psutil_windows import NORMAL_PRIORITY_CLASS # noqa: F401 

108 from ._psutil_windows import REALTIME_PRIORITY_CLASS # noqa: F401 

109 from ._pswindows import CONN_DELETE_TCB # noqa: F401 

110 from ._pswindows import IOPRIO_HIGH # noqa: F401 

111 from ._pswindows import IOPRIO_LOW # noqa: F401 

112 from ._pswindows import IOPRIO_NORMAL # noqa: F401 

113 from ._pswindows import IOPRIO_VERYLOW # noqa: F401 

114 

115elif MACOS: 

116 from . import _psosx as _psplatform 

117 

118elif BSD: 

119 from . import _psbsd as _psplatform 

120 

121elif SUNOS: 

122 from . import _pssunos as _psplatform 

123 from ._pssunos import CONN_BOUND # noqa: F401 

124 from ._pssunos import CONN_IDLE # noqa: F401 

125 

126 # This is public writable API which is read from _pslinux.py and 

127 # _pssunos.py via sys.modules. 

128 PROCFS_PATH = "/proc" 

129 

130elif AIX: 

131 from . import _psaix as _psplatform 

132 

133 # This is public API and it will be retrieved from _pslinux.py 

134 # via sys.modules. 

135 PROCFS_PATH = "/proc" 

136 

137else: # pragma: no cover 

138 msg = f"platform {sys.platform} is not supported" 

139 raise NotImplementedError(msg) 

140 

141 

142# fmt: off 

143__all__ = [ 

144 # exceptions 

145 "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied", 

146 "TimeoutExpired", 

147 

148 # constants 

149 "version_info", "__version__", 

150 

151 "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", 

152 "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", 

153 "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED", 

154 "STATUS_PARKED", 

155 

156 "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", 

157 "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", 

158 "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE", 

159 # "CONN_IDLE", "CONN_BOUND", 

160 

161 "AF_LINK", 

162 

163 "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN", 

164 

165 "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED", 

166 

167 "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX", 

168 "SUNOS", "WINDOWS", "AIX", 

169 

170 # "RLIM_INFINITY", "RLIMIT_AS", "RLIMIT_CORE", "RLIMIT_CPU", "RLIMIT_DATA", 

171 # "RLIMIT_FSIZE", "RLIMIT_LOCKS", "RLIMIT_MEMLOCK", "RLIMIT_NOFILE", 

172 # "RLIMIT_NPROC", "RLIMIT_RSS", "RLIMIT_STACK", "RLIMIT_MSGQUEUE", 

173 # "RLIMIT_NICE", "RLIMIT_RTPRIO", "RLIMIT_RTTIME", "RLIMIT_SIGPENDING", 

174 

175 # classes 

176 "Process", "Popen", 

177 

178 # functions 

179 "pid_exists", "pids", "process_iter", "wait_procs", # proc 

180 "virtual_memory", "swap_memory", # memory 

181 "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu 

182 "cpu_stats", # "cpu_freq", "getloadavg" 

183 "net_io_counters", "net_connections", "net_if_addrs", # network 

184 "net_if_stats", 

185 "disk_io_counters", "disk_partitions", "disk_usage", # disk 

186 # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors 

187 "users", "boot_time", # others 

188] 

189# fmt: on 

190 

191 

192__all__.extend(_psplatform.__extra__all__) 

193 

194# Linux, FreeBSD 

195if hasattr(_psplatform.Process, "rlimit"): 

196 # Populate global namespace with RLIM* constants. 

197 from . import _psutil_posix 

198 

199 _globals = globals() 

200 _name = None 

201 for _name in dir(_psutil_posix): 

202 if _name.startswith('RLIM') and _name.isupper(): 

203 _globals[_name] = getattr(_psutil_posix, _name) 

204 __all__.append(_name) 

205 del _globals, _name 

206 

207AF_LINK = _psplatform.AF_LINK 

208 

209__author__ = "Giampaolo Rodola'" 

210__version__ = "7.0.0" 

211version_info = tuple(int(num) for num in __version__.split('.')) 

212 

213_timer = getattr(time, 'monotonic', time.time) 

214_TOTAL_PHYMEM = None 

215_LOWEST_PID = None 

216_SENTINEL = object() 

217 

218# Sanity check in case the user messed up with psutil installation 

219# or did something weird with sys.path. In this case we might end 

220# up importing a python module using a C extension module which 

221# was compiled for a different version of psutil. 

222# We want to prevent that by failing sooner rather than later. 

223# See: https://github.com/giampaolo/psutil/issues/564 

224if int(__version__.replace('.', '')) != getattr( 

225 _psplatform.cext, 'version', None 

226): 

227 msg = f"version conflict: {_psplatform.cext.__file__!r} C extension " 

228 msg += "module was built for another version of psutil" 

229 if hasattr(_psplatform.cext, 'version'): 

230 v = ".".join(list(str(_psplatform.cext.version))) 

231 msg += f" ({v} instead of {__version__})" 

232 else: 

233 msg += f" (different than {__version__})" 

234 what = getattr( 

235 _psplatform.cext, 

236 "__file__", 

237 "the existing psutil install directory", 

238 ) 

239 msg += f"; you may try to 'pip uninstall psutil', manually remove {what}" 

240 msg += " or clean the virtual env somehow, then reinstall" 

241 raise ImportError(msg) 

242 

243 

244# ===================================================================== 

245# --- Utils 

246# ===================================================================== 

247 

248 

249if hasattr(_psplatform, 'ppid_map'): 

250 # Faster version (Windows and Linux). 

251 _ppid_map = _psplatform.ppid_map 

252else: # pragma: no cover 

253 

254 def _ppid_map(): 

255 """Return a {pid: ppid, ...} dict for all running processes in 

256 one shot. Used to speed up Process.children(). 

257 """ 

258 ret = {} 

259 for pid in pids(): 

260 try: 

261 ret[pid] = _psplatform.Process(pid).ppid() 

262 except (NoSuchProcess, ZombieProcess): 

263 pass 

264 return ret 

265 

266 

267def _pprint_secs(secs): 

268 """Format seconds in a human readable form.""" 

269 now = time.time() 

270 secs_ago = int(now - secs) 

271 fmt = "%H:%M:%S" if secs_ago < 60 * 60 * 24 else "%Y-%m-%d %H:%M:%S" 

272 return datetime.datetime.fromtimestamp(secs).strftime(fmt) 

273 

274 

275def _check_conn_kind(kind): 

276 """Check net_connections()'s `kind` parameter.""" 

277 kinds = tuple(_common.conn_tmap) 

278 if kind not in kinds: 

279 msg = f"invalid kind argument {kind!r}; valid ones are: {kinds}" 

280 raise ValueError(msg) 

281 

282 

283# ===================================================================== 

284# --- Process class 

285# ===================================================================== 

286 

287 

288class Process: 

289 """Represents an OS process with the given PID. 

290 If PID is omitted current process PID (os.getpid()) is used. 

291 Raise NoSuchProcess if PID does not exist. 

292 

293 Note that most of the methods of this class do not make sure that 

294 the PID of the process being queried has been reused. That means 

295 that you may end up retrieving information for another process. 

296 

297 The only exceptions for which process identity is pre-emptively 

298 checked and guaranteed are: 

299 

300 - parent() 

301 - children() 

302 - nice() (set) 

303 - ionice() (set) 

304 - rlimit() (set) 

305 - cpu_affinity (set) 

306 - suspend() 

307 - resume() 

308 - send_signal() 

309 - terminate() 

310 - kill() 

311 

312 To prevent this problem for all other methods you can use 

313 is_running() before querying the process. 

314 """ 

315 

316 def __init__(self, pid=None): 

317 self._init(pid) 

318 

319 def _init(self, pid, _ignore_nsp=False): 

320 if pid is None: 

321 pid = os.getpid() 

322 else: 

323 if pid < 0: 

324 msg = f"pid must be a positive integer (got {pid})" 

325 raise ValueError(msg) 

326 try: 

327 _psplatform.cext.check_pid_range(pid) 

328 except OverflowError as err: 

329 msg = "process PID out of range" 

330 raise NoSuchProcess(pid, msg=msg) from err 

331 

332 self._pid = pid 

333 self._name = None 

334 self._exe = None 

335 self._create_time = None 

336 self._gone = False 

337 self._pid_reused = False 

338 self._hash = None 

339 self._lock = threading.RLock() 

340 # used for caching on Windows only (on POSIX ppid may change) 

341 self._ppid = None 

342 # platform-specific modules define an _psplatform.Process 

343 # implementation class 

344 self._proc = _psplatform.Process(pid) 

345 self._last_sys_cpu_times = None 

346 self._last_proc_cpu_times = None 

347 self._exitcode = _SENTINEL 

348 self._ident = (self.pid, None) 

349 try: 

350 self._ident = self._get_ident() 

351 except AccessDenied: 

352 # This should happen on Windows only, since we use the fast 

353 # create time method. AFAIK, on all other platforms we are 

354 # able to get create time for all PIDs. 

355 pass 

356 except ZombieProcess: 

357 # Zombies can still be queried by this class (although 

358 # not always) and pids() return them so just go on. 

359 pass 

360 except NoSuchProcess: 

361 if not _ignore_nsp: 

362 msg = "process PID not found" 

363 raise NoSuchProcess(pid, msg=msg) from None 

364 self._gone = True 

365 

366 def _get_ident(self): 

367 """Return a (pid, uid) tuple which is supposed to identify a 

368 Process instance univocally over time. The PID alone is not 

369 enough, as it can be assigned to a new process after this one 

370 terminates, so we add process creation time to the mix. We need 

371 this in order to prevent killing the wrong process later on. 

372 This is also known as PID reuse or PID recycling problem. 

373 

374 The reliability of this strategy mostly depends on 

375 create_time() precision, which is 0.01 secs on Linux. The 

376 assumption is that, after a process terminates, the kernel 

377 won't reuse the same PID after such a short period of time 

378 (0.01 secs). Technically this is inherently racy, but 

379 practically it should be good enough. 

380 """ 

381 if WINDOWS: 

382 # Use create_time() fast method in order to speedup 

383 # `process_iter()`. This means we'll get AccessDenied for 

384 # most ADMIN processes, but that's fine since it means 

385 # we'll also get AccessDenied on kill(). 

386 # https://github.com/giampaolo/psutil/issues/2366#issuecomment-2381646555 

387 self._create_time = self._proc.create_time(fast_only=True) 

388 return (self.pid, self._create_time) 

389 else: 

390 return (self.pid, self.create_time()) 

391 

392 def __str__(self): 

393 info = collections.OrderedDict() 

394 info["pid"] = self.pid 

395 if self._name: 

396 info['name'] = self._name 

397 with self.oneshot(): 

398 if self._pid_reused: 

399 info["status"] = "terminated + PID reused" 

400 else: 

401 try: 

402 info["name"] = self.name() 

403 info["status"] = self.status() 

404 except ZombieProcess: 

405 info["status"] = "zombie" 

406 except NoSuchProcess: 

407 info["status"] = "terminated" 

408 except AccessDenied: 

409 pass 

410 

411 if self._exitcode not in {_SENTINEL, None}: 

412 info["exitcode"] = self._exitcode 

413 if self._create_time is not None: 

414 info['started'] = _pprint_secs(self._create_time) 

415 

416 return "{}.{}({})".format( 

417 self.__class__.__module__, 

418 self.__class__.__name__, 

419 ", ".join([f"{k}={v!r}" for k, v in info.items()]), 

420 ) 

421 

422 __repr__ = __str__ 

423 

424 def __eq__(self, other): 

425 # Test for equality with another Process object based 

426 # on PID and creation time. 

427 if not isinstance(other, Process): 

428 return NotImplemented 

429 if OPENBSD or NETBSD: # pragma: no cover 

430 # Zombie processes on Open/NetBSD have a creation time of 

431 # 0.0. This covers the case when a process started normally 

432 # (so it has a ctime), then it turned into a zombie. It's 

433 # important to do this because is_running() depends on 

434 # __eq__. 

435 pid1, ident1 = self._ident 

436 pid2, ident2 = other._ident 

437 if pid1 == pid2: 

438 if ident1 and not ident2: 

439 try: 

440 return self.status() == STATUS_ZOMBIE 

441 except Error: 

442 pass 

443 return self._ident == other._ident 

444 

445 def __ne__(self, other): 

446 return not self == other 

447 

448 def __hash__(self): 

449 if self._hash is None: 

450 self._hash = hash(self._ident) 

451 return self._hash 

452 

453 def _raise_if_pid_reused(self): 

454 """Raises NoSuchProcess in case process PID has been reused.""" 

455 if self._pid_reused or (not self.is_running() and self._pid_reused): 

456 # We may directly raise NSP in here already if PID is just 

457 # not running, but I prefer NSP to be raised naturally by 

458 # the actual Process API call. This way unit tests will tell 

459 # us if the API is broken (aka don't raise NSP when it 

460 # should). We also remain consistent with all other "get" 

461 # APIs which don't use _raise_if_pid_reused(). 

462 msg = "process no longer exists and its PID has been reused" 

463 raise NoSuchProcess(self.pid, self._name, msg=msg) 

464 

465 @property 

466 def pid(self): 

467 """The process PID.""" 

468 return self._pid 

469 

470 # --- utility methods 

471 

472 @contextlib.contextmanager 

473 def oneshot(self): 

474 """Utility context manager which considerably speeds up the 

475 retrieval of multiple process information at the same time. 

476 

477 Internally different process info (e.g. name, ppid, uids, 

478 gids, ...) may be fetched by using the same routine, but 

479 only one information is returned and the others are discarded. 

480 When using this context manager the internal routine is 

481 executed once (in the example below on name()) and the 

482 other info are cached. 

483 

484 The cache is cleared when exiting the context manager block. 

485 The advice is to use this every time you retrieve more than 

486 one information about the process. If you're lucky, you'll 

487 get a hell of a speedup. 

488 

489 >>> import psutil 

490 >>> p = psutil.Process() 

491 >>> with p.oneshot(): 

492 ... p.name() # collect multiple info 

493 ... p.cpu_times() # return cached value 

494 ... p.cpu_percent() # return cached value 

495 ... p.create_time() # return cached value 

496 ... 

497 >>> 

498 """ 

499 with self._lock: 

500 if hasattr(self, "_cache"): 

501 # NOOP: this covers the use case where the user enters the 

502 # context twice: 

503 # 

504 # >>> with p.oneshot(): 

505 # ... with p.oneshot(): 

506 # ... 

507 # 

508 # Also, since as_dict() internally uses oneshot() 

509 # I expect that the code below will be a pretty common 

510 # "mistake" that the user will make, so let's guard 

511 # against that: 

512 # 

513 # >>> with p.oneshot(): 

514 # ... p.as_dict() 

515 # ... 

516 yield 

517 else: 

518 try: 

519 # cached in case cpu_percent() is used 

520 self.cpu_times.cache_activate(self) 

521 # cached in case memory_percent() is used 

522 self.memory_info.cache_activate(self) 

523 # cached in case parent() is used 

524 self.ppid.cache_activate(self) 

525 # cached in case username() is used 

526 if POSIX: 

527 self.uids.cache_activate(self) 

528 # specific implementation cache 

529 self._proc.oneshot_enter() 

530 yield 

531 finally: 

532 self.cpu_times.cache_deactivate(self) 

533 self.memory_info.cache_deactivate(self) 

534 self.ppid.cache_deactivate(self) 

535 if POSIX: 

536 self.uids.cache_deactivate(self) 

537 self._proc.oneshot_exit() 

538 

539 def as_dict(self, attrs=None, ad_value=None): 

540 """Utility method returning process information as a 

541 hashable dictionary. 

542 If *attrs* is specified it must be a list of strings 

543 reflecting available Process class' attribute names 

544 (e.g. ['cpu_times', 'name']) else all public (read 

545 only) attributes are assumed. 

546 *ad_value* is the value which gets assigned in case 

547 AccessDenied or ZombieProcess exception is raised when 

548 retrieving that particular process information. 

549 """ 

550 valid_names = _as_dict_attrnames 

551 if attrs is not None: 

552 if not isinstance(attrs, (list, tuple, set, frozenset)): 

553 msg = f"invalid attrs type {type(attrs)}" 

554 raise TypeError(msg) 

555 attrs = set(attrs) 

556 invalid_names = attrs - valid_names 

557 if invalid_names: 

558 msg = "invalid attr name{} {}".format( 

559 "s" if len(invalid_names) > 1 else "", 

560 ", ".join(map(repr, invalid_names)), 

561 ) 

562 raise ValueError(msg) 

563 

564 retdict = {} 

565 ls = attrs or valid_names 

566 with self.oneshot(): 

567 for name in ls: 

568 try: 

569 if name == 'pid': 

570 ret = self.pid 

571 else: 

572 meth = getattr(self, name) 

573 ret = meth() 

574 except (AccessDenied, ZombieProcess): 

575 ret = ad_value 

576 except NotImplementedError: 

577 # in case of not implemented functionality (may happen 

578 # on old or exotic systems) we want to crash only if 

579 # the user explicitly asked for that particular attr 

580 if attrs: 

581 raise 

582 continue 

583 retdict[name] = ret 

584 return retdict 

585 

586 def parent(self): 

587 """Return the parent process as a Process object pre-emptively 

588 checking whether PID has been reused. 

589 If no parent is known return None. 

590 """ 

591 lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0] 

592 if self.pid == lowest_pid: 

593 return None 

594 ppid = self.ppid() 

595 if ppid is not None: 

596 ctime = self.create_time() 

597 try: 

598 parent = Process(ppid) 

599 if parent.create_time() <= ctime: 

600 return parent 

601 # ...else ppid has been reused by another process 

602 except NoSuchProcess: 

603 pass 

604 

605 def parents(self): 

606 """Return the parents of this process as a list of Process 

607 instances. If no parents are known return an empty list. 

608 """ 

609 parents = [] 

610 proc = self.parent() 

611 while proc is not None: 

612 parents.append(proc) 

613 proc = proc.parent() 

614 return parents 

615 

616 def is_running(self): 

617 """Return whether this process is running. 

618 

619 It also checks if PID has been reused by another process, in 

620 which case it will remove the process from `process_iter()` 

621 internal cache and return False. 

622 """ 

623 if self._gone or self._pid_reused: 

624 return False 

625 try: 

626 # Checking if PID is alive is not enough as the PID might 

627 # have been reused by another process. Process identity / 

628 # uniqueness over time is guaranteed by (PID + creation 

629 # time) and that is verified in __eq__. 

630 self._pid_reused = self != Process(self.pid) 

631 if self._pid_reused: 

632 _pids_reused.add(self.pid) 

633 raise NoSuchProcess(self.pid) 

634 return True 

635 except ZombieProcess: 

636 # We should never get here as it's already handled in 

637 # Process.__init__; here just for extra safety. 

638 return True 

639 except NoSuchProcess: 

640 self._gone = True 

641 return False 

642 

643 # --- actual API 

644 

645 @memoize_when_activated 

646 def ppid(self): 

647 """The process parent PID. 

648 On Windows the return value is cached after first call. 

649 """ 

650 # On POSIX we don't want to cache the ppid as it may unexpectedly 

651 # change to 1 (init) in case this process turns into a zombie: 

652 # https://github.com/giampaolo/psutil/issues/321 

653 # http://stackoverflow.com/questions/356722/ 

654 

655 # XXX should we check creation time here rather than in 

656 # Process.parent()? 

657 self._raise_if_pid_reused() 

658 if POSIX: 

659 return self._proc.ppid() 

660 else: # pragma: no cover 

661 self._ppid = self._ppid or self._proc.ppid() 

662 return self._ppid 

663 

664 def name(self): 

665 """The process name. The return value is cached after first call.""" 

666 # Process name is only cached on Windows as on POSIX it may 

667 # change, see: 

668 # https://github.com/giampaolo/psutil/issues/692 

669 if WINDOWS and self._name is not None: 

670 return self._name 

671 name = self._proc.name() 

672 if POSIX and len(name) >= 15: 

673 # On UNIX the name gets truncated to the first 15 characters. 

674 # If it matches the first part of the cmdline we return that 

675 # one instead because it's usually more explicative. 

676 # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". 

677 try: 

678 cmdline = self.cmdline() 

679 except (AccessDenied, ZombieProcess): 

680 # Just pass and return the truncated name: it's better 

681 # than nothing. Note: there are actual cases where a 

682 # zombie process can return a name() but not a 

683 # cmdline(), see: 

684 # https://github.com/giampaolo/psutil/issues/2239 

685 pass 

686 else: 

687 if cmdline: 

688 extended_name = os.path.basename(cmdline[0]) 

689 if extended_name.startswith(name): 

690 name = extended_name 

691 self._name = name 

692 self._proc._name = name 

693 return name 

694 

695 def exe(self): 

696 """The process executable as an absolute path. 

697 May also be an empty string. 

698 The return value is cached after first call. 

699 """ 

700 

701 def guess_it(fallback): 

702 # try to guess exe from cmdline[0] in absence of a native 

703 # exe representation 

704 cmdline = self.cmdline() 

705 if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'): 

706 exe = cmdline[0] # the possible exe 

707 # Attempt to guess only in case of an absolute path. 

708 # It is not safe otherwise as the process might have 

709 # changed cwd. 

710 if ( 

711 os.path.isabs(exe) 

712 and os.path.isfile(exe) 

713 and os.access(exe, os.X_OK) 

714 ): 

715 return exe 

716 if isinstance(fallback, AccessDenied): 

717 raise fallback 

718 return fallback 

719 

720 if self._exe is None: 

721 try: 

722 exe = self._proc.exe() 

723 except AccessDenied as err: 

724 return guess_it(fallback=err) 

725 else: 

726 if not exe: 

727 # underlying implementation can legitimately return an 

728 # empty string; if that's the case we don't want to 

729 # raise AD while guessing from the cmdline 

730 try: 

731 exe = guess_it(fallback=exe) 

732 except AccessDenied: 

733 pass 

734 self._exe = exe 

735 return self._exe 

736 

737 def cmdline(self): 

738 """The command line this process has been called with.""" 

739 return self._proc.cmdline() 

740 

741 def status(self): 

742 """The process current status as a STATUS_* constant.""" 

743 try: 

744 return self._proc.status() 

745 except ZombieProcess: 

746 return STATUS_ZOMBIE 

747 

748 def username(self): 

749 """The name of the user that owns the process. 

750 On UNIX this is calculated by using *real* process uid. 

751 """ 

752 if POSIX: 

753 if pwd is None: 

754 # might happen if python was installed from sources 

755 msg = "requires pwd module shipped with standard python" 

756 raise ImportError(msg) 

757 real_uid = self.uids().real 

758 try: 

759 return pwd.getpwuid(real_uid).pw_name 

760 except KeyError: 

761 # the uid can't be resolved by the system 

762 return str(real_uid) 

763 else: 

764 return self._proc.username() 

765 

766 def create_time(self): 

767 """The process creation time as a floating point number 

768 expressed in seconds since the epoch. 

769 The return value is cached after first call. 

770 """ 

771 if self._create_time is None: 

772 self._create_time = self._proc.create_time() 

773 return self._create_time 

774 

775 def cwd(self): 

776 """Process current working directory as an absolute path.""" 

777 return self._proc.cwd() 

778 

779 def nice(self, value=None): 

780 """Get or set process niceness (priority).""" 

781 if value is None: 

782 return self._proc.nice_get() 

783 else: 

784 self._raise_if_pid_reused() 

785 self._proc.nice_set(value) 

786 

787 if POSIX: 

788 

789 @memoize_when_activated 

790 def uids(self): 

791 """Return process UIDs as a (real, effective, saved) 

792 namedtuple. 

793 """ 

794 return self._proc.uids() 

795 

796 def gids(self): 

797 """Return process GIDs as a (real, effective, saved) 

798 namedtuple. 

799 """ 

800 return self._proc.gids() 

801 

802 def terminal(self): 

803 """The terminal associated with this process, if any, 

804 else None. 

805 """ 

806 return self._proc.terminal() 

807 

808 def num_fds(self): 

809 """Return the number of file descriptors opened by this 

810 process (POSIX only). 

811 """ 

812 return self._proc.num_fds() 

813 

814 # Linux, BSD, AIX and Windows only 

815 if hasattr(_psplatform.Process, "io_counters"): 

816 

817 def io_counters(self): 

818 """Return process I/O statistics as a 

819 (read_count, write_count, read_bytes, write_bytes) 

820 namedtuple. 

821 Those are the number of read/write calls performed and the 

822 amount of bytes read and written by the process. 

823 """ 

824 return self._proc.io_counters() 

825 

826 # Linux and Windows 

827 if hasattr(_psplatform.Process, "ionice_get"): 

828 

829 def ionice(self, ioclass=None, value=None): 

830 """Get or set process I/O niceness (priority). 

831 

832 On Linux *ioclass* is one of the IOPRIO_CLASS_* constants. 

833 *value* is a number which goes from 0 to 7. The higher the 

834 value, the lower the I/O priority of the process. 

835 

836 On Windows only *ioclass* is used and it can be set to 2 

837 (normal), 1 (low) or 0 (very low). 

838 

839 Available on Linux and Windows > Vista only. 

840 """ 

841 if ioclass is None: 

842 if value is not None: 

843 msg = "'ioclass' argument must be specified" 

844 raise ValueError(msg) 

845 return self._proc.ionice_get() 

846 else: 

847 self._raise_if_pid_reused() 

848 return self._proc.ionice_set(ioclass, value) 

849 

850 # Linux / FreeBSD only 

851 if hasattr(_psplatform.Process, "rlimit"): 

852 

853 def rlimit(self, resource, limits=None): 

854 """Get or set process resource limits as a (soft, hard) 

855 tuple. 

856 

857 *resource* is one of the RLIMIT_* constants. 

858 *limits* is supposed to be a (soft, hard) tuple. 

859 

860 See "man prlimit" for further info. 

861 Available on Linux and FreeBSD only. 

862 """ 

863 if limits is not None: 

864 self._raise_if_pid_reused() 

865 return self._proc.rlimit(resource, limits) 

866 

867 # Windows, Linux and FreeBSD only 

868 if hasattr(_psplatform.Process, "cpu_affinity_get"): 

869 

870 def cpu_affinity(self, cpus=None): 

871 """Get or set process CPU affinity. 

872 If specified, *cpus* must be a list of CPUs for which you 

873 want to set the affinity (e.g. [0, 1]). 

874 If an empty list is passed, all egible CPUs are assumed 

875 (and set). 

876 (Windows, Linux and BSD only). 

877 """ 

878 if cpus is None: 

879 return sorted(set(self._proc.cpu_affinity_get())) 

880 else: 

881 self._raise_if_pid_reused() 

882 if not cpus: 

883 if hasattr(self._proc, "_get_eligible_cpus"): 

884 cpus = self._proc._get_eligible_cpus() 

885 else: 

886 cpus = tuple(range(len(cpu_times(percpu=True)))) 

887 self._proc.cpu_affinity_set(list(set(cpus))) 

888 

889 # Linux, FreeBSD, SunOS 

890 if hasattr(_psplatform.Process, "cpu_num"): 

891 

892 def cpu_num(self): 

893 """Return what CPU this process is currently running on. 

894 The returned number should be <= psutil.cpu_count() 

895 and <= len(psutil.cpu_percent(percpu=True)). 

896 It may be used in conjunction with 

897 psutil.cpu_percent(percpu=True) to observe the system 

898 workload distributed across CPUs. 

899 """ 

900 return self._proc.cpu_num() 

901 

902 # All platforms has it, but maybe not in the future. 

903 if hasattr(_psplatform.Process, "environ"): 

904 

905 def environ(self): 

906 """The environment variables of the process as a dict. Note: this 

907 might not reflect changes made after the process started. 

908 """ 

909 return self._proc.environ() 

910 

911 if WINDOWS: 

912 

913 def num_handles(self): 

914 """Return the number of handles opened by this process 

915 (Windows only). 

916 """ 

917 return self._proc.num_handles() 

918 

919 def num_ctx_switches(self): 

920 """Return the number of voluntary and involuntary context 

921 switches performed by this process. 

922 """ 

923 return self._proc.num_ctx_switches() 

924 

925 def num_threads(self): 

926 """Return the number of threads used by this process.""" 

927 return self._proc.num_threads() 

928 

929 if hasattr(_psplatform.Process, "threads"): 

930 

931 def threads(self): 

932 """Return threads opened by process as a list of 

933 (id, user_time, system_time) namedtuples representing 

934 thread id and thread CPU times (user/system). 

935 On OpenBSD this method requires root access. 

936 """ 

937 return self._proc.threads() 

938 

939 def children(self, recursive=False): 

940 """Return the children of this process as a list of Process 

941 instances, pre-emptively checking whether PID has been reused. 

942 If *recursive* is True return all the parent descendants. 

943 

944 Example (A == this process): 

945 

946 A ─┐ 

947 

948 ├─ B (child) ─┐ 

949 │ └─ X (grandchild) ─┐ 

950 │ └─ Y (great grandchild) 

951 ├─ C (child) 

952 └─ D (child) 

953 

954 >>> import psutil 

955 >>> p = psutil.Process() 

956 >>> p.children() 

957 B, C, D 

958 >>> p.children(recursive=True) 

959 B, X, Y, C, D 

960 

961 Note that in the example above if process X disappears 

962 process Y won't be listed as the reference to process A 

963 is lost. 

964 """ 

965 self._raise_if_pid_reused() 

966 ppid_map = _ppid_map() 

967 ret = [] 

968 if not recursive: 

969 for pid, ppid in ppid_map.items(): 

970 if ppid == self.pid: 

971 try: 

972 child = Process(pid) 

973 # if child happens to be older than its parent 

974 # (self) it means child's PID has been reused 

975 if self.create_time() <= child.create_time(): 

976 ret.append(child) 

977 except (NoSuchProcess, ZombieProcess): 

978 pass 

979 else: 

980 # Construct a {pid: [child pids]} dict 

981 reverse_ppid_map = collections.defaultdict(list) 

982 for pid, ppid in ppid_map.items(): 

983 reverse_ppid_map[ppid].append(pid) 

984 # Recursively traverse that dict, starting from self.pid, 

985 # such that we only call Process() on actual children 

986 seen = set() 

987 stack = [self.pid] 

988 while stack: 

989 pid = stack.pop() 

990 if pid in seen: 

991 # Since pids can be reused while the ppid_map is 

992 # constructed, there may be rare instances where 

993 # there's a cycle in the recorded process "tree". 

994 continue 

995 seen.add(pid) 

996 for child_pid in reverse_ppid_map[pid]: 

997 try: 

998 child = Process(child_pid) 

999 # if child happens to be older than its parent 

1000 # (self) it means child's PID has been reused 

1001 intime = self.create_time() <= child.create_time() 

1002 if intime: 

1003 ret.append(child) 

1004 stack.append(child_pid) 

1005 except (NoSuchProcess, ZombieProcess): 

1006 pass 

1007 return ret 

1008 

1009 def cpu_percent(self, interval=None): 

1010 """Return a float representing the current process CPU 

1011 utilization as a percentage. 

1012 

1013 When *interval* is 0.0 or None (default) compares process times 

1014 to system CPU times elapsed since last call, returning 

1015 immediately (non-blocking). That means that the first time 

1016 this is called it will return a meaningful 0.0 value. 

1017 

1018 When *interval* is > 0.0 compares process times to system CPU 

1019 times elapsed before and after the interval (blocking). 

1020 

1021 In this case is recommended for accuracy that this function 

1022 be called with at least 0.1 seconds between calls. 

1023 

1024 A value > 100.0 can be returned in case of processes running 

1025 multiple threads on different CPU cores. 

1026 

1027 The returned value is explicitly NOT split evenly between 

1028 all available logical CPUs. This means that a busy loop process 

1029 running on a system with 2 logical CPUs will be reported as 

1030 having 100% CPU utilization instead of 50%. 

1031 

1032 Examples: 

1033 

1034 >>> import psutil 

1035 >>> p = psutil.Process(os.getpid()) 

1036 >>> # blocking 

1037 >>> p.cpu_percent(interval=1) 

1038 2.0 

1039 >>> # non-blocking (percentage since last call) 

1040 >>> p.cpu_percent(interval=None) 

1041 2.9 

1042 >>> 

1043 """ 

1044 blocking = interval is not None and interval > 0.0 

1045 if interval is not None and interval < 0: 

1046 msg = f"interval is not positive (got {interval!r})" 

1047 raise ValueError(msg) 

1048 num_cpus = cpu_count() or 1 

1049 

1050 def timer(): 

1051 return _timer() * num_cpus 

1052 

1053 if blocking: 

1054 st1 = timer() 

1055 pt1 = self._proc.cpu_times() 

1056 time.sleep(interval) 

1057 st2 = timer() 

1058 pt2 = self._proc.cpu_times() 

1059 else: 

1060 st1 = self._last_sys_cpu_times 

1061 pt1 = self._last_proc_cpu_times 

1062 st2 = timer() 

1063 pt2 = self._proc.cpu_times() 

1064 if st1 is None or pt1 is None: 

1065 self._last_sys_cpu_times = st2 

1066 self._last_proc_cpu_times = pt2 

1067 return 0.0 

1068 

1069 delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system) 

1070 delta_time = st2 - st1 

1071 # reset values for next call in case of interval == None 

1072 self._last_sys_cpu_times = st2 

1073 self._last_proc_cpu_times = pt2 

1074 

1075 try: 

1076 # This is the utilization split evenly between all CPUs. 

1077 # E.g. a busy loop process on a 2-CPU-cores system at this 

1078 # point is reported as 50% instead of 100%. 

1079 overall_cpus_percent = (delta_proc / delta_time) * 100 

1080 except ZeroDivisionError: 

1081 # interval was too low 

1082 return 0.0 

1083 else: 

1084 # Note 1: 

1085 # in order to emulate "top" we multiply the value for the num 

1086 # of CPU cores. This way the busy process will be reported as 

1087 # having 100% (or more) usage. 

1088 # 

1089 # Note 2: 

1090 # taskmgr.exe on Windows differs in that it will show 50% 

1091 # instead. 

1092 # 

1093 # Note 3: 

1094 # a percentage > 100 is legitimate as it can result from a 

1095 # process with multiple threads running on different CPU 

1096 # cores (top does the same), see: 

1097 # http://stackoverflow.com/questions/1032357 

1098 # https://github.com/giampaolo/psutil/issues/474 

1099 single_cpu_percent = overall_cpus_percent * num_cpus 

1100 return round(single_cpu_percent, 1) 

1101 

1102 @memoize_when_activated 

1103 def cpu_times(self): 

1104 """Return a (user, system, children_user, children_system) 

1105 namedtuple representing the accumulated process time, in 

1106 seconds. 

1107 This is similar to os.times() but per-process. 

1108 On macOS and Windows children_user and children_system are 

1109 always set to 0. 

1110 """ 

1111 return self._proc.cpu_times() 

1112 

1113 @memoize_when_activated 

1114 def memory_info(self): 

1115 """Return a namedtuple with variable fields depending on the 

1116 platform, representing memory information about the process. 

1117 

1118 The "portable" fields available on all platforms are `rss` and `vms`. 

1119 

1120 All numbers are expressed in bytes. 

1121 """ 

1122 return self._proc.memory_info() 

1123 

1124 def memory_full_info(self): 

1125 """This method returns the same information as memory_info(), 

1126 plus, on some platform (Linux, macOS, Windows), also provides 

1127 additional metrics (USS, PSS and swap). 

1128 The additional metrics provide a better representation of actual 

1129 process memory usage. 

1130 

1131 Namely USS is the memory which is unique to a process and which 

1132 would be freed if the process was terminated right now. 

1133 

1134 It does so by passing through the whole process address. 

1135 As such it usually requires higher user privileges than 

1136 memory_info() and is considerably slower. 

1137 """ 

1138 return self._proc.memory_full_info() 

1139 

1140 def memory_percent(self, memtype="rss"): 

1141 """Compare process memory to total physical system memory and 

1142 calculate process memory utilization as a percentage. 

1143 *memtype* argument is a string that dictates what type of 

1144 process memory you want to compare against (defaults to "rss"). 

1145 The list of available strings can be obtained like this: 

1146 

1147 >>> psutil.Process().memory_info()._fields 

1148 ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss') 

1149 """ 

1150 valid_types = list(_psplatform.pfullmem._fields) 

1151 if memtype not in valid_types: 

1152 msg = ( 

1153 f"invalid memtype {memtype!r}; valid types are" 

1154 f" {tuple(valid_types)!r}" 

1155 ) 

1156 raise ValueError(msg) 

1157 fun = ( 

1158 self.memory_info 

1159 if memtype in _psplatform.pmem._fields 

1160 else self.memory_full_info 

1161 ) 

1162 metrics = fun() 

1163 value = getattr(metrics, memtype) 

1164 

1165 # use cached value if available 

1166 total_phymem = _TOTAL_PHYMEM or virtual_memory().total 

1167 if not total_phymem > 0: 

1168 # we should never get here 

1169 msg = ( 

1170 "can't calculate process memory percent because total physical" 

1171 f" system memory is not positive ({total_phymem!r})" 

1172 ) 

1173 raise ValueError(msg) 

1174 return (value / float(total_phymem)) * 100 

1175 

1176 if hasattr(_psplatform.Process, "memory_maps"): 

1177 

1178 def memory_maps(self, grouped=True): 

1179 """Return process' mapped memory regions as a list of namedtuples 

1180 whose fields are variable depending on the platform. 

1181 

1182 If *grouped* is True the mapped regions with the same 'path' 

1183 are grouped together and the different memory fields are summed. 

1184 

1185 If *grouped* is False every mapped region is shown as a single 

1186 entity and the namedtuple will also include the mapped region's 

1187 address space ('addr') and permission set ('perms'). 

1188 """ 

1189 it = self._proc.memory_maps() 

1190 if grouped: 

1191 d = {} 

1192 for tupl in it: 

1193 path = tupl[2] 

1194 nums = tupl[3:] 

1195 try: 

1196 d[path] = list(map(lambda x, y: x + y, d[path], nums)) 

1197 except KeyError: 

1198 d[path] = nums 

1199 nt = _psplatform.pmmap_grouped 

1200 return [nt(path, *d[path]) for path in d] 

1201 else: 

1202 nt = _psplatform.pmmap_ext 

1203 return [nt(*x) for x in it] 

1204 

1205 def open_files(self): 

1206 """Return files opened by process as a list of 

1207 (path, fd) namedtuples including the absolute file name 

1208 and file descriptor number. 

1209 """ 

1210 return self._proc.open_files() 

1211 

1212 def net_connections(self, kind='inet'): 

1213 """Return socket connections opened by process as a list of 

1214 (fd, family, type, laddr, raddr, status) namedtuples. 

1215 The *kind* parameter filters for connections that match the 

1216 following criteria: 

1217 

1218 +------------+----------------------------------------------------+ 

1219 | Kind Value | Connections using | 

1220 +------------+----------------------------------------------------+ 

1221 | inet | IPv4 and IPv6 | 

1222 | inet4 | IPv4 | 

1223 | inet6 | IPv6 | 

1224 | tcp | TCP | 

1225 | tcp4 | TCP over IPv4 | 

1226 | tcp6 | TCP over IPv6 | 

1227 | udp | UDP | 

1228 | udp4 | UDP over IPv4 | 

1229 | udp6 | UDP over IPv6 | 

1230 | unix | UNIX socket (both UDP and TCP protocols) | 

1231 | all | the sum of all the possible families and protocols | 

1232 +------------+----------------------------------------------------+ 

1233 """ 

1234 _check_conn_kind(kind) 

1235 return self._proc.net_connections(kind) 

1236 

1237 @_common.deprecated_method(replacement="net_connections") 

1238 def connections(self, kind="inet"): 

1239 return self.net_connections(kind=kind) 

1240 

1241 # --- signals 

1242 

1243 if POSIX: 

1244 

1245 def _send_signal(self, sig): 

1246 assert not self.pid < 0, self.pid 

1247 self._raise_if_pid_reused() 

1248 

1249 pid, ppid, name = self.pid, self._ppid, self._name 

1250 if pid == 0: 

1251 # see "man 2 kill" 

1252 msg = ( 

1253 "preventing sending signal to process with PID 0 as it " 

1254 "would affect every process in the process group of the " 

1255 "calling process (os.getpid()) instead of PID 0" 

1256 ) 

1257 raise ValueError(msg) 

1258 try: 

1259 os.kill(pid, sig) 

1260 except ProcessLookupError as err: 

1261 if OPENBSD and pid_exists(pid): 

1262 # We do this because os.kill() lies in case of 

1263 # zombie processes. 

1264 raise ZombieProcess(pid, name, ppid) from err 

1265 self._gone = True 

1266 raise NoSuchProcess(pid, name) from err 

1267 except PermissionError as err: 

1268 raise AccessDenied(pid, name) from err 

1269 

1270 def send_signal(self, sig): 

1271 """Send a signal *sig* to process pre-emptively checking 

1272 whether PID has been reused (see signal module constants) . 

1273 On Windows only SIGTERM is valid and is treated as an alias 

1274 for kill(). 

1275 """ 

1276 if POSIX: 

1277 self._send_signal(sig) 

1278 else: # pragma: no cover 

1279 self._raise_if_pid_reused() 

1280 if sig != signal.SIGTERM and not self.is_running(): 

1281 msg = "process no longer exists" 

1282 raise NoSuchProcess(self.pid, self._name, msg=msg) 

1283 self._proc.send_signal(sig) 

1284 

1285 def suspend(self): 

1286 """Suspend process execution with SIGSTOP pre-emptively checking 

1287 whether PID has been reused. 

1288 On Windows this has the effect of suspending all process threads. 

1289 """ 

1290 if POSIX: 

1291 self._send_signal(signal.SIGSTOP) 

1292 else: # pragma: no cover 

1293 self._raise_if_pid_reused() 

1294 self._proc.suspend() 

1295 

1296 def resume(self): 

1297 """Resume process execution with SIGCONT pre-emptively checking 

1298 whether PID has been reused. 

1299 On Windows this has the effect of resuming all process threads. 

1300 """ 

1301 if POSIX: 

1302 self._send_signal(signal.SIGCONT) 

1303 else: # pragma: no cover 

1304 self._raise_if_pid_reused() 

1305 self._proc.resume() 

1306 

1307 def terminate(self): 

1308 """Terminate the process with SIGTERM pre-emptively checking 

1309 whether PID has been reused. 

1310 On Windows this is an alias for kill(). 

1311 """ 

1312 if POSIX: 

1313 self._send_signal(signal.SIGTERM) 

1314 else: # pragma: no cover 

1315 self._raise_if_pid_reused() 

1316 self._proc.kill() 

1317 

1318 def kill(self): 

1319 """Kill the current process with SIGKILL pre-emptively checking 

1320 whether PID has been reused. 

1321 """ 

1322 if POSIX: 

1323 self._send_signal(signal.SIGKILL) 

1324 else: # pragma: no cover 

1325 self._raise_if_pid_reused() 

1326 self._proc.kill() 

1327 

1328 def wait(self, timeout=None): 

1329 """Wait for process to terminate and, if process is a children 

1330 of os.getpid(), also return its exit code, else None. 

1331 On Windows there's no such limitation (exit code is always 

1332 returned). 

1333 

1334 If the process is already terminated immediately return None 

1335 instead of raising NoSuchProcess. 

1336 

1337 If *timeout* (in seconds) is specified and process is still 

1338 alive raise TimeoutExpired. 

1339 

1340 To wait for multiple Process(es) use psutil.wait_procs(). 

1341 """ 

1342 if timeout is not None and not timeout >= 0: 

1343 msg = "timeout must be a positive integer" 

1344 raise ValueError(msg) 

1345 if self._exitcode is not _SENTINEL: 

1346 return self._exitcode 

1347 self._exitcode = self._proc.wait(timeout) 

1348 return self._exitcode 

1349 

1350 

1351# The valid attr names which can be processed by Process.as_dict(). 

1352# fmt: off 

1353_as_dict_attrnames = { 

1354 x for x in dir(Process) if not x.startswith("_") and x not in 

1355 {'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', 

1356 'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit', 

1357 'connections', 'oneshot'} 

1358} 

1359# fmt: on 

1360 

1361 

1362# ===================================================================== 

1363# --- Popen class 

1364# ===================================================================== 

1365 

1366 

1367class Popen(Process): 

1368 """Same as subprocess.Popen, but in addition it provides all 

1369 psutil.Process methods in a single class. 

1370 For the following methods which are common to both classes, psutil 

1371 implementation takes precedence: 

1372 

1373 * send_signal() 

1374 * terminate() 

1375 * kill() 

1376 

1377 This is done in order to avoid killing another process in case its 

1378 PID has been reused, fixing BPO-6973. 

1379 

1380 >>> import psutil 

1381 >>> from subprocess import PIPE 

1382 >>> p = psutil.Popen(["python", "-c", "print 'hi'"], stdout=PIPE) 

1383 >>> p.name() 

1384 'python' 

1385 >>> p.uids() 

1386 user(real=1000, effective=1000, saved=1000) 

1387 >>> p.username() 

1388 'giampaolo' 

1389 >>> p.communicate() 

1390 ('hi', None) 

1391 >>> p.terminate() 

1392 >>> p.wait(timeout=2) 

1393 0 

1394 >>> 

1395 """ 

1396 

1397 def __init__(self, *args, **kwargs): 

1398 # Explicitly avoid to raise NoSuchProcess in case the process 

1399 # spawned by subprocess.Popen terminates too quickly, see: 

1400 # https://github.com/giampaolo/psutil/issues/193 

1401 self.__subproc = subprocess.Popen(*args, **kwargs) 

1402 self._init(self.__subproc.pid, _ignore_nsp=True) 

1403 

1404 def __dir__(self): 

1405 return sorted(set(dir(Popen) + dir(subprocess.Popen))) 

1406 

1407 def __enter__(self): 

1408 if hasattr(self.__subproc, '__enter__'): 

1409 self.__subproc.__enter__() 

1410 return self 

1411 

1412 def __exit__(self, *args, **kwargs): 

1413 if hasattr(self.__subproc, '__exit__'): 

1414 return self.__subproc.__exit__(*args, **kwargs) 

1415 else: 

1416 if self.stdout: 

1417 self.stdout.close() 

1418 if self.stderr: 

1419 self.stderr.close() 

1420 try: 

1421 # Flushing a BufferedWriter may raise an error. 

1422 if self.stdin: 

1423 self.stdin.close() 

1424 finally: 

1425 # Wait for the process to terminate, to avoid zombies. 

1426 self.wait() 

1427 

1428 def __getattribute__(self, name): 

1429 try: 

1430 return object.__getattribute__(self, name) 

1431 except AttributeError: 

1432 try: 

1433 return object.__getattribute__(self.__subproc, name) 

1434 except AttributeError: 

1435 msg = f"{self.__class__!r} has no attribute {name!r}" 

1436 raise AttributeError(msg) from None 

1437 

1438 def wait(self, timeout=None): 

1439 if self.__subproc.returncode is not None: 

1440 return self.__subproc.returncode 

1441 ret = super().wait(timeout) 

1442 self.__subproc.returncode = ret 

1443 return ret 

1444 

1445 

1446# ===================================================================== 

1447# --- system processes related functions 

1448# ===================================================================== 

1449 

1450 

1451def pids(): 

1452 """Return a list of current running PIDs.""" 

1453 global _LOWEST_PID 

1454 ret = sorted(_psplatform.pids()) 

1455 _LOWEST_PID = ret[0] 

1456 return ret 

1457 

1458 

1459def pid_exists(pid): 

1460 """Return True if given PID exists in the current process list. 

1461 This is faster than doing "pid in psutil.pids()" and 

1462 should be preferred. 

1463 """ 

1464 if pid < 0: 

1465 return False 

1466 elif pid == 0 and POSIX: 

1467 # On POSIX we use os.kill() to determine PID existence. 

1468 # According to "man 2 kill" PID 0 has a special meaning 

1469 # though: it refers to <<every process in the process 

1470 # group of the calling process>> and that is not we want 

1471 # to do here. 

1472 return pid in pids() 

1473 else: 

1474 return _psplatform.pid_exists(pid) 

1475 

1476 

1477_pmap = {} 

1478_pids_reused = set() 

1479 

1480 

1481def process_iter(attrs=None, ad_value=None): 

1482 """Return a generator yielding a Process instance for all 

1483 running processes. 

1484 

1485 Every new Process instance is only created once and then cached 

1486 into an internal table which is updated every time this is used. 

1487 Cache can optionally be cleared via `process_iter.clear_cache()`. 

1488 

1489 The sorting order in which processes are yielded is based on 

1490 their PIDs. 

1491 

1492 *attrs* and *ad_value* have the same meaning as in 

1493 Process.as_dict(). If *attrs* is specified as_dict() is called 

1494 and the resulting dict is stored as a 'info' attribute attached 

1495 to returned Process instance. 

1496 If *attrs* is an empty list it will retrieve all process info 

1497 (slow). 

1498 """ 

1499 global _pmap 

1500 

1501 def add(pid): 

1502 proc = Process(pid) 

1503 pmap[proc.pid] = proc 

1504 return proc 

1505 

1506 def remove(pid): 

1507 pmap.pop(pid, None) 

1508 

1509 pmap = _pmap.copy() 

1510 a = set(pids()) 

1511 b = set(pmap.keys()) 

1512 new_pids = a - b 

1513 gone_pids = b - a 

1514 for pid in gone_pids: 

1515 remove(pid) 

1516 while _pids_reused: 

1517 pid = _pids_reused.pop() 

1518 debug(f"refreshing Process instance for reused PID {pid}") 

1519 remove(pid) 

1520 try: 

1521 ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items())) 

1522 for pid, proc in ls: 

1523 try: 

1524 if proc is None: # new process 

1525 proc = add(pid) 

1526 if attrs is not None: 

1527 proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value) 

1528 yield proc 

1529 except NoSuchProcess: 

1530 remove(pid) 

1531 finally: 

1532 _pmap = pmap 

1533 

1534 

1535process_iter.cache_clear = lambda: _pmap.clear() # noqa: PLW0108 

1536process_iter.cache_clear.__doc__ = "Clear process_iter() internal cache." 

1537 

1538 

1539def wait_procs(procs, timeout=None, callback=None): 

1540 """Convenience function which waits for a list of processes to 

1541 terminate. 

1542 

1543 Return a (gone, alive) tuple indicating which processes 

1544 are gone and which ones are still alive. 

1545 

1546 The gone ones will have a new *returncode* attribute indicating 

1547 process exit status (may be None). 

1548 

1549 *callback* is a function which gets called every time a process 

1550 terminates (a Process instance is passed as callback argument). 

1551 

1552 Function will return as soon as all processes terminate or when 

1553 *timeout* occurs. 

1554 Differently from Process.wait() it will not raise TimeoutExpired if 

1555 *timeout* occurs. 

1556 

1557 Typical use case is: 

1558 

1559 - send SIGTERM to a list of processes 

1560 - give them some time to terminate 

1561 - send SIGKILL to those ones which are still alive 

1562 

1563 Example: 

1564 

1565 >>> def on_terminate(proc): 

1566 ... print("process {} terminated".format(proc)) 

1567 ... 

1568 >>> for p in procs: 

1569 ... p.terminate() 

1570 ... 

1571 >>> gone, alive = wait_procs(procs, timeout=3, callback=on_terminate) 

1572 >>> for p in alive: 

1573 ... p.kill() 

1574 """ 

1575 

1576 def check_gone(proc, timeout): 

1577 try: 

1578 returncode = proc.wait(timeout=timeout) 

1579 except (TimeoutExpired, subprocess.TimeoutExpired): 

1580 pass 

1581 else: 

1582 if returncode is not None or not proc.is_running(): 

1583 # Set new Process instance attribute. 

1584 proc.returncode = returncode 

1585 gone.add(proc) 

1586 if callback is not None: 

1587 callback(proc) 

1588 

1589 if timeout is not None and not timeout >= 0: 

1590 msg = f"timeout must be a positive integer, got {timeout}" 

1591 raise ValueError(msg) 

1592 gone = set() 

1593 alive = set(procs) 

1594 if callback is not None and not callable(callback): 

1595 msg = f"callback {callback!r} is not a callable" 

1596 raise TypeError(msg) 

1597 if timeout is not None: 

1598 deadline = _timer() + timeout 

1599 

1600 while alive: 

1601 if timeout is not None and timeout <= 0: 

1602 break 

1603 for proc in alive: 

1604 # Make sure that every complete iteration (all processes) 

1605 # will last max 1 sec. 

1606 # We do this because we don't want to wait too long on a 

1607 # single process: in case it terminates too late other 

1608 # processes may disappear in the meantime and their PID 

1609 # reused. 

1610 max_timeout = 1.0 / len(alive) 

1611 if timeout is not None: 

1612 timeout = min((deadline - _timer()), max_timeout) 

1613 if timeout <= 0: 

1614 break 

1615 check_gone(proc, timeout) 

1616 else: 

1617 check_gone(proc, max_timeout) 

1618 alive = alive - gone # noqa: PLR6104 

1619 

1620 if alive: 

1621 # Last attempt over processes survived so far. 

1622 # timeout == 0 won't make this function wait any further. 

1623 for proc in alive: 

1624 check_gone(proc, 0) 

1625 alive = alive - gone # noqa: PLR6104 

1626 

1627 return (list(gone), list(alive)) 

1628 

1629 

1630# ===================================================================== 

1631# --- CPU related functions 

1632# ===================================================================== 

1633 

1634 

1635def cpu_count(logical=True): 

1636 """Return the number of logical CPUs in the system (same as 

1637 os.cpu_count()). 

1638 

1639 If *logical* is False return the number of physical cores only 

1640 (e.g. hyper thread CPUs are excluded). 

1641 

1642 Return None if undetermined. 

1643 

1644 The return value is cached after first call. 

1645 If desired cache can be cleared like this: 

1646 

1647 >>> psutil.cpu_count.cache_clear() 

1648 """ 

1649 if logical: 

1650 ret = _psplatform.cpu_count_logical() 

1651 else: 

1652 ret = _psplatform.cpu_count_cores() 

1653 if ret is not None and ret < 1: 

1654 ret = None 

1655 return ret 

1656 

1657 

1658def cpu_times(percpu=False): 

1659 """Return system-wide CPU times as a namedtuple. 

1660 Every CPU time represents the seconds the CPU has spent in the 

1661 given mode. The namedtuple's fields availability varies depending on the 

1662 platform: 

1663 

1664 - user 

1665 - system 

1666 - idle 

1667 - nice (UNIX) 

1668 - iowait (Linux) 

1669 - irq (Linux, FreeBSD) 

1670 - softirq (Linux) 

1671 - steal (Linux >= 2.6.11) 

1672 - guest (Linux >= 2.6.24) 

1673 - guest_nice (Linux >= 3.2.0) 

1674 

1675 When *percpu* is True return a list of namedtuples for each CPU. 

1676 First element of the list refers to first CPU, second element 

1677 to second CPU and so on. 

1678 The order of the list is consistent across calls. 

1679 """ 

1680 if not percpu: 

1681 return _psplatform.cpu_times() 

1682 else: 

1683 return _psplatform.per_cpu_times() 

1684 

1685 

1686try: 

1687 _last_cpu_times = {threading.current_thread().ident: cpu_times()} 

1688except Exception: # noqa: BLE001 

1689 # Don't want to crash at import time. 

1690 _last_cpu_times = {} 

1691 

1692try: 

1693 _last_per_cpu_times = { 

1694 threading.current_thread().ident: cpu_times(percpu=True) 

1695 } 

1696except Exception: # noqa: BLE001 

1697 # Don't want to crash at import time. 

1698 _last_per_cpu_times = {} 

1699 

1700 

1701def _cpu_tot_time(times): 

1702 """Given a cpu_time() ntuple calculates the total CPU time 

1703 (including idle time). 

1704 """ 

1705 tot = sum(times) 

1706 if LINUX: 

1707 # On Linux guest times are already accounted in "user" or 

1708 # "nice" times, so we subtract them from total. 

1709 # Htop does the same. References: 

1710 # https://github.com/giampaolo/psutil/pull/940 

1711 # http://unix.stackexchange.com/questions/178045 

1712 # https://github.com/torvalds/linux/blob/ 

1713 # 447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/ 

1714 # cputime.c#L158 

1715 tot -= getattr(times, "guest", 0) # Linux 2.6.24+ 

1716 tot -= getattr(times, "guest_nice", 0) # Linux 3.2.0+ 

1717 return tot 

1718 

1719 

1720def _cpu_busy_time(times): 

1721 """Given a cpu_time() ntuple calculates the busy CPU time. 

1722 We do so by subtracting all idle CPU times. 

1723 """ 

1724 busy = _cpu_tot_time(times) 

1725 busy -= times.idle 

1726 # Linux: "iowait" is time during which the CPU does not do anything 

1727 # (waits for IO to complete). On Linux IO wait is *not* accounted 

1728 # in "idle" time so we subtract it. Htop does the same. 

1729 # References: 

1730 # https://github.com/torvalds/linux/blob/ 

1731 # 447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/cputime.c#L244 

1732 busy -= getattr(times, "iowait", 0) 

1733 return busy 

1734 

1735 

1736def _cpu_times_deltas(t1, t2): 

1737 assert t1._fields == t2._fields, (t1, t2) 

1738 field_deltas = [] 

1739 for field in _psplatform.scputimes._fields: 

1740 field_delta = getattr(t2, field) - getattr(t1, field) 

1741 # CPU times are always supposed to increase over time 

1742 # or at least remain the same and that's because time 

1743 # cannot go backwards. 

1744 # Surprisingly sometimes this might not be the case (at 

1745 # least on Windows and Linux), see: 

1746 # https://github.com/giampaolo/psutil/issues/392 

1747 # https://github.com/giampaolo/psutil/issues/645 

1748 # https://github.com/giampaolo/psutil/issues/1210 

1749 # Trim negative deltas to zero to ignore decreasing fields. 

1750 # top does the same. Reference: 

1751 # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063 

1752 field_delta = max(0, field_delta) 

1753 field_deltas.append(field_delta) 

1754 return _psplatform.scputimes(*field_deltas) 

1755 

1756 

1757def cpu_percent(interval=None, percpu=False): 

1758 """Return a float representing the current system-wide CPU 

1759 utilization as a percentage. 

1760 

1761 When *interval* is > 0.0 compares system CPU times elapsed before 

1762 and after the interval (blocking). 

1763 

1764 When *interval* is 0.0 or None compares system CPU times elapsed 

1765 since last call or module import, returning immediately (non 

1766 blocking). That means the first time this is called it will 

1767 return a meaningless 0.0 value which you should ignore. 

1768 In this case is recommended for accuracy that this function be 

1769 called with at least 0.1 seconds between calls. 

1770 

1771 When *percpu* is True returns a list of floats representing the 

1772 utilization as a percentage for each CPU. 

1773 First element of the list refers to first CPU, second element 

1774 to second CPU and so on. 

1775 The order of the list is consistent across calls. 

1776 

1777 Examples: 

1778 

1779 >>> # blocking, system-wide 

1780 >>> psutil.cpu_percent(interval=1) 

1781 2.0 

1782 >>> 

1783 >>> # blocking, per-cpu 

1784 >>> psutil.cpu_percent(interval=1, percpu=True) 

1785 [2.0, 1.0] 

1786 >>> 

1787 >>> # non-blocking (percentage since last call) 

1788 >>> psutil.cpu_percent(interval=None) 

1789 2.9 

1790 >>> 

1791 """ 

1792 tid = threading.current_thread().ident 

1793 blocking = interval is not None and interval > 0.0 

1794 if interval is not None and interval < 0: 

1795 msg = f"interval is not positive (got {interval})" 

1796 raise ValueError(msg) 

1797 

1798 def calculate(t1, t2): 

1799 times_delta = _cpu_times_deltas(t1, t2) 

1800 all_delta = _cpu_tot_time(times_delta) 

1801 busy_delta = _cpu_busy_time(times_delta) 

1802 

1803 try: 

1804 busy_perc = (busy_delta / all_delta) * 100 

1805 except ZeroDivisionError: 

1806 return 0.0 

1807 else: 

1808 return round(busy_perc, 1) 

1809 

1810 # system-wide usage 

1811 if not percpu: 

1812 if blocking: 

1813 t1 = cpu_times() 

1814 time.sleep(interval) 

1815 else: 

1816 t1 = _last_cpu_times.get(tid) or cpu_times() 

1817 _last_cpu_times[tid] = cpu_times() 

1818 return calculate(t1, _last_cpu_times[tid]) 

1819 # per-cpu usage 

1820 else: 

1821 ret = [] 

1822 if blocking: 

1823 tot1 = cpu_times(percpu=True) 

1824 time.sleep(interval) 

1825 else: 

1826 tot1 = _last_per_cpu_times.get(tid) or cpu_times(percpu=True) 

1827 _last_per_cpu_times[tid] = cpu_times(percpu=True) 

1828 for t1, t2 in zip(tot1, _last_per_cpu_times[tid]): 

1829 ret.append(calculate(t1, t2)) 

1830 return ret 

1831 

1832 

1833# Use a separate dict for cpu_times_percent(), so it's independent from 

1834# cpu_percent() and they can both be used within the same program. 

1835_last_cpu_times_2 = _last_cpu_times.copy() 

1836_last_per_cpu_times_2 = _last_per_cpu_times.copy() 

1837 

1838 

1839def cpu_times_percent(interval=None, percpu=False): 

1840 """Same as cpu_percent() but provides utilization percentages 

1841 for each specific CPU time as is returned by cpu_times(). 

1842 For instance, on Linux we'll get: 

1843 

1844 >>> cpu_times_percent() 

1845 cpupercent(user=4.8, nice=0.0, system=4.8, idle=90.5, iowait=0.0, 

1846 irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) 

1847 >>> 

1848 

1849 *interval* and *percpu* arguments have the same meaning as in 

1850 cpu_percent(). 

1851 """ 

1852 tid = threading.current_thread().ident 

1853 blocking = interval is not None and interval > 0.0 

1854 if interval is not None and interval < 0: 

1855 msg = f"interval is not positive (got {interval!r})" 

1856 raise ValueError(msg) 

1857 

1858 def calculate(t1, t2): 

1859 nums = [] 

1860 times_delta = _cpu_times_deltas(t1, t2) 

1861 all_delta = _cpu_tot_time(times_delta) 

1862 # "scale" is the value to multiply each delta with to get percentages. 

1863 # We use "max" to avoid division by zero (if all_delta is 0, then all 

1864 # fields are 0 so percentages will be 0 too. all_delta cannot be a 

1865 # fraction because cpu times are integers) 

1866 scale = 100.0 / max(1, all_delta) 

1867 for field_delta in times_delta: 

1868 field_perc = field_delta * scale 

1869 field_perc = round(field_perc, 1) 

1870 # make sure we don't return negative values or values over 100% 

1871 field_perc = min(max(0.0, field_perc), 100.0) 

1872 nums.append(field_perc) 

1873 return _psplatform.scputimes(*nums) 

1874 

1875 # system-wide usage 

1876 if not percpu: 

1877 if blocking: 

1878 t1 = cpu_times() 

1879 time.sleep(interval) 

1880 else: 

1881 t1 = _last_cpu_times_2.get(tid) or cpu_times() 

1882 _last_cpu_times_2[tid] = cpu_times() 

1883 return calculate(t1, _last_cpu_times_2[tid]) 

1884 # per-cpu usage 

1885 else: 

1886 ret = [] 

1887 if blocking: 

1888 tot1 = cpu_times(percpu=True) 

1889 time.sleep(interval) 

1890 else: 

1891 tot1 = _last_per_cpu_times_2.get(tid) or cpu_times(percpu=True) 

1892 _last_per_cpu_times_2[tid] = cpu_times(percpu=True) 

1893 for t1, t2 in zip(tot1, _last_per_cpu_times_2[tid]): 

1894 ret.append(calculate(t1, t2)) 

1895 return ret 

1896 

1897 

1898def cpu_stats(): 

1899 """Return CPU statistics.""" 

1900 return _psplatform.cpu_stats() 

1901 

1902 

1903if hasattr(_psplatform, "cpu_freq"): 

1904 

1905 def cpu_freq(percpu=False): 

1906 """Return CPU frequency as a namedtuple including current, 

1907 min and max frequency expressed in Mhz. 

1908 

1909 If *percpu* is True and the system supports per-cpu frequency 

1910 retrieval (Linux only) a list of frequencies is returned for 

1911 each CPU. If not a list with one element is returned. 

1912 """ 

1913 ret = _psplatform.cpu_freq() 

1914 if percpu: 

1915 return ret 

1916 else: 

1917 num_cpus = float(len(ret)) 

1918 if num_cpus == 0: 

1919 return None 

1920 elif num_cpus == 1: 

1921 return ret[0] 

1922 else: 

1923 currs, mins, maxs = 0.0, 0.0, 0.0 

1924 set_none = False 

1925 for cpu in ret: 

1926 currs += cpu.current 

1927 # On Linux if /proc/cpuinfo is used min/max are set 

1928 # to None. 

1929 if LINUX and cpu.min is None: 

1930 set_none = True 

1931 continue 

1932 mins += cpu.min 

1933 maxs += cpu.max 

1934 

1935 current = currs / num_cpus 

1936 

1937 if set_none: 

1938 min_ = max_ = None 

1939 else: 

1940 min_ = mins / num_cpus 

1941 max_ = maxs / num_cpus 

1942 

1943 return _common.scpufreq(current, min_, max_) 

1944 

1945 __all__.append("cpu_freq") 

1946 

1947 

1948if hasattr(os, "getloadavg") or hasattr(_psplatform, "getloadavg"): 

1949 # Perform this hasattr check once on import time to either use the 

1950 # platform based code or proxy straight from the os module. 

1951 if hasattr(os, "getloadavg"): 

1952 getloadavg = os.getloadavg 

1953 else: 

1954 getloadavg = _psplatform.getloadavg 

1955 

1956 __all__.append("getloadavg") 

1957 

1958 

1959# ===================================================================== 

1960# --- system memory related functions 

1961# ===================================================================== 

1962 

1963 

1964def virtual_memory(): 

1965 """Return statistics about system memory usage as a namedtuple 

1966 including the following fields, expressed in bytes: 

1967 

1968 - total: 

1969 total physical memory available. 

1970 

1971 - available: 

1972 the memory that can be given instantly to processes without the 

1973 system going into swap. 

1974 This is calculated by summing different memory values depending 

1975 on the platform and it is supposed to be used to monitor actual 

1976 memory usage in a cross platform fashion. 

1977 

1978 - percent: 

1979 the percentage usage calculated as (total - available) / total * 100 

1980 

1981 - used: 

1982 memory used, calculated differently depending on the platform and 

1983 designed for informational purposes only: 

1984 macOS: active + wired 

1985 BSD: active + wired + cached 

1986 Linux: total - free 

1987 

1988 - free: 

1989 memory not being used at all (zeroed) that is readily available; 

1990 note that this doesn't reflect the actual memory available 

1991 (use 'available' instead) 

1992 

1993 Platform-specific fields: 

1994 

1995 - active (UNIX): 

1996 memory currently in use or very recently used, and so it is in RAM. 

1997 

1998 - inactive (UNIX): 

1999 memory that is marked as not used. 

2000 

2001 - buffers (BSD, Linux): 

2002 cache for things like file system metadata. 

2003 

2004 - cached (BSD, macOS): 

2005 cache for various things. 

2006 

2007 - wired (macOS, BSD): 

2008 memory that is marked to always stay in RAM. It is never moved to disk. 

2009 

2010 - shared (BSD): 

2011 memory that may be simultaneously accessed by multiple processes. 

2012 

2013 The sum of 'used' and 'available' does not necessarily equal total. 

2014 On Windows 'available' and 'free' are the same. 

2015 """ 

2016 global _TOTAL_PHYMEM 

2017 ret = _psplatform.virtual_memory() 

2018 # cached for later use in Process.memory_percent() 

2019 _TOTAL_PHYMEM = ret.total 

2020 return ret 

2021 

2022 

2023def swap_memory(): 

2024 """Return system swap memory statistics as a namedtuple including 

2025 the following fields: 

2026 

2027 - total: total swap memory in bytes 

2028 - used: used swap memory in bytes 

2029 - free: free swap memory in bytes 

2030 - percent: the percentage usage 

2031 - sin: no. of bytes the system has swapped in from disk (cumulative) 

2032 - sout: no. of bytes the system has swapped out from disk (cumulative) 

2033 

2034 'sin' and 'sout' on Windows are meaningless and always set to 0. 

2035 """ 

2036 return _psplatform.swap_memory() 

2037 

2038 

2039# ===================================================================== 

2040# --- disks/partitions related functions 

2041# ===================================================================== 

2042 

2043 

2044def disk_usage(path): 

2045 """Return disk usage statistics about the given *path* as a 

2046 namedtuple including total, used and free space expressed in bytes 

2047 plus the percentage usage. 

2048 """ 

2049 return _psplatform.disk_usage(path) 

2050 

2051 

2052def disk_partitions(all=False): 

2053 """Return mounted partitions as a list of 

2054 (device, mountpoint, fstype, opts) namedtuple. 

2055 'opts' field is a raw string separated by commas indicating mount 

2056 options which may vary depending on the platform. 

2057 

2058 If *all* parameter is False return physical devices only and ignore 

2059 all others. 

2060 """ 

2061 return _psplatform.disk_partitions(all) 

2062 

2063 

2064def disk_io_counters(perdisk=False, nowrap=True): 

2065 """Return system disk I/O statistics as a namedtuple including 

2066 the following fields: 

2067 

2068 - read_count: number of reads 

2069 - write_count: number of writes 

2070 - read_bytes: number of bytes read 

2071 - write_bytes: number of bytes written 

2072 - read_time: time spent reading from disk (in ms) 

2073 - write_time: time spent writing to disk (in ms) 

2074 

2075 Platform specific: 

2076 

2077 - busy_time: (Linux, FreeBSD) time spent doing actual I/Os (in ms) 

2078 - read_merged_count (Linux): number of merged reads 

2079 - write_merged_count (Linux): number of merged writes 

2080 

2081 If *perdisk* is True return the same information for every 

2082 physical disk installed on the system as a dictionary 

2083 with partition names as the keys and the namedtuple 

2084 described above as the values. 

2085 

2086 If *nowrap* is True it detects and adjust the numbers which overflow 

2087 and wrap (restart from 0) and add "old value" to "new value" so that 

2088 the returned numbers will always be increasing or remain the same, 

2089 but never decrease. 

2090 "disk_io_counters.cache_clear()" can be used to invalidate the 

2091 cache. 

2092 

2093 On recent Windows versions 'diskperf -y' command may need to be 

2094 executed first otherwise this function won't find any disk. 

2095 """ 

2096 kwargs = dict(perdisk=perdisk) if LINUX else {} 

2097 rawdict = _psplatform.disk_io_counters(**kwargs) 

2098 if not rawdict: 

2099 return {} if perdisk else None 

2100 if nowrap: 

2101 rawdict = _wrap_numbers(rawdict, 'psutil.disk_io_counters') 

2102 nt = getattr(_psplatform, "sdiskio", _common.sdiskio) 

2103 if perdisk: 

2104 for disk, fields in rawdict.items(): 

2105 rawdict[disk] = nt(*fields) 

2106 return rawdict 

2107 else: 

2108 return nt(*(sum(x) for x in zip(*rawdict.values()))) 

2109 

2110 

2111disk_io_counters.cache_clear = functools.partial( 

2112 _wrap_numbers.cache_clear, 'psutil.disk_io_counters' 

2113) 

2114disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" 

2115 

2116 

2117# ===================================================================== 

2118# --- network related functions 

2119# ===================================================================== 

2120 

2121 

2122def net_io_counters(pernic=False, nowrap=True): 

2123 """Return network I/O statistics as a namedtuple including 

2124 the following fields: 

2125 

2126 - bytes_sent: number of bytes sent 

2127 - bytes_recv: number of bytes received 

2128 - packets_sent: number of packets sent 

2129 - packets_recv: number of packets received 

2130 - errin: total number of errors while receiving 

2131 - errout: total number of errors while sending 

2132 - dropin: total number of incoming packets which were dropped 

2133 - dropout: total number of outgoing packets which were dropped 

2134 (always 0 on macOS and BSD) 

2135 

2136 If *pernic* is True return the same information for every 

2137 network interface installed on the system as a dictionary 

2138 with network interface names as the keys and the namedtuple 

2139 described above as the values. 

2140 

2141 If *nowrap* is True it detects and adjust the numbers which overflow 

2142 and wrap (restart from 0) and add "old value" to "new value" so that 

2143 the returned numbers will always be increasing or remain the same, 

2144 but never decrease. 

2145 "net_io_counters.cache_clear()" can be used to invalidate the 

2146 cache. 

2147 """ 

2148 rawdict = _psplatform.net_io_counters() 

2149 if not rawdict: 

2150 return {} if pernic else None 

2151 if nowrap: 

2152 rawdict = _wrap_numbers(rawdict, 'psutil.net_io_counters') 

2153 if pernic: 

2154 for nic, fields in rawdict.items(): 

2155 rawdict[nic] = _common.snetio(*fields) 

2156 return rawdict 

2157 else: 

2158 return _common.snetio(*[sum(x) for x in zip(*rawdict.values())]) 

2159 

2160 

2161net_io_counters.cache_clear = functools.partial( 

2162 _wrap_numbers.cache_clear, 'psutil.net_io_counters' 

2163) 

2164net_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" 

2165 

2166 

2167def net_connections(kind='inet'): 

2168 """Return system-wide socket connections as a list of 

2169 (fd, family, type, laddr, raddr, status, pid) namedtuples. 

2170 In case of limited privileges 'fd' and 'pid' may be set to -1 

2171 and None respectively. 

2172 The *kind* parameter filters for connections that fit the 

2173 following criteria: 

2174 

2175 +------------+----------------------------------------------------+ 

2176 | Kind Value | Connections using | 

2177 +------------+----------------------------------------------------+ 

2178 | inet | IPv4 and IPv6 | 

2179 | inet4 | IPv4 | 

2180 | inet6 | IPv6 | 

2181 | tcp | TCP | 

2182 | tcp4 | TCP over IPv4 | 

2183 | tcp6 | TCP over IPv6 | 

2184 | udp | UDP | 

2185 | udp4 | UDP over IPv4 | 

2186 | udp6 | UDP over IPv6 | 

2187 | unix | UNIX socket (both UDP and TCP protocols) | 

2188 | all | the sum of all the possible families and protocols | 

2189 +------------+----------------------------------------------------+ 

2190 

2191 On macOS this function requires root privileges. 

2192 """ 

2193 _check_conn_kind(kind) 

2194 return _psplatform.net_connections(kind) 

2195 

2196 

2197def net_if_addrs(): 

2198 """Return the addresses associated to each NIC (network interface 

2199 card) installed on the system as a dictionary whose keys are the 

2200 NIC names and value is a list of namedtuples for each address 

2201 assigned to the NIC. Each namedtuple includes 5 fields: 

2202 

2203 - family: can be either socket.AF_INET, socket.AF_INET6 or 

2204 psutil.AF_LINK, which refers to a MAC address. 

2205 - address: is the primary address and it is always set. 

2206 - netmask: and 'broadcast' and 'ptp' may be None. 

2207 - ptp: stands for "point to point" and references the 

2208 destination address on a point to point interface 

2209 (typically a VPN). 

2210 - broadcast: and *ptp* are mutually exclusive. 

2211 

2212 Note: you can have more than one address of the same family 

2213 associated with each interface. 

2214 """ 

2215 rawlist = _psplatform.net_if_addrs() 

2216 rawlist.sort(key=lambda x: x[1]) # sort by family 

2217 ret = collections.defaultdict(list) 

2218 for name, fam, addr, mask, broadcast, ptp in rawlist: 

2219 try: 

2220 fam = socket.AddressFamily(fam) 

2221 except ValueError: 

2222 if WINDOWS and fam == -1: 

2223 fam = _psplatform.AF_LINK 

2224 elif ( 

2225 hasattr(_psplatform, "AF_LINK") and fam == _psplatform.AF_LINK 

2226 ): 

2227 # Linux defines AF_LINK as an alias for AF_PACKET. 

2228 # We re-set the family here so that repr(family) 

2229 # will show AF_LINK rather than AF_PACKET 

2230 fam = _psplatform.AF_LINK 

2231 

2232 if fam == _psplatform.AF_LINK: 

2233 # The underlying C function may return an incomplete MAC 

2234 # address in which case we fill it with null bytes, see: 

2235 # https://github.com/giampaolo/psutil/issues/786 

2236 separator = ":" if POSIX else "-" 

2237 while addr.count(separator) < 5: 

2238 addr += f"{separator}00" 

2239 

2240 nt = _common.snicaddr(fam, addr, mask, broadcast, ptp) 

2241 

2242 # On Windows broadcast is None, so we determine it via 

2243 # ipaddress module. 

2244 if WINDOWS and fam in {socket.AF_INET, socket.AF_INET6}: 

2245 try: 

2246 broadcast = _common.broadcast_addr(nt) 

2247 except Exception as err: # noqa: BLE001 

2248 debug(err) 

2249 else: 

2250 if broadcast is not None: 

2251 nt._replace(broadcast=broadcast) 

2252 

2253 ret[name].append(nt) 

2254 

2255 return dict(ret) 

2256 

2257 

2258def net_if_stats(): 

2259 """Return information about each NIC (network interface card) 

2260 installed on the system as a dictionary whose keys are the 

2261 NIC names and value is a namedtuple with the following fields: 

2262 

2263 - isup: whether the interface is up (bool) 

2264 - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or 

2265 NIC_DUPLEX_UNKNOWN 

2266 - speed: the NIC speed expressed in mega bits (MB); if it can't 

2267 be determined (e.g. 'localhost') it will be set to 0. 

2268 - mtu: the maximum transmission unit expressed in bytes. 

2269 """ 

2270 return _psplatform.net_if_stats() 

2271 

2272 

2273# ===================================================================== 

2274# --- sensors 

2275# ===================================================================== 

2276 

2277 

2278# Linux, macOS 

2279if hasattr(_psplatform, "sensors_temperatures"): 

2280 

2281 def sensors_temperatures(fahrenheit=False): 

2282 """Return hardware temperatures. Each entry is a namedtuple 

2283 representing a certain hardware sensor (it may be a CPU, an 

2284 hard disk or something else, depending on the OS and its 

2285 configuration). 

2286 All temperatures are expressed in celsius unless *fahrenheit* 

2287 is set to True. 

2288 """ 

2289 

2290 def convert(n): 

2291 if n is not None: 

2292 return (float(n) * 9 / 5) + 32 if fahrenheit else n 

2293 

2294 ret = collections.defaultdict(list) 

2295 rawdict = _psplatform.sensors_temperatures() 

2296 

2297 for name, values in rawdict.items(): 

2298 while values: 

2299 label, current, high, critical = values.pop(0) 

2300 current = convert(current) 

2301 high = convert(high) 

2302 critical = convert(critical) 

2303 

2304 if high and not critical: 

2305 critical = high 

2306 elif critical and not high: 

2307 high = critical 

2308 

2309 ret[name].append( 

2310 _common.shwtemp(label, current, high, critical) 

2311 ) 

2312 

2313 return dict(ret) 

2314 

2315 __all__.append("sensors_temperatures") 

2316 

2317 

2318# Linux 

2319if hasattr(_psplatform, "sensors_fans"): 

2320 

2321 def sensors_fans(): 

2322 """Return fans speed. Each entry is a namedtuple 

2323 representing a certain hardware sensor. 

2324 All speed are expressed in RPM (rounds per minute). 

2325 """ 

2326 return _psplatform.sensors_fans() 

2327 

2328 __all__.append("sensors_fans") 

2329 

2330 

2331# Linux, Windows, FreeBSD, macOS 

2332if hasattr(_psplatform, "sensors_battery"): 

2333 

2334 def sensors_battery(): 

2335 """Return battery information. If no battery is installed 

2336 returns None. 

2337 

2338 - percent: battery power left as a percentage. 

2339 - secsleft: a rough approximation of how many seconds are left 

2340 before the battery runs out of power. May be 

2341 POWER_TIME_UNLIMITED or POWER_TIME_UNLIMITED. 

2342 - power_plugged: True if the AC power cable is connected. 

2343 """ 

2344 return _psplatform.sensors_battery() 

2345 

2346 __all__.append("sensors_battery") 

2347 

2348 

2349# ===================================================================== 

2350# --- other system related functions 

2351# ===================================================================== 

2352 

2353 

2354def boot_time(): 

2355 """Return the system boot time expressed in seconds since the epoch.""" 

2356 # Note: we are not caching this because it is subject to 

2357 # system clock updates. 

2358 return _psplatform.boot_time() 

2359 

2360 

2361def users(): 

2362 """Return users currently connected on the system as a list of 

2363 namedtuples including the following fields. 

2364 

2365 - user: the name of the user 

2366 - terminal: the tty or pseudo-tty associated with the user, if any. 

2367 - host: the host name associated with the entry, if any. 

2368 - started: the creation time as a floating point number expressed in 

2369 seconds since the epoch. 

2370 """ 

2371 return _psplatform.users() 

2372 

2373 

2374# ===================================================================== 

2375# --- Windows services 

2376# ===================================================================== 

2377 

2378 

2379if WINDOWS: 

2380 

2381 def win_service_iter(): 

2382 """Return a generator yielding a WindowsService instance for all 

2383 Windows services installed. 

2384 """ 

2385 return _psplatform.win_service_iter() 

2386 

2387 def win_service_get(name): 

2388 """Get a Windows service by *name*. 

2389 Raise NoSuchProcess if no service with such name exists. 

2390 """ 

2391 return _psplatform.win_service_get(name) 

2392 

2393 

2394# ===================================================================== 

2395 

2396 

2397def _set_debug(value): 

2398 """Enable or disable PSUTIL_DEBUG option, which prints debugging 

2399 messages to stderr. 

2400 """ 

2401 import psutil._common 

2402 

2403 psutil._common.PSUTIL_DEBUG = bool(value) 

2404 _psplatform.cext.set_debug(bool(value)) 

2405 

2406 

2407del memoize_when_activated