distro
distro answers one question: which OS distribution is this Python process running on? It reads /etc/os-release (falling back to lsb_release output, distro release files, and uname on BSD) and gives you a stable machine-readable id like "ubuntu" or "rhel", version numbers, codenames, and the ID_LIKE chain, plus a small CLI. It exists because platform.linux_distribution() was removed from the standard library in Python 3.8, and it became the drop-in replacement that pip, Ansible-adjacent tooling, and countless installers depend on.
The correct answer when you must know the Linux distribution on Python versions or edge cases the stdlib does not cover, and a dependency you will inherit through pip anyway. On Python 3.10+ with simple needs, platform.freedesktop_os_release() does the job without adding a package.
Use it if
- You branch install or config logic on the distribution: distro.id() returning "debian", "fedora", or "alpine" is exactly the switch value you need
- You need family matching, not exact matching: distro.like() exposes ID_LIKE so Linux Mint can be treated as "ubuntu debian" without an ever-growing if-chain
- You report environment info in diagnostics or telemetry and want one call (distro.info() or the distro CLI) that behaves the same across Linux and BSD
- You still support Python below 3.10 and need os-release parsing that works there
- You are on Python 3.10+ and only need the basics: platform.freedesktop_os_release() in the standard library parses /etc/os-release with zero dependencies, which covers the majority of uses
- You need Windows or macOS answers: distro is Linux and BSD only; on other platforms it returns empty strings, so it cannot be your cross-platform OS detector
- You expect active development: the last release (1.9.0) shipped in December 2023; the repo still gets pushes but this is finished software on a very slow cadence, which also means new niche distros can lag
- You want hardware or runtime facts (CPU, memory, virtualization): that is psutil territory; distro only knows what the distribution calls itself
Setup reality
pip install distro is as easy as it gets: pure Python, no dependencies, supports Python 3.7+ (current release requires 3.6+ per metadata, tests target 3.7+ and PyPy). It is even documented as safe to vendor by copying distro.py into your project. The gotchas are environmental, not install-time: results depend entirely on the host's /etc/os-release quality, minimal containers can lack lsb_release so fallback data like codenames may be empty, and calling it on Windows or macOS quietly yields empty strings rather than an error, which surprises people writing cross-platform checks.
Patterns
Get the machine-readable distribution IDget-distro-id
import distro
print(distro.id()) # 'ubuntu', 'rhel', 'alpine', 'arch', ...IDs are normalized and stable (rhel, not Red Hat Enterprise Linux); on non-Linux/BSD platforms this returns an empty string, not an exception.
Get a pretty name for displayhuman-readable-name
import distro
print(distro.name(pretty=True)) # 'Ubuntu 24.04.2 LTS'
print(distro.name()) # 'Ubuntu'pretty=True includes version and codename when the distribution provides them; use this for logs and bug reports, never for branching logic.
Get the most precise version availablebest-version
import distro
print(distro.version()) # '24.04'
print(distro.version(best=True)) # '24.04.2'best=True scans all data sources for the most detailed number instead of respecting source precedence; the two calls can disagree, so pick one convention and stick to it.
Compare against a minimum versionversion-parts
import distro
major, minor, build = distro.version_parts(best=True)
if distro.id() == "ubuntu" and int(major) >= 22:
enable_modern_path()Parts come back as strings and may be empty on rolling-release distros like Arch, so guard the int() conversion.
Match distribution families via ID_LIKEfamily-matching
import distro
DEBIAN_FAMILY = {"debian", "ubuntu"}
ids = {distro.id(), *distro.like().split()}
if ids & DEBIAN_FAMILY:
use_apt()
elif ids & {"rhel", "fedora", "centos"}:
use_dnf()distro.like() returns a space-separated string (possibly empty), so derivatives like Mint ('ubuntu debian') or Rocky ('rhel centos fedora') match without enumerating every fork.
Collect everything for diagnosticsfull-info-dict
import distro
info = distro.info(pretty=False, best=True)
# {'id': 'ubuntu', 'version': '24.04.2', 'version_parts': {...},
# 'like': 'debian', 'codename': 'noble'}
report["os"] = infoinfo() is JSON-serializable as-is, which makes it the one-liner for attaching environment context to error reports.
Get the release codename for repo URLscodename
import distro
codename = distro.codename() # 'noble', 'bookworm', ...
repo_line = f"deb https://example.com/apt {codename} main"Codename can be an empty string on distros without one (Fedora, Arch) and sometimes requires lsb_release to be installed; always handle the empty case.
Read a raw os-release attributeraw-os-release-field
import distro
print(distro.os_release_attr("id_like")) # 'debian'
print(distro.os_release_attr("version_codename")) # 'noble'
print(distro.os_release_attr("variant_id")) # e.g. 'workstation'Attribute names are the lowercased os-release keys; this is the escape hatch for fields the high-level API does not expose, like VARIANT_ID or BUILD_ID.
Use the CLI in shell scriptscli-usage
$ distro
Name: Ubuntu 24.04.2 LTS
Version: 24.04.2
Codename: noble
$ distro -j | jq -r .id
ubuntuThe console script installs with the package; python -m distro works too and is safer inside virtualenvs that are not on PATH.
Vendor distro.py without adding a dependencyvendor-single-file
# In your project, no pip install:
$ curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py
# then in code
from . import distro
print(distro.id())Vendoring is explicitly supported by the project; installers and bootstrap scripts use this to detect the OS before any package manager is available.
Prefer stdlib on 3.10+, fall back to distrostdlib-fallback
import platform
try:
os_release = platform.freedesktop_os_release()
distro_id = os_release.get("ID", "")
except (AttributeError, OSError):
import distro
distro_id = distro.id()platform.freedesktop_os_release() only reads os-release files; distro additionally falls back to lsb_release, release files, and uname, so keep it for old or odd hosts.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| psutil | PyPI | You need system facts beyond the distro name: CPU, memory, disks, processes, boot time. |
| distro-info | PyPI | You specifically need Debian and Ubuntu release metadata such as EOL dates and supported series, not host detection. |