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
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
1# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
5"""psutil is a cross-platform library for retrieving information on
6running processes and system utilization (CPU, memory, disks, network,
7sensors) in Python. Supported platforms:
9 - Linux
10 - Windows
11 - macOS
12 - FreeBSD
13 - OpenBSD
14 - NetBSD
15 - Sun Solaris
16 - AIX
18Supported Python versions are cPython 3.6+ and PyPy.
19"""
21import collections
22import contextlib
23import datetime
24import functools
25import os
26import signal
27import socket
28import subprocess
29import sys
30import threading
31import time
34try:
35 import pwd
36except ImportError:
37 pwd = None
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
90if LINUX:
91 # This is public API and it will be retrieved from _pslinux.py
92 # via sys.modules.
93 PROCFS_PATH = "/proc"
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
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
115elif MACOS:
116 from . import _psosx as _psplatform
118elif BSD:
119 from . import _psbsd as _psplatform
121elif SUNOS:
122 from . import _pssunos as _psplatform
123 from ._pssunos import CONN_BOUND # noqa: F401
124 from ._pssunos import CONN_IDLE # noqa: F401
126 # This is public writable API which is read from _pslinux.py and
127 # _pssunos.py via sys.modules.
128 PROCFS_PATH = "/proc"
130elif AIX:
131 from . import _psaix as _psplatform
133 # This is public API and it will be retrieved from _pslinux.py
134 # via sys.modules.
135 PROCFS_PATH = "/proc"
137else: # pragma: no cover
138 msg = f"platform {sys.platform} is not supported"
139 raise NotImplementedError(msg)
142# fmt: off
143__all__ = [
144 # exceptions
145 "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied",
146 "TimeoutExpired",
148 # constants
149 "version_info", "__version__",
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",
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",
161 "AF_LINK",
163 "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN",
165 "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
167 "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX",
168 "SUNOS", "WINDOWS", "AIX",
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",
175 # classes
176 "Process", "Popen",
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
192__all__.extend(_psplatform.__extra__all__)
194# Linux, FreeBSD
195if hasattr(_psplatform.Process, "rlimit"):
196 # Populate global namespace with RLIM* constants.
197 from . import _psutil_posix
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
207AF_LINK = _psplatform.AF_LINK
209__author__ = "Giampaolo Rodola'"
210__version__ = "7.0.0"
211version_info = tuple(int(num) for num in __version__.split('.'))
213_timer = getattr(time, 'monotonic', time.time)
214_TOTAL_PHYMEM = None
215_LOWEST_PID = None
216_SENTINEL = object()
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)
244# =====================================================================
245# --- Utils
246# =====================================================================
249if hasattr(_psplatform, 'ppid_map'):
250 # Faster version (Windows and Linux).
251 _ppid_map = _psplatform.ppid_map
252else: # pragma: no cover
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
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)
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)
283# =====================================================================
284# --- Process class
285# =====================================================================
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.
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.
297 The only exceptions for which process identity is pre-emptively
298 checked and guaranteed are:
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()
312 To prevent this problem for all other methods you can use
313 is_running() before querying the process.
314 """
316 def __init__(self, pid=None):
317 self._init(pid)
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
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
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.
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())
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
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)
416 return "{}.{}({})".format(
417 self.__class__.__module__,
418 self.__class__.__name__,
419 ", ".join([f"{k}={v!r}" for k, v in info.items()]),
420 )
422 __repr__ = __str__
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
445 def __ne__(self, other):
446 return not self == other
448 def __hash__(self):
449 if self._hash is None:
450 self._hash = hash(self._ident)
451 return self._hash
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)
465 @property
466 def pid(self):
467 """The process PID."""
468 return self._pid
470 # --- utility methods
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.
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.
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.
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()
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)
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
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
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
616 def is_running(self):
617 """Return whether this process is running.
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
643 # --- actual API
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/
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
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
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 """
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
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
737 def cmdline(self):
738 """The command line this process has been called with."""
739 return self._proc.cmdline()
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
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()
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
775 def cwd(self):
776 """Process current working directory as an absolute path."""
777 return self._proc.cwd()
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)
787 if POSIX:
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()
796 def gids(self):
797 """Return process GIDs as a (real, effective, saved)
798 namedtuple.
799 """
800 return self._proc.gids()
802 def terminal(self):
803 """The terminal associated with this process, if any,
804 else None.
805 """
806 return self._proc.terminal()
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()
814 # Linux, BSD, AIX and Windows only
815 if hasattr(_psplatform.Process, "io_counters"):
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()
826 # Linux and Windows
827 if hasattr(_psplatform.Process, "ionice_get"):
829 def ionice(self, ioclass=None, value=None):
830 """Get or set process I/O niceness (priority).
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.
836 On Windows only *ioclass* is used and it can be set to 2
837 (normal), 1 (low) or 0 (very low).
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)
850 # Linux / FreeBSD only
851 if hasattr(_psplatform.Process, "rlimit"):
853 def rlimit(self, resource, limits=None):
854 """Get or set process resource limits as a (soft, hard)
855 tuple.
857 *resource* is one of the RLIMIT_* constants.
858 *limits* is supposed to be a (soft, hard) tuple.
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)
867 # Windows, Linux and FreeBSD only
868 if hasattr(_psplatform.Process, "cpu_affinity_get"):
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)))
889 # Linux, FreeBSD, SunOS
890 if hasattr(_psplatform.Process, "cpu_num"):
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()
902 # All platforms has it, but maybe not in the future.
903 if hasattr(_psplatform.Process, "environ"):
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()
911 if WINDOWS:
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()
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()
925 def num_threads(self):
926 """Return the number of threads used by this process."""
927 return self._proc.num_threads()
929 if hasattr(_psplatform.Process, "threads"):
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()
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.
944 Example (A == this process):
946 A ─┐
947 │
948 ├─ B (child) ─┐
949 │ └─ X (grandchild) ─┐
950 │ └─ Y (great grandchild)
951 ├─ C (child)
952 └─ D (child)
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
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
1009 def cpu_percent(self, interval=None):
1010 """Return a float representing the current process CPU
1011 utilization as a percentage.
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.
1018 When *interval* is > 0.0 compares process times to system CPU
1019 times elapsed before and after the interval (blocking).
1021 In this case is recommended for accuracy that this function
1022 be called with at least 0.1 seconds between calls.
1024 A value > 100.0 can be returned in case of processes running
1025 multiple threads on different CPU cores.
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%.
1032 Examples:
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
1050 def timer():
1051 return _timer() * num_cpus
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
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
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)
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()
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.
1118 The "portable" fields available on all platforms are `rss` and `vms`.
1120 All numbers are expressed in bytes.
1121 """
1122 return self._proc.memory_info()
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.
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.
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()
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:
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)
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
1176 if hasattr(_psplatform.Process, "memory_maps"):
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.
1182 If *grouped* is True the mapped regions with the same 'path'
1183 are grouped together and the different memory fields are summed.
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]
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()
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:
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)
1237 @_common.deprecated_method(replacement="net_connections")
1238 def connections(self, kind="inet"):
1239 return self.net_connections(kind=kind)
1241 # --- signals
1243 if POSIX:
1245 def _send_signal(self, sig):
1246 assert not self.pid < 0, self.pid
1247 self._raise_if_pid_reused()
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
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)
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()
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()
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()
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()
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).
1334 If the process is already terminated immediately return None
1335 instead of raising NoSuchProcess.
1337 If *timeout* (in seconds) is specified and process is still
1338 alive raise TimeoutExpired.
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
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
1362# =====================================================================
1363# --- Popen class
1364# =====================================================================
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:
1373 * send_signal()
1374 * terminate()
1375 * kill()
1377 This is done in order to avoid killing another process in case its
1378 PID has been reused, fixing BPO-6973.
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 """
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)
1404 def __dir__(self):
1405 return sorted(set(dir(Popen) + dir(subprocess.Popen)))
1407 def __enter__(self):
1408 if hasattr(self.__subproc, '__enter__'):
1409 self.__subproc.__enter__()
1410 return self
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()
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
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
1446# =====================================================================
1447# --- system processes related functions
1448# =====================================================================
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
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)
1477_pmap = {}
1478_pids_reused = set()
1481def process_iter(attrs=None, ad_value=None):
1482 """Return a generator yielding a Process instance for all
1483 running processes.
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()`.
1489 The sorting order in which processes are yielded is based on
1490 their PIDs.
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
1501 def add(pid):
1502 proc = Process(pid)
1503 pmap[proc.pid] = proc
1504 return proc
1506 def remove(pid):
1507 pmap.pop(pid, None)
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
1535process_iter.cache_clear = lambda: _pmap.clear() # noqa: PLW0108
1536process_iter.cache_clear.__doc__ = "Clear process_iter() internal cache."
1539def wait_procs(procs, timeout=None, callback=None):
1540 """Convenience function which waits for a list of processes to
1541 terminate.
1543 Return a (gone, alive) tuple indicating which processes
1544 are gone and which ones are still alive.
1546 The gone ones will have a new *returncode* attribute indicating
1547 process exit status (may be None).
1549 *callback* is a function which gets called every time a process
1550 terminates (a Process instance is passed as callback argument).
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.
1557 Typical use case is:
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
1563 Example:
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 """
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)
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
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
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
1627 return (list(gone), list(alive))
1630# =====================================================================
1631# --- CPU related functions
1632# =====================================================================
1635def cpu_count(logical=True):
1636 """Return the number of logical CPUs in the system (same as
1637 os.cpu_count()).
1639 If *logical* is False return the number of physical cores only
1640 (e.g. hyper thread CPUs are excluded).
1642 Return None if undetermined.
1644 The return value is cached after first call.
1645 If desired cache can be cleared like this:
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
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:
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)
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()
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 = {}
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 = {}
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
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
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)
1757def cpu_percent(interval=None, percpu=False):
1758 """Return a float representing the current system-wide CPU
1759 utilization as a percentage.
1761 When *interval* is > 0.0 compares system CPU times elapsed before
1762 and after the interval (blocking).
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.
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.
1777 Examples:
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)
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)
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)
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
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()
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:
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 >>>
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)
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)
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
1898def cpu_stats():
1899 """Return CPU statistics."""
1900 return _psplatform.cpu_stats()
1903if hasattr(_psplatform, "cpu_freq"):
1905 def cpu_freq(percpu=False):
1906 """Return CPU frequency as a namedtuple including current,
1907 min and max frequency expressed in Mhz.
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
1935 current = currs / num_cpus
1937 if set_none:
1938 min_ = max_ = None
1939 else:
1940 min_ = mins / num_cpus
1941 max_ = maxs / num_cpus
1943 return _common.scpufreq(current, min_, max_)
1945 __all__.append("cpu_freq")
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
1956 __all__.append("getloadavg")
1959# =====================================================================
1960# --- system memory related functions
1961# =====================================================================
1964def virtual_memory():
1965 """Return statistics about system memory usage as a namedtuple
1966 including the following fields, expressed in bytes:
1968 - total:
1969 total physical memory available.
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.
1978 - percent:
1979 the percentage usage calculated as (total - available) / total * 100
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
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)
1993 Platform-specific fields:
1995 - active (UNIX):
1996 memory currently in use or very recently used, and so it is in RAM.
1998 - inactive (UNIX):
1999 memory that is marked as not used.
2001 - buffers (BSD, Linux):
2002 cache for things like file system metadata.
2004 - cached (BSD, macOS):
2005 cache for various things.
2007 - wired (macOS, BSD):
2008 memory that is marked to always stay in RAM. It is never moved to disk.
2010 - shared (BSD):
2011 memory that may be simultaneously accessed by multiple processes.
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
2023def swap_memory():
2024 """Return system swap memory statistics as a namedtuple including
2025 the following fields:
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)
2034 'sin' and 'sout' on Windows are meaningless and always set to 0.
2035 """
2036 return _psplatform.swap_memory()
2039# =====================================================================
2040# --- disks/partitions related functions
2041# =====================================================================
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)
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.
2058 If *all* parameter is False return physical devices only and ignore
2059 all others.
2060 """
2061 return _psplatform.disk_partitions(all)
2064def disk_io_counters(perdisk=False, nowrap=True):
2065 """Return system disk I/O statistics as a namedtuple including
2066 the following fields:
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)
2075 Platform specific:
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
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.
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.
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())))
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"
2117# =====================================================================
2118# --- network related functions
2119# =====================================================================
2122def net_io_counters(pernic=False, nowrap=True):
2123 """Return network I/O statistics as a namedtuple including
2124 the following fields:
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)
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.
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())])
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"
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:
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 +------------+----------------------------------------------------+
2191 On macOS this function requires root privileges.
2192 """
2193 _check_conn_kind(kind)
2194 return _psplatform.net_connections(kind)
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:
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.
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
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"
2240 nt = _common.snicaddr(fam, addr, mask, broadcast, ptp)
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)
2253 ret[name].append(nt)
2255 return dict(ret)
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:
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()
2273# =====================================================================
2274# --- sensors
2275# =====================================================================
2278# Linux, macOS
2279if hasattr(_psplatform, "sensors_temperatures"):
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 """
2290 def convert(n):
2291 if n is not None:
2292 return (float(n) * 9 / 5) + 32 if fahrenheit else n
2294 ret = collections.defaultdict(list)
2295 rawdict = _psplatform.sensors_temperatures()
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)
2304 if high and not critical:
2305 critical = high
2306 elif critical and not high:
2307 high = critical
2309 ret[name].append(
2310 _common.shwtemp(label, current, high, critical)
2311 )
2313 return dict(ret)
2315 __all__.append("sensors_temperatures")
2318# Linux
2319if hasattr(_psplatform, "sensors_fans"):
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()
2328 __all__.append("sensors_fans")
2331# Linux, Windows, FreeBSD, macOS
2332if hasattr(_psplatform, "sensors_battery"):
2334 def sensors_battery():
2335 """Return battery information. If no battery is installed
2336 returns None.
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()
2346 __all__.append("sensors_battery")
2349# =====================================================================
2350# --- other system related functions
2351# =====================================================================
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()
2361def users():
2362 """Return users currently connected on the system as a list of
2363 namedtuples including the following fields.
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()
2374# =====================================================================
2375# --- Windows services
2376# =====================================================================
2379if WINDOWS:
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()
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)
2394# =====================================================================
2397def _set_debug(value):
2398 """Enable or disable PSUTIL_DEBUG option, which prints debugging
2399 messages to stderr.
2400 """
2401 import psutil._common
2403 psutil._common.PSUTIL_DEBUG = bool(value)
2404 _psplatform.cext.set_debug(bool(value))
2407del memoize_when_activated