This is part of the psutil 5.3.0 release (see the changelog for the full list of changes).
The old pattern¶
Iterating over processes and collecting attributes requires more boilerplate than it should. A process returned by psutil.process_iter() may disappear before you access it, or require elevated privileges, so every lookup has to be guarded with a try / except:
>>> import psutil
>>> for proc in psutil.process_iter():
... try:
... pinfo = proc.as_dict(attrs=['pid', 'name'])
... except (psutil.NoSuchProcess, psutil.AccessDenied):
... pass
... else:
... print(pinfo)
...
{'pid': 1, 'name': 'systemd'}
{'pid': 2, 'name': 'kthreadd'}
{'pid': 3, 'name': 'ksoftirqd/0'}
This is not decorative. It's necessary to avoid the race condition.
The new pattern¶
5.3.0 adds attrs and ad_value parameters to psutil.process_iter(). With these, the loop body becomes:
>>> import psutil
>>> for proc in psutil.process_iter(attrs=['pid', 'name']):
... print(proc.info)
...
{'pid': 1, 'name': 'systemd'}
{'pid': 2, 'name': 'kthreadd'}
{'pid': 3, 'name': 'ksoftirqd/0'}
Internally, process_iter() attaches an info dict to the Process instance. The attributes are pre-fetched in one shot. Processes that disappear during iteration are silently skipped, and attributes that would raise AccessDenied get assigned ad_value, which defaults to None:
for p in psutil.process_iter(['name', 'username'], ad_value="N/A"):
print(p.name(), p.username())
Performance¶
Beyond the syntactic win, the new syntax is also faster than calling individual methods in a loop. process_iter(attrs=[...]) is equivalent to using Process.oneshot() on each process (see Making psutil twice as fast for how that works): attributes that share a syscall or a /proc file are fetched together instead of re-read on every method call, which is a lot faster.
Comprehensions¶
With the exception boilerplate out of the way, comprehensions finally work cleanly. E.g. getting processes owned by the current user can be written as:
>>> import getpass
>>> from pprint import pprint as pp
>>> pp([(p.pid, p.info['name']) for p in psutil.process_iter(attrs=['name', 'username']) if p.info['username'] == getpass.getuser()])
[(16832, 'bash'),
(19772, 'ssh'),
(20492, 'python')]