archey4/archey/distributions.py
2024-05-10 16:01:32 +02:00

154 lines
5.7 KiB
Python

"""
Distributions enumeration module.
Operating Systems detection logic.
Interface to `os-release` (through `distro` module).
"""
import os
import platform
import sys
from contextlib import suppress
from enum import Enum
from functools import lru_cache
from typing import List, Optional
import distro
class Distributions(Enum):
"""
This enumeration lists supported operating systems (keys).
Values contain their respective `distro` identifier.
See <https://distro.readthedocs.io/en/latest/#distro.id>.
"""
ALPINE = "alpine"
ANDROID = "android"
ARCH = "arch"
ARMBIAN = "armbian"
BUILDROOT = "buildroot"
BUNSENLABS = "bunsenlabs"
CENTOS = "centos"
CRUNCHBANG = "crunchbang"
DARWIN = "darwin"
DEBIAN = "debian"
DEVUAN = "devuan"
ELEMENTARY = "elementary"
ENDEAVOUROS = "endeavouros"
ENSO = "enso"
FEDORA = "fedora"
FREEBSD = "freebsd"
GENTOO = "gentoo"
GUIX = "guix"
KALI = "kali"
MANJARO = "manjaro"
MOEVALENT = "moevalent"
NETBSD = "netbsd"
NIXOS = "nixos"
NOBARA = "nobara"
LINUX = "linux"
LINUXMINT = "linuxmint"
OPENBSD = "openbsd"
OPENSUSE = "opensuse"
POP = "pop"
PARABOLA = "parabola"
QUIRINUX = "quirinux"
RASPBIAN = "raspbian"
ROCKY = "rocky"
RHEL = "rhel"
SIDUCTION = "siduction"
SLACKWARE = "slackware"
UBUNTU = "ubuntu"
UNIVALENT = "univalent"
WINDOWS = "windows"
@staticmethod
def get_identifiers() -> List[str]:
"""Simple getter returning current supported distributions identifiers"""
return [d.value for d in Distributions.__members__.values()]
@staticmethod
@lru_cache(maxsize=None) # Python < 3.9, `functools.cache` is not yet available.
def get_local() -> "Distributions": # pylint: disable=too-many-return-statements
"""Entry point of Archey distribution detection logic"""
distribution = Distributions._vendor_detection()
# In case nothing got detected the "regular" way...
if not distribution:
# Are we running on Darwin (somehow not previously detected by `distro`) ?
if platform.system() == "Darwin":
return Distributions.DARWIN
# Android systems are currently not being handled by `distro`.
# At first, we imitate the Python standard library, by checking whether CPython
# has been built for Android.
# See <https://github.com/python/cpython/search?l=Python&q=getandroidapilevel>
# As a fallback, we mimic Neofetch behavior, by relying on the file-system.
# See <https://github.com/nir0s/distro/issues/253>
if hasattr(sys, "getandroidapilevel") or (
os.path.isdir("/system/app") and os.path.isdir("/system/priv-app")
):
return Distributions.ANDROID
# If nothing of the above matched, fall-back on the Linux logo.
return Distributions.LINUX
# Below are brain-dead cases for distributions not properly handled by `distro`.
# One _may_ want to add its own logic to add support for such undetectable systems.
if distribution == Distributions.DEBIAN:
# CrunchBang is tagged as _regular_ Debian by `distro`.
# Below conditions are here to work-around this issue.
# First condition : CrunchBang-Linux and CrunchBang-Monara.
# Second condition : CrunchBang++ (CBPP).
if os.path.isfile("/etc/lsb-release-crunchbang") or os.path.isfile(
"/usr/bin/cbpp-exit"
):
return Distributions.CRUNCHBANG
# Armbian is also detected as _regular_ Debian by `distro`, but going directly
# through release info gives us a proper id (see <python-distro/distro#366>).
if distro.distro_release_attr("id") == "armbian":
return Distributions.ARMBIAN
elif distribution == Distributions.UBUNTU:
# Older Pop!_OS releases (< 20.*) didn't ship their own `ID` (from `os-release`).
# Thus, they are detected as "regular" Ubuntu distributions.
# We may here rely on their `NAME` (from `os-release`), which is sufficient.
if Distributions.get_distro_name(pretty=False) == "Pop!_OS":
return Distributions.POP
return distribution
@staticmethod
def _vendor_detection() -> Optional["Distributions"]:
"""Main distribution detection logic, relying on `distro`, handling _common_ cases"""
# Are we running on Windows ?
if platform.system() == "Windows":
return Distributions.WINDOWS
# Is `ID` (from `os-release`) well-known and supported ?
with suppress(ValueError):
return Distributions(distro.id())
# Is any of `ID_LIKE` (from `os-release`) well-known and supported ?
# See <https://www.freedesktop.org/software/systemd/man/os-release.html#ID_LIKE=>.
for id_like in distro.like().split(" "):
with suppress(ValueError):
return Distributions(id_like)
# Nothing of the above matched, let's return `None` and let the caller handle it.
return None
@staticmethod
def get_distro_name(pretty: bool = True) -> Optional[str]:
"""Simple wrapper to `distro` to return the current distribution _pretty_ name"""
return distro.name(pretty=pretty) or None
@staticmethod
def get_ansi_color() -> Optional[str]:
"""
Simple wrapper to `distro` to return the distribution preferred ANSI color.
See <https://www.freedesktop.org/software/systemd/man/os-release.html#ANSI_COLOR=>.
"""
return distro.os_release_attr("ansi_color") or None