Migration guide

This page summarises the breaking changes introduced in each major release and shows the code changes required to upgrade.

Note

Minor and patch releases (e.g. 6.1.x, 7.1.x) do not contain breaking changes. Only major releases are listed here.

Migrating to 8.0

Key breaking changes in 8.0:

Important

Do not rely on positional unpacking of named tuples. Always use attribute access (e.g. t.rss).

process_iter(): p.info is deprecated

process_iter() now caches pre-fetched values internally, so normal method calls can return them without using the Process.info dict. p.info still works, but raises DeprecationWarning.

import psutil

# before
for p in psutil.process_iter(attrs=["name", "status"]):
    print(p.info["name"], p.info["status"])

# after
for p in psutil.process_iter(attrs=["name", "status"]):
    print(p.name(), p.status())  # return cached values, never raise

When attrs are specified, the corresponding method calls return cached values without extra syscalls. AccessDenied / ZombieProcess are handled transparently by returning ad_value.

If you need a dict, use Process.as_dict() instead of Process.info.

import psutil

# before
for p in psutil.process_iter(attrs=["name", "status"]):
    print(p.info)

# after
attrs = ["name", "status"]
for p in psutil.process_iter(attrs=attrs):
    print(p.as_dict(attrs))  # return cached values, never raise

Note

If "name" was pre-fetched via attrs, p.name() returns ad_value instead of raising AccessDenied. If you need the exception, do not include the method in attrs.

Named tuple field order changed

  • cpu_times(): user, system, idle fields changed order on Linux, macOS and BSD. They are now always the first 3 fields on all platforms, with platform-specific fields (e.g. nice) following. Positional access (e.g. cpu_times()[3]) silently returns the wrong field.

    # before
    user, nice, system, idle = psutil.cpu_times()
    
    # after
    t = psutil.cpu_times()
    user, system, idle = t.user, t.system, t.idle
    
  • Process.memory_info(): the returned named tuple changed size and field order.

    • Linux: lib and dirty fields removed (they were always 0 since Linux 2.6). Aliases returning 0 and emitting DeprecationWarning are kept.

    • macOS: pfaults and pageins removed with no aliases. Use Process.page_faults() instead.

    • Windows: old fields were renamed: wsetrss, peak_wsetpeak_rss, pagefile and privatevms, peak_pagefilepeak_vms, num_page_faultsProcess.page_faults(). The old names still work but raise DeprecationWarning. paged_pool, nonpaged_pool, peak_paged_pool, peak_nonpaged_pool moved to Process.memory_extras().

    • BSD: a new peak_rss field was added.

  • virtual_memory(): on Windows, new cached and wired fields were added.

cpu_times() interrupt renamed to irq on Windows

The interrupt field of cpu_times() on Windows was renamed to irq to match Linux and BSD. The old name still works but raises DeprecationWarning.

Constants and fields are now enums

These now yield enum members instead of plain str / int, and the matching module constants are members of the same enums:

They subclass enum.StrEnum / enum.IntEnum, so they compare equal to the values they replace: p.status() == psutil.STATUS_RUNNING keeps working. Only code inspecting repr() or type needs updating.

memory_full_info() is deprecated

Process.memory_full_info() is deprecated. Use Process.memory_footprint() instead; it returns the same fields (uss, pss and swap), plus a new shared field.

New memory_extras() method

8.0 introduces a new Process.memory_extras() method, returning extra platform-specific memory metrics which complement Process.memory_info():

  • Linux: peak_rss, peak_vms, rss_anon, rss_file, rss_shmem, swap_anon, locked.

  • macOS: phys_footprint, peak_footprint.

  • Windows: virtual, peak_virtual, paged_pool, nonpaged_pool, peak_paged_pool, peak_nonpaged_pool.

New Process.attrs class attribute

Process.attrs is a new frozenset containing the valid attribute names accepted by Process.as_dict() and process_iter(). It avoids creating a throwaway process just to discover them:

# before
attrs = list(psutil.Process().as_dict().keys())

# after
attrs = psutil.Process.attrs

It also makes it easy to pass all or a subset of attributes. process_iter(attrs=[]) (empty list meaning “all”) is now deprecated; use Process.attrs instead:

# all attrs
psutil.process_iter(attrs=psutil.Process.attrs)

# all except connections
psutil.process_iter(attrs=psutil.Process.attrs - {"net_connections"})

Python 3.6 and 3.7 dropped

The minimum version is now Python 3.8.

Windows < 10 dropped

Support for Windows Vista, 7, 8, 8.1 and their server counterparts (Server 2008 to 2012 R2) was removed. The minimum version is now Windows 10 / Windows Server 2016. The last release supporting older versions is the 7.2.x series. See #2893.

Git tags renamed

Git tags were renamed from release-X.Y.Z to vX.Y.Z (e.g. release-7.2.2v7.2.2). Old tags remain for backward compatibility. If your scripts or URLs reference psutil tags, update them to the new format. See #2788.


Migrating to 7.0

Process.memory_info_ex() removed

Process.memory_info_ex(), deprecated since 4.0.0 in 2016, was removed. Use Process.memory_full_info() instead.

# before
p.memory_info_ex()

# after
p.memory_full_info()

Python 2.7 dropped

Python 2.7 is no longer supported. The last release supporting it is psutil 6.1.x:

pip2 install "psutil==6.1.*"

Migrating to 6.0

Process.connections() renamed

Process.connections() was renamed to Process.net_connections() for consistency with the system-level net_connections(). The old name raises DeprecationWarning and will be removed in a future release:

# before
p.connections()
p.connections(kind="tcp")

# after
p.net_connections()
p.net_connections(kind="tcp")

disk_partitions() lost two fields

The maxfile and maxpath fields were removed from the named tuple returned by disk_partitions(). Positional unpacking will break:

# before (broken)
device, mountpoint, fstype, opts, maxfile, maxpath = part

# after
device, mountpoint, fstype, opts = (
    part.device, part.mountpoint, part.fstype, part.opts
)

process_iter() no longer checks for PID reuse

process_iter() no longer preemptively checks whether yielded PIDs have been reused, making it ~20× faster. To verify that a process object is still alive and refers to the same process, use Process.is_running() explicitly:

for p in psutil.process_iter(["name"]):
    if p.is_running():
        print(p.pid, p.name())