duck.cli.commands.sync.backends

Thin wrappers around real package managers. Each backend only knows how to check and install packages for one tool, so Duck Sync stays a thin coordination layer instead of a package manager of its own.

Module Contents

Classes

AptBackend

Debian/Ubuntu package manager backend.

BrewBackend

Homebrew backend for macOS.

ChocoBackend

Chocolatey backend for Windows.

CustomBackend

Escape hatch for package managers not natively supported by Duck Sync.

DnfBackend

Fedora/RHEL package manager backend.

PackageBackend

Base interface every system package manager wrapper implements.

PacmanBackend

Arch Linux package manager backend.

PythonBackend

Installs python packages, preferring uv when present since it is faster and already used across Duck Framework projects.

TermuxBackend

Backend for Termux on Android, which wraps apt/dpkg through its own pkg command and runs as an unprivileged user with no sudo binary.

Functions

get_backend

Instantiates a system package backend by name or custom command string.

Data

BACKEND_REGISTRY

API

class duck.cli.commands.sync.backends.AptBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Debian/Ubuntu package manager backend.

Initialization

base_command

[‘apt-get’, ‘install’, ‘-y’]

is_installed(package: str) bool[source]
name

‘apt’

duck.cli.commands.sync.backends.BACKEND_REGISTRY: dict[str, type[duck.cli.commands.sync.backends.PackageBackend]]

None

class duck.cli.commands.sync.backends.BrewBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Homebrew backend for macOS.

Initialization

base_command

[‘brew’, ‘install’]

is_installed(package: str) bool[source]
name

‘brew’

sudo_capable

False

class duck.cli.commands.sync.backends.ChocoBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Chocolatey backend for Windows.

Initialization

base_command

[‘choco’, ‘install’, ‘-y’]

is_installed(package: str) bool[source]
name

‘choco’

sudo_capable

False

class duck.cli.commands.sync.backends.CustomBackend(command: str, use_sudo: bool = False, sudo_capable: bool = True, **kwargs)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Escape hatch for package managers not natively supported by Duck Sync.

Accepts any install command string such as "nix-env -iA nixpkgs" or "some_command -i". The package name is appended as a trailing positional argument when :meth:install is called.

is_installed always returns False so every package is attempted on every run. Override is_installed by subclassing if you need a smarter check.

Parameters:
  • command – Shell-style install command prefix, e.g. "brew install" or "some_command -i". Split with :func:shlex.split.

  • use_sudo – Whether to prepend sudo to the resolved command. Only applied when :attr:sudo_capable is True on this instance.

  • sudo_capable – Whether the custom command supports sudo. Defaults to True so callers can opt in or out explicitly.

Initialization

is_installed(package: str) bool[source]
Parameters:

package – Package name (unused; always returns False).

Returns:

Always False; the custom backend never skips an install.

name

‘custom’

class duck.cli.commands.sync.backends.DnfBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Fedora/RHEL package manager backend.

Initialization

base_command

[‘dnf’, ‘install’, ‘-y’]

is_installed(package: str) bool[source]
name

‘dnf’

class duck.cli.commands.sync.backends.PackageBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: abc.ABC

Base interface every system package manager wrapper implements.

Parameters:

use_sudo – Whether to prefix install commands with sudo. Backends that never use sudo (brew, choco) ignore this flag.

Initialization

base_command: list[str]

None

install(package: str, extra_args: list[str] | None = None, dry_run: bool = False) None[source]

Installs a package using this backend’s native command.

Parameters:
  • package – Backend-specific package name. May contain multiple space-separated packages (e.g. “gdal-bin libgdal-dev”).

  • extra_args – Extra flags passed straight through to the underlying package manager, e.g. [“–no-install-recommends”].

  • dry_run – When True, only prints the command without running it.

Raises:

InstallationError – If the install command exits non-zero.

property install_command: list[str]
Returns:

The install command prefix, with sudo prepended only when this backend supports it and use_sudo is enabled.

abstractmethod is_installed(package: str) bool[source]

Checks whether a package is already present on the system.

Parameters:

package – Backend-specific package name.

Returns:

True if the package appears to already be installed.

name: str

None

on_install_failed(package: str, reason: str) None[source]

Called when a dependency fails to install.

Parameters:
  • package – The package that failed.

  • reason – Human-readable failure reason.

on_install_finished(package: str) None[source]

Called after a dependency has been installed successfully.

Parameters:

package – The package that was installed.

on_install_started(package: str) None[source]

Called immediately before a dependency installation begins.

Parameters:

package – The package about to be installed.

run(command: list[str], package: str) None[source]

Executes an install command and translates failures.

Parameters:
  • command – Full command line to run.

  • package – Name used in the raised error for context.

Raises:

InstallationError – If the command fails.

sudo_capable: bool

True

class duck.cli.commands.sync.backends.PacmanBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Arch Linux package manager backend.

Initialization

base_command

[‘pacman’, ‘-S’, ‘–noconfirm’]

is_installed(package: str) bool[source]
name

‘pacman’

class duck.cli.commands.sync.backends.PythonBackend(use_sudo: bool = False, **kwargs)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Installs python packages, preferring uv when present since it is faster and already used across Duck Framework projects.

Initialization

property base_command: list[str]
Returns:

The install command prefix for whichever tool is active, e.g. [“uv”, “pip”, “install”] or [“pip”, “install”].

install_requirements(path: pathlib.Path, extra_args: list[str] | None = None, dry_run: bool = False) None[source]

Installs every package listed in a requirements.txt file.

Parameters:
  • path – Resolved path to the requirements file.

  • extra_args – Extra flags such as [“–no-cache-dir”].

  • dry_run – When True, only prints the command without running it.

Raises:

InstallationError – If the install command exits non-zero.

is_installed(package: str) bool[source]

Check whether a Python package is installed and satisfies the requested version constraint.

Parameters:

package – Package requirement string, e.g. "django>=5.0" or "requests[socks]".

Returns:

True if the package is installed and satisfies the requirement.

name

‘python’

class duck.cli.commands.sync.backends.TermuxBackend(use_sudo: bool = True, on_install_started: Callable = None, on_install_finished: Callable = None, on_install_failed: Callable = None)[source]

Bases: duck.cli.commands.sync.backends.PackageBackend

Backend for Termux on Android, which wraps apt/dpkg through its own pkg command and runs as an unprivileged user with no sudo binary.

Initialization

base_command

[‘pkg’, ‘install’, ‘-y’]

is_installed(package: str) bool[source]
name

‘termux’

sudo_capable

False

duck.cli.commands.sync.backends.get_backend(name: str, use_sudo: bool = True, **backend_kwargs) duck.cli.commands.sync.backends.PackageBackend[source]

Instantiates a system package backend by name or custom command string.

When name is a key in BACKEND_REGISTRY the matching built-in backend is returned. Otherwise the value is treated as a raw shell-style install command and wrapped in a CustomBackend, letting callers force any arbitrary tool:

    get_backend("apt")             # built-in AptBackend
    get_backend("brew")                # built-in BrewBackend
    get_backend("some_command -i")     # CustomBackend("some_command -i")
    get_backend("nix-env -iA nixpkgs") # CustomBackend("nix-env -iA nixpkgs")
Parameters:
  • name – Backend identifier ("apt", "brew" …) or a shell-style install command prefix ("some_command -i").

  • use_sudo – Whether install commands should be prefixed with sudo. Ignored by built-in backends that are never sudo-capable, and forwarded to :class:CustomBackend for raw commands.

  • **backend_kwargs – Extra keyword arguments to provide to backend class.

Returns:

A ready-to-use :class:PackageBackend instance.