Skip to content

API reference for the Python implementation of sp-variant

variant: the top-level functions and structures

sp_variant.variant

Build variant definitions and commands.

Config dataclass

Runtime configuration for the sp-variant library functions.

Source code in python/sp_variant/defs.py
@dataclasses.dataclass(frozen=True)
class Config:
    """Runtime configuration for the sp-variant library functions."""

    args: list[str] | None = None
    """Additional arguments passed to the command."""

    command: str | None = None
    """The main argument: a command to execute, a variant specification to show, etc."""

    noop: bool = False
    """No-operation mode; display what would have been done."""

    repodir: pathlib.Path | None = None
    """The path to the directory containing the `add-storpool-repo` data files to install."""

    repotype: RepoType = REPO_TYPES[0]
    """Which StorPool repository to configure."""

    verbose: bool = False
    """Verbose operation; display diagnostic output."""

    def diag(self, msg: str) -> None:
        """Output a diagnostic message in verbose mode."""
        if self.verbose:
            print(msg, file=sys.stderr)  # noqa: T201

    @property
    def _diag_to_stderr(self) -> bool:
        """We always send the diagnostic messages to stderr now."""
        return True

    @_diag_to_stderr.setter
    def _diag_to_stderr(self, value: bool) -> None:
        """Simulate setting the property, do nothing instead."""

    # OK, this is a bit ugly. It's going away soon.
    def _do_setattr(self, name: str, value: Any) -> None:  # noqa: ANN401
        """Ignore any attempts to set the `_diag_to_stderr` member."""
        if name == "_diag_to_stderr":
            return

        _config_orig_setattr(self, name, value)
args: list[str] | None = None class-attribute instance-attribute

Additional arguments passed to the command.

command: str | None = None class-attribute instance-attribute

The main argument: a command to execute, a variant specification to show, etc.

noop: bool = False class-attribute instance-attribute

No-operation mode; display what would have been done.

repodir: pathlib.Path | None = None class-attribute instance-attribute

The path to the directory containing the add-storpool-repo data files to install.

repotype: RepoType = REPO_TYPES[0] class-attribute instance-attribute

Which StorPool repository to configure.

verbose: bool = False class-attribute instance-attribute

Verbose operation; display diagnostic output.

diag(msg)

Output a diagnostic message in verbose mode.

Source code in python/sp_variant/defs.py
def diag(self, msg: str) -> None:
    """Output a diagnostic message in verbose mode."""
    if self.verbose:
        print(msg, file=sys.stderr)  # noqa: T201

Variant

Bases: NamedTuple

The information about a Linux distribution version (build variant).

Source code in python/sp_variant/defs.py
class Variant(NamedTuple):
    """The information about a Linux distribution version (build variant)."""

    name: str
    """The name of the variant, e.g. `ALMA9`, `UBUNTU2204`, etc."""

    descr: str
    """The human-readable description of the variant."""

    parent: str
    """The name of the variant that this one is based on."""

    family: str
    """The OS "family" that this distribution belongs to."""

    detect: Detect
    """The ways to check whether we are running this variant."""

    supported: Supported
    """The aspects of StorPool operation supported for this build variant."""

    commands: Commands
    """The OS commands to execute for particular purposes."""

    min_sys_python: str
    """The minimum Python version that we can depend on."""

    repo: DebRepo | YumRepo
    """The StorPool repository files to install."""

    package: dict[str, str]
    """The names of the packages to be used for this variant."""

    systemd_lib: str
    """The name of the directory to install systemd unit files to."""

    file_ext: str
    """The filename extension of the OS packages ("deb", "rpm", etc.)."""

    initramfs_flavor: str
    """The type of initramfs-generating tools."""

    builder: Builder
    """The data specific to the StorPool builder containers."""
builder: Builder instance-attribute

The data specific to the StorPool builder containers.

commands: Commands instance-attribute

The OS commands to execute for particular purposes.

descr: str instance-attribute

The human-readable description of the variant.

detect: Detect instance-attribute

The ways to check whether we are running this variant.

family: str instance-attribute

The OS "family" that this distribution belongs to.

file_ext: str instance-attribute

The filename extension of the OS packages ("deb", "rpm", etc.).

initramfs_flavor: str instance-attribute

The type of initramfs-generating tools.

min_sys_python: str instance-attribute

The minimum Python version that we can depend on.

name: str instance-attribute

The name of the variant, e.g. ALMA9, UBUNTU2204, etc.

package: dict[str, str] instance-attribute

The names of the packages to be used for this variant.

parent: str instance-attribute

The name of the variant that this one is based on.

repo: DebRepo | YumRepo instance-attribute

The StorPool repository files to install.

supported: Supported instance-attribute

The aspects of StorPool operation supported for this build variant.

systemd_lib: str instance-attribute

The name of the directory to install systemd unit files to.

VariantDetectError

Bases: VariantError

An error that occurred during the detection of a variant.

Source code in python/sp_variant/variant.py
class VariantDetectError(VariantError):
    """An error that occurred during the detection of a variant."""

VariantError

Bases: Exception

Base class for errors that occurred during variant processing.

Source code in python/sp_variant/defs.py
class VariantError(Exception):
    """Base class for errors that occurred during variant processing."""

VariantFileError

Bases: VariantError

A filesystem-related error occurred.

Source code in python/sp_variant/variant.py
class VariantFileError(VariantError):
    """A filesystem-related error occurred."""

VariantKeyError

Bases: VariantError

A variant with an unknown name was requested.

Source code in python/sp_variant/variant.py
class VariantKeyError(VariantError):
    """A variant with an unknown name was requested."""

VariantRemoteError

Bases: VariantError

An error occurred while communicating with a remote host.

Source code in python/sp_variant/variant.py
class VariantRemoteError(VariantError):
    """An error occurred while communicating with a remote host."""

    hostname: str
    """The name of the remote host that we could not communicate with."""

    msg: str
    """The description of the error that occurred."""

    def __init__(self, hostname: str, msg: str) -> None:
        """Store the hostname and the error message."""
        super().__init__()
        self.hostname = hostname
        self.msg = msg

    def __str__(self) -> str:
        """Return a human-readable representation of the error."""
        return f"{self.hostname}: {self.msg}"
hostname: str = hostname instance-attribute

The name of the remote host that we could not communicate with.

msg: str = msg instance-attribute

The description of the error that occurred.

__init__(hostname, msg)

Store the hostname and the error message.

Source code in python/sp_variant/variant.py
def __init__(self, hostname: str, msg: str) -> None:
    """Store the hostname and the error message."""
    super().__init__()
    self.hostname = hostname
    self.msg = msg
__str__()

Return a human-readable representation of the error.

Source code in python/sp_variant/variant.py
def __str__(self) -> str:
    """Return a human-readable representation of the error."""
    return f"{self.hostname}: {self.msg}"

detect_variant(cfg=_DEFAULT_CONFIG)

Detect the build variant for the current host.

Source code in python/sp_variant/variant.py
def detect_variant(cfg: Config = _DEFAULT_CONFIG) -> Variant:
    """Detect the build variant for the current host."""
    vbuild.build_variants(cfg)
    cfg.diag("Trying to detect the current hosts's build variant")

    if (var := _detect_from_os_release(cfg)) is not None:
        return var

    if (var := _detect_from_files(cfg)) is not None:
        return var

    raise VariantDetectError("Could not detect the current host's build variant")

get_all_variants(cfg=_DEFAULT_CONFIG)

Return information about all the supported variants.

Source code in python/sp_variant/variant.py
def get_all_variants(cfg: Config = _DEFAULT_CONFIG) -> dict[str, Variant]:
    """Return information about all the supported variants."""
    vbuild.build_variants(cfg)
    return dict(vbuild.VARIANTS)

get_all_variants_in_order(cfg=_DEFAULT_CONFIG)

Return information about all supported variants in detect order.

Source code in python/sp_variant/variant.py
def get_all_variants_in_order(cfg: Config = _DEFAULT_CONFIG) -> list[Variant]:
    """Return information about all supported variants in detect order."""
    vbuild.build_variants(cfg)
    return list(vbuild.DETECT_ORDER)

get_by_alias(alias, cfg=_DEFAULT_CONFIG)

Return the variant with the specified name.

Source code in python/sp_variant/variant.py
def get_by_alias(alias: str, cfg: Config = _DEFAULT_CONFIG) -> Variant:
    """Return the variant with the specified name."""
    vbuild.build_variants(cfg)
    for var in vbuild.VARIANTS.values():
        if var.builder.alias == alias:
            return var
    raise VariantKeyError(f"No variant with alias {alias}")

get_variant(name, cfg=_DEFAULT_CONFIG)

Return the variant with the specified name.

Source code in python/sp_variant/variant.py
def get_variant(name: str, cfg: Config = _DEFAULT_CONFIG) -> Variant:
    """Return the variant with the specified name."""
    vbuild.build_variants(cfg)
    try:
        return vbuild.VARIANTS[name]
    except KeyError as err:
        raise VariantKeyError(f"No variant named {name}") from err

list_all_packages(var, patterns=None)

Parse the output of the "list installed packages" command.

Source code in python/sp_variant/variant.py
def list_all_packages(var: Variant, patterns: Iterable[str] | None = None) -> list[defs.OSPackage]:
    """Parse the output of the "list installed packages" command."""
    cmd: Final = list(var.commands.package.list_all)
    if patterns is not None:
        cmd.extend(patterns)

    res: Final = []
    for line in subprocess.check_output(cmd, shell=False).decode("UTF-8").splitlines():
        fields = line.split("\t")
        if len(fields) != 4:
            raise VariantFileError(f"Unexpected line in the '{shlex.join(cmd)}' output: {line!r}")
        # This may need updating at some point, but it'll work for now
        if not fields[3].startswith("ii"):
            continue

        res.append(
            defs.OSPackage(
                name=fields[0],
                version=fields[1],
                arch=fields[2],
                status="installed",
            ),
        )

    return res

update_namedtuple(data, updates)

Create a new named tuple with some updated values.

Source code in python/sp_variant/vbuild.py
def update_namedtuple(data: _TNamedTuple, updates: dict[str, Any]) -> _TNamedTuple:
    """Create a new named tuple with some updated values."""
    if not updates:
        return data
    fields: Final[tuple[str, ...]] = data._fields

    newv: Final = {name: getattr(data, name) for name in fields}
    prefix: Final = f"Internal error: could not update {newv} with {updates}"

    for name, value in updates.items():
        if name not in newv:
            raise defs.VariantConfigError(f"{prefix}: unexpected field {name}")
        orig = newv[name]

        for vtype, handler in _UPDATE_HANDLERS:
            if isinstance(value, vtype):
                newv[name] = handler(prefix, name, orig, value)
                break
        else:
            raise defs.VariantConfigError(
                f"{prefix}: weird {type(value).__name__} update for {name}",
            )

    updated: Final[_TNamedTuple] = type(data)(**newv)  # type: ignore[call-overload]
    return updated

defs: some additional definitions and substructures

sp_variant.defs

Common definitions for the OS/distribution variant detection library.

Builder

Bases: NamedTuple

StorPool builder data.

Source code in python/sp_variant/defs.py
class Builder(NamedTuple):
    """StorPool builder data."""

    alias: str
    """The builder name."""

    base_image: str
    """The base Docker image that the builder is generated from."""

    branch: str
    """The branch used by the sp-pkg tool to specify the variant."""

    kernel_package: str
    """The base kernel OS package."""

    utf8_locale: str
    """The name of the locale to use for clean UTF-8 output."""
alias: str instance-attribute

The builder name.

base_image: str instance-attribute

The base Docker image that the builder is generated from.

branch: str instance-attribute

The branch used by the sp-pkg tool to specify the variant.

kernel_package: str instance-attribute

The base kernel OS package.

utf8_locale: str instance-attribute

The name of the locale to use for clean UTF-8 output.

Commands

Bases: NamedTuple

Variant-specific commands, mainly related to the packaging system.

Source code in python/sp_variant/defs.py
class Commands(NamedTuple):
    """Variant-specific commands, mainly related to the packaging system."""

    package: CommandsPackage
    """Commands related to installing packages from upstream repositories."""

    pkgfile: CommandsPkgFile
    """Commands related to installing packages from locally-fetched files."""
package: CommandsPackage instance-attribute

Commands related to installing packages from upstream repositories.

pkgfile: CommandsPkgFile instance-attribute

Commands related to installing packages from locally-fetched files.

CommandsPackage

Bases: NamedTuple

Variant-specific commands related to OS packages.

Source code in python/sp_variant/defs.py
class CommandsPackage(NamedTuple):
    """Variant-specific commands related to OS packages."""

    update_db: list[str]
    """Make the package manager fetch new data from the upstream repositories."""

    install: list[str]
    """Install one or more packages from the upstream repositories."""

    list_all: list[str]
    """List the currently installed packages."""

    purge: list[str]
    """Remove a package and all its files, including configuration ones."""

    remove: list[str]
    """Remove a package and all its files, possibly leaving configuration ones."""

    remove_impl: list[str]
    """Remove a package using the low-level OS package manager."""
install: list[str] instance-attribute

Install one or more packages from the upstream repositories.

list_all: list[str] instance-attribute

List the currently installed packages.

purge: list[str] instance-attribute

Remove a package and all its files, including configuration ones.

remove: list[str] instance-attribute

Remove a package and all its files, possibly leaving configuration ones.

remove_impl: list[str] instance-attribute

Remove a package using the low-level OS package manager.

update_db: list[str] instance-attribute

Make the package manager fetch new data from the upstream repositories.

CommandsPkgFile

Bases: NamedTuple

Variant-specific commands related to OS package files.

Source code in python/sp_variant/defs.py
class CommandsPkgFile(NamedTuple):
    """Variant-specific commands related to OS package files."""

    dep_query: list[str]
    """List the packages that the one in the specified package file depends on."""

    install: list[str]
    """Install a package from a locally-fetched file."""
dep_query: list[str] instance-attribute

List the packages that the one in the specified package file depends on.

install: list[str] instance-attribute

Install a package from a locally-fetched file.

Config dataclass

Runtime configuration for the sp-variant library functions.

Source code in python/sp_variant/defs.py
@dataclasses.dataclass(frozen=True)
class Config:
    """Runtime configuration for the sp-variant library functions."""

    args: list[str] | None = None
    """Additional arguments passed to the command."""

    command: str | None = None
    """The main argument: a command to execute, a variant specification to show, etc."""

    noop: bool = False
    """No-operation mode; display what would have been done."""

    repodir: pathlib.Path | None = None
    """The path to the directory containing the `add-storpool-repo` data files to install."""

    repotype: RepoType = REPO_TYPES[0]
    """Which StorPool repository to configure."""

    verbose: bool = False
    """Verbose operation; display diagnostic output."""

    def diag(self, msg: str) -> None:
        """Output a diagnostic message in verbose mode."""
        if self.verbose:
            print(msg, file=sys.stderr)  # noqa: T201

    @property
    def _diag_to_stderr(self) -> bool:
        """We always send the diagnostic messages to stderr now."""
        return True

    @_diag_to_stderr.setter
    def _diag_to_stderr(self, value: bool) -> None:
        """Simulate setting the property, do nothing instead."""

    # OK, this is a bit ugly. It's going away soon.
    def _do_setattr(self, name: str, value: Any) -> None:  # noqa: ANN401
        """Ignore any attempts to set the `_diag_to_stderr` member."""
        if name == "_diag_to_stderr":
            return

        _config_orig_setattr(self, name, value)
args: list[str] | None = None class-attribute instance-attribute

Additional arguments passed to the command.

command: str | None = None class-attribute instance-attribute

The main argument: a command to execute, a variant specification to show, etc.

noop: bool = False class-attribute instance-attribute

No-operation mode; display what would have been done.

repodir: pathlib.Path | None = None class-attribute instance-attribute

The path to the directory containing the add-storpool-repo data files to install.

repotype: RepoType = REPO_TYPES[0] class-attribute instance-attribute

Which StorPool repository to configure.

verbose: bool = False class-attribute instance-attribute

Verbose operation; display diagnostic output.

diag(msg)

Output a diagnostic message in verbose mode.

Source code in python/sp_variant/defs.py
def diag(self, msg: str) -> None:
    """Output a diagnostic message in verbose mode."""
    if self.verbose:
        print(msg, file=sys.stderr)  # noqa: T201

DebRepo

Bases: NamedTuple

Debian package repository data.

Source code in python/sp_variant/defs.py
class DebRepo(NamedTuple):
    """Debian package repository data."""

    codename: str
    """The distribution codename (e.g. "buster")."""

    vendor: str
    """The distribution vendor ("debian", "ubuntu", etc.)."""

    sources: str
    """The APT sources list file to copy to /etc/apt/sources.list.d/."""

    keyring: str
    """The GnuPG keyring file to copy to /usr/share/keyrings/."""

    req_packages: list[str]
    """OS packages that need to be installed before `apt-get update` is run."""
codename: str instance-attribute

The distribution codename (e.g. "buster").

keyring: str instance-attribute

The GnuPG keyring file to copy to /usr/share/keyrings/.

req_packages: list[str] instance-attribute

OS packages that need to be installed before apt-get update is run.

sources: str instance-attribute

The APT sources list file to copy to /etc/apt/sources.list.d/.

vendor: str instance-attribute

The distribution vendor ("debian", "ubuntu", etc.).

Detect

Bases: NamedTuple

Check whether this host is running this particular OS variant.

Source code in python/sp_variant/defs.py
class Detect(NamedTuple):
    """Check whether this host is running this particular OS variant."""

    filename: str
    """The name of the file to read."""

    regex: Pattern[str]
    """The regular expression pattern to look for in the file."""

    os_id: str
    """The "ID" field in the /etc/os-release file."""

    os_version_regex: Pattern[str]
    """The regular expression pattern for the "VERSION_ID" os-release field."""
filename: str instance-attribute

The name of the file to read.

os_id: str instance-attribute

The "ID" field in the /etc/os-release file.

os_version_regex: Pattern[str] instance-attribute

The regular expression pattern for the "VERSION_ID" os-release field.

regex: Pattern[str] instance-attribute

The regular expression pattern to look for in the file.

OSPackage

Bases: NamedTuple

The attributes of a currently-installed or known OS package.

Source code in python/sp_variant/defs.py
class OSPackage(NamedTuple):
    """The attributes of a currently-installed or known OS package."""

    name: str
    """The package name."""

    version: str
    """The package version."""

    arch: str
    """The system-dependent package architecture name."""

    status: str
    """The system-dependent status of the package (installed, half-installed, removed, etc)."""
arch: str instance-attribute

The system-dependent package architecture name.

name: str instance-attribute

The package name.

status: str instance-attribute

The system-dependent status of the package (installed, half-installed, removed, etc).

version: str instance-attribute

The package version.

RepoType

Bases: NamedTuple

Attributes common to a StorPool package repository.

Source code in python/sp_variant/defs.py
class RepoType(NamedTuple):
    """Attributes common to a StorPool package repository."""

    name: str
    """The name of the StorPool package repository."""

    extension: str
    """The extension to be used in filenames for configurating the package manager."""

    url: str
    """The base URL of the StorPool package repository."""
extension: str instance-attribute

The extension to be used in filenames for configurating the package manager.

name: str instance-attribute

The name of the StorPool package repository.

url: str instance-attribute

The base URL of the StorPool package repository.

Supported

Bases: NamedTuple

The aspects of the StorPool operation supported for this build variant.

Source code in python/sp_variant/defs.py
class Supported(NamedTuple):
    """The aspects of the StorPool operation supported for this build variant."""

    repo: bool
    """Is there a StorPool third-party packages repository?"""
repo: bool instance-attribute

Is there a StorPool third-party packages repository?

Variant

Bases: NamedTuple

The information about a Linux distribution version (build variant).

Source code in python/sp_variant/defs.py
class Variant(NamedTuple):
    """The information about a Linux distribution version (build variant)."""

    name: str
    """The name of the variant, e.g. `ALMA9`, `UBUNTU2204`, etc."""

    descr: str
    """The human-readable description of the variant."""

    parent: str
    """The name of the variant that this one is based on."""

    family: str
    """The OS "family" that this distribution belongs to."""

    detect: Detect
    """The ways to check whether we are running this variant."""

    supported: Supported
    """The aspects of StorPool operation supported for this build variant."""

    commands: Commands
    """The OS commands to execute for particular purposes."""

    min_sys_python: str
    """The minimum Python version that we can depend on."""

    repo: DebRepo | YumRepo
    """The StorPool repository files to install."""

    package: dict[str, str]
    """The names of the packages to be used for this variant."""

    systemd_lib: str
    """The name of the directory to install systemd unit files to."""

    file_ext: str
    """The filename extension of the OS packages ("deb", "rpm", etc.)."""

    initramfs_flavor: str
    """The type of initramfs-generating tools."""

    builder: Builder
    """The data specific to the StorPool builder containers."""
builder: Builder instance-attribute

The data specific to the StorPool builder containers.

commands: Commands instance-attribute

The OS commands to execute for particular purposes.

descr: str instance-attribute

The human-readable description of the variant.

detect: Detect instance-attribute

The ways to check whether we are running this variant.

family: str instance-attribute

The OS "family" that this distribution belongs to.

file_ext: str instance-attribute

The filename extension of the OS packages ("deb", "rpm", etc.).

initramfs_flavor: str instance-attribute

The type of initramfs-generating tools.

min_sys_python: str instance-attribute

The minimum Python version that we can depend on.

name: str instance-attribute

The name of the variant, e.g. ALMA9, UBUNTU2204, etc.

package: dict[str, str] instance-attribute

The names of the packages to be used for this variant.

parent: str instance-attribute

The name of the variant that this one is based on.

repo: DebRepo | YumRepo instance-attribute

The StorPool repository files to install.

supported: Supported instance-attribute

The aspects of StorPool operation supported for this build variant.

systemd_lib: str instance-attribute

The name of the directory to install systemd unit files to.

VariantConfigError

Bases: VariantError

Invalid parameters passed to the variant routines.

Source code in python/sp_variant/defs.py
class VariantConfigError(VariantError):
    """Invalid parameters passed to the variant routines."""

VariantError

Bases: Exception

Base class for errors that occurred during variant processing.

Source code in python/sp_variant/defs.py
class VariantError(Exception):
    """Base class for errors that occurred during variant processing."""

VariantUpdate

Bases: NamedTuple

The changes to be applied to the parent variant definition.

Source code in python/sp_variant/defs.py
class VariantUpdate(NamedTuple):
    """The changes to be applied to the parent variant definition."""

    name: str
    """The name of the new variant."""

    descr: str
    """The description of the new variant."""

    parent: str
    """The variant that the new one is based on."""

    detect: Detect
    """The ways to detect the new variant."""

    updates: dict[str, Any]
    """The changes to be applied to the parent variant's structure."""
descr: str instance-attribute

The description of the new variant.

detect: Detect instance-attribute

The ways to detect the new variant.

name: str instance-attribute

The name of the new variant.

parent: str instance-attribute

The variant that the new one is based on.

updates: dict[str, Any] instance-attribute

The changes to be applied to the parent variant's structure.

YumRepo

Bases: NamedTuple

Yum/DNF package repository data.

Source code in python/sp_variant/defs.py
class YumRepo(NamedTuple):
    """Yum/DNF package repository data."""

    yumdef: str
    """The *.repo file to copy to /etc/yum.repos.d/."""

    keyring: str
    """The keyring file to copy to /etc/pki/rpm-gpg/."""
keyring: str instance-attribute

The keyring file to copy to /etc/pki/rpm-gpg/.

yumdef: str instance-attribute

The *.repo file to copy to /etc/yum.repos.d/.

jsonify(obj)

Return a more readable representation of an object.

Source code in python/sp_variant/defs.py
def jsonify(obj: Any) -> Any:  # noqa: ANN401  # this needs to operate on, well, anything
    """Return a more readable representation of an object."""
    if type(obj).__name__.endswith("Pattern") and hasattr(obj, "pattern"):
        return jsonify(obj.pattern)

    if hasattr(obj, "_asdict"):
        return {name: jsonify(value) for name, value in obj._asdict().items()}
    if isinstance(obj, dict):
        return {name: jsonify(value) for name, value in obj.items()}

    if isinstance(obj, list):
        return [jsonify(item) for item in obj]

    return obj