abx-pkg Β Β Β Β π¦ aptΒ brewΒ pipΒ uvΒ npmΒ pnpmΒ yarnΒ bunΒ denoΒ cargoΒ gemΒ gogetΒ nixΒ dockerΒ bashΒ puppeteerΒ playwrightΒ chromewebstoreΒ ansibleΒ pyinfra
Simple Python interfaces for package managers + installed binaries.
It's an ORM for your package managers, providing a nice python types for packages + installers.
This is a Python library for installing & managing packages locally with a variety of package managers.
It's designed for when requirements.txt isn't enough, and you have to detect or install dependencies at runtime. It's great for installing and managing MCP servers and their dependencies at runtime.
pip install abx-pkgfrom abx_pkg import Binary, npm
curl = Binary(name="curl").load()
print(curl.abspath, curl.version, curl.exec(cmd=["--version"]))
npm.install("puppeteer")π¦ Provides consistent interfaces for runtime dependency resolution & installation across multiple package managers & OSs β¨ Built with
pydanticv2 for strong static typing guarantees and easy conversion to/from json π Usable withdjango>= 4.0,django-ninja, and OpenAPI +django-jsonformto build UIs & APIs π¦ Driver layer can bepyinfra/ansible/ or built-inabx-pkgengine
Built by ArchiveBox to install & auto-update our extractor dependencies at runtime (chrome, wget, curl, etc.) on macOS/Linux/Docker.
Source Code: https://github.com/ArchiveBox/abx-pkg/
Documentation: https://github.com/ArchiveBox/abx-pkg/blob/main/README.md
from abx_pkg import Binary, apt, brew, pip, npm, env
# Provider singletons are available as simple imports β no manual instantiation needed
dependencies = [
Binary(name='curl', binproviders=[env, apt, brew]),
Binary(name='yt-dlp', binproviders=[env, pip, uv, apt, brew]),
Binary(name='playwright', binproviders=[env, npm, pnpm]),
Binary(name='chromium', binproviders=[playwright, puppeteer, apt]),
Binary(name='postgres', binproviders=[docker, env, apt, brew]),
]
for binary in dependencies:
binary = binary.install()
print(binary.abspath, binary.version, binary.binprovider, binary.is_valid, binary.sha256)
# Path(...) SemVer(...) EnvProvider()/AptProvider()/BrewProvider()/PipProvider()/NpmProvider() True '<sha256>'
binary.exec(cmd=['--version']) # curl 7.81.0 (x86_64-apple-darwin23.0) libcurl/7.81.0 ...Tip
π Stay safe from supply-chain attcaks with abx-pkg: We default to safe behavior (when providers allow):
min_release_age=7(we only install packages that have been published for 7 days or longer)postinstall_scripts=False(we don't run post-install scripts for packages by default)install_root=~/.config/abx/lib(CLI defaults to installing packages in a dedicated prefix so host system stays clean)
You can customize these defaults on Binary or BinProvider, or with ABX_PKG_MIN_RELEASE_AGE/ABX_PKG_POSTINSTALL_SCRIPTS=/ABX_PKG_NO_CACHE/ABX_PKG_LIB_DIR (see Configuration below).
pip install abx-pkg
# or
uv tool add abx-pkgInstalling abx-pkg also provides an abx-pkg CLI entrypoint:
abx-pkg --version
abx-pkg install yt-dlp
abx-pkg update yt-dlp
abx-pkg uninstall yt-dlp
abx-pkg load yt-dlpabx-pkg run yt-dlp --help # resolves yt-dlp via the configured providers and execs it
abx-pkg --binproviders=pip,brew run pip show black # restrict provider resolution (exercises PipProvider.exec)
abx-pkg --binproviders=pip --install run yt-dlp # load first, then install via selected providers if needed
abx-pkg --binproviders=pip --update run yt-dlp # ensure the binary is available, then update before exec
abx-pkg --binproviders=pip --no-cache --install run yt-dlp # bypass cached/current-state checks during resolution + installabx-pkg options (e.g. --binproviders, --lib, --install, --update, --no-cache) must appear before the run subcommand; every argument after the binary name is forwarded verbatim to the underlying binary. run exits with the child's exit code, passes its stdout/stderr through unbuffered, and routes any abx-pkg install/load logs to stderr only β no headers, no footers, no parsing.
Think npx / uvx / pipx run β but for every package manager abx-pkg supports. abx is a thin alias for abx-pkg --install run ...: it resolves the binary via the configured providers, installs it if missing, then execs it with the forwarded arguments.
abx yt-dlp --help # auto-install (if needed) and run yt-dlp
abx --update yt-dlp --help # ensure the binary is available, then update before running
abx --binproviders=env,uv,pip,apt,brew yt-dlp # restrict provider resolutionOptions before the binary name (--lib, --binproviders, --dry-run, --debug, --no-cache, --update) are forwarded to abx-pkg; everything after the binary name is forwarded to the binary itself.
Every Binary / BinProvider configuration field is exposed as a CLI flag on the group and on subcommands (install, update, uninstall, load), and is also available to run / abx via group-level flags placed before the binary name. Providers that can't enforce a given option emit a warning to stderr and continue β no hard failure.
abx-pkg --min-version=1.2.3 --min-release-age=7 install yt-dlp
abx-pkg --postinstall-scripts=False --binproviders=apt,uv,pip install black
abx-pkg --no-cache install black
abx-pkg --install-root=/tmp/yt-dlp-root --bin-dir=/tmp/yt-dlp-bin install yt-dlp
abx-pkg --overrides='{"pip":{"install_args":["yt-dlp[default]"]}}' install yt-dlp
abx-pkg --install-timeout=600 --version-timeout=20 --euid=1000 install yt-dlp
abx --min-version=2024.1.1 --min-release-age=0 yt-dlp --help| Flag | Type | Meaning |
|---|---|---|
--min-version=SEMVER |
str |
Minimum acceptable version (set on Binary.min_version). |
--postinstall-scripts[=BOOL] |
bool |
Allow post-install scripts. Bare --postinstall-scripts = True. Providers that can't disable them warn-and-ignore. |
--min-release-age=DAYS |
float |
Minimum days since publication. Non-supporting providers warn-and-ignore. |
--no-cache[=BOOL] |
bool |
Skip cached/current-state checks and force fresh install/update/load probes. Bare --no-cache = True. |
--overrides=JSON |
dict |
Per-provider Binary.overrides handler replacements (install_args, abspath, version, β¦). |
--install-root=PATH |
Path |
Override the per-provider install directory. |
--bin-dir=PATH |
Path |
Override the per-provider bin directory. |
--euid=UID |
int |
Pin the UID used when providers shell out. |
--install-timeout=SECONDS |
int |
Seconds to wait for install/update/uninstall subprocesses. |
--version-timeout=SECONDS |
int |
Seconds to wait for version/metadata probes. |
--dry-run[=BOOL] |
bool |
Show installer commands without executing them. Bare --dry-run = True. |
--debug[=BOOL] |
bool |
Emit DEBUG logs to stderr. Bare --debug = True. Defaults to ABX_PKG_DEBUG or False. |
Every value-taking flag also accepts the literal string None / null / nil / "" to reset to the provider's built-in default (or its env-var-backed default). The precedence is: explicit per-subcommand flag > group-level flag > environment variable > built-in default.
abx-pkg install --binproviders=env,uv,pip,apt,brew prettier
# or
env ABX_PKG_BINPROVIDERS=env,uv,pip,apt,brew abx-pkg install yt-dlpabx-pkg --lib=~/.config/abx/lib install yt-dlp # the default behavior
abx-pkg --lib=./vendor install yt-dlp # store all packages under $PWD/vendor
abx-pkg --lib=/tmp/abxlib install yt-dlp # store all packages under /tmp/abxlib
# or
env ABX_PKG_LIB_DIR=/any/dir/path abx-pkg install yt-dlpabx-pkg install --dry-run some-dangerous-package # outputs commands that would be run without executing them
# or
env ABX_PKG_DRY_RUN=1 abx-pkg install some-dangerous-packageCLI result lines are written to stdout. Progress logging is written to stderr at INFO by default. Enable DEBUG logging with ABX_PKG_DEBUG=1 or --debug.
All built-in providers are available as lazy singletons β just import them by name:
from abx_pkg import apt, brew, pip, npm, env
apt.install('curl')
env.load('wget')These are instantiated on first access and cached for reuse. If you need custom configuration, you can still instantiate provider classes directly:
from pathlib import Path
from abx_pkg import PipProvider
custom_pip = PipProvider(install_root=Path("/tmp/abx-pkg-venv"), min_release_age=0)Use the Binary class to declare a package that can be installed by one of several ordered providers, with an optional version floor:
from abx_pkg import Binary, SemVer, env, brew
curl = Binary(
name="curl",
min_version=SemVer("8.0.0"),
binproviders=[env, brew],
).install()min_version is enforced after a provider resolves or installs a binary β provider discovery can still succeed, but the final Binary is rejected if the loaded version is below the floor. Use min_version=None to disable the check.
Pass no_cache=True to load() / install() / update() / uninstall() when you want to bypass cached/current-state checks. For install(), no_cache=True skips the initial load() check and forces a fresh install path. The equivalent CLI and env controls are --no-cache and ABX_PKG_NO_CACHE=1.
from pydantic import InstanceOf
from abx_pkg import BinProvider, Binary, BinProviderName, BinName, HandlerDict, BrewProvider
from abx_pkg import env, pip, apt
class CustomBrewProvider(BrewProvider):
name: BinProviderName = 'custom_brew'
def get_macos_packages(self, bin_name: str, **context) -> list[str]:
return ['yt-dlp'] if bin_name == 'ytdlp' else [bin_name]
class YtdlpBinary(Binary):
name: BinName = 'ytdlp'
description: str = 'YT-DLP (Replacement for YouTube-DL) Media Downloader'
# define the providers this binary supports
binproviders: list[InstanceOf[BinProvider]] = [env, pip, apt, CustomBrewProvider()]
# customize installed package names for specific package managers
overrides: dict[BinProviderName, HandlerDict] = {
'pip': {'install_args': ['yt-dlp[default,curl-cffi]']}, # literal values
'apt': {'install_args': lambda: ['yt-dlp', 'ffmpeg']}, # any pure Callable
'custom_brew': {'install_args': 'self.get_macos_packages'}, # or a string ref to a method on self
}
ytdlp = YtdlpBinary().install()
print(ytdlp.binprovider) # EnvProvider(...) / PipProvider(...) / AptProvider(...) / CustomBrewProvider(...)
print(ytdlp.abspath) # Path(...)
print(ytdlp.version) # SemVer(...)
print(ytdlp.is_valid) # True
# Lifecycle actions preserve the Binary type and refresh/clear loaded metadata as needed
ytdlp = ytdlp.update()
assert ytdlp.is_valid
ytdlp = ytdlp.uninstall()
assert ytdlp.abspath is None and ytdlp.version is Nonefrom abx_pkg import Binary, apt, brew, env
# Use providers directly for package manager operations
apt.install('wget')
print(apt.PATH, apt.get_abspaths('wget'), apt.get_version('wget'))
# our Binary API provides a nice type-checkable, validated, serializable handle
ffmpeg = Binary(name='ffmpeg', binproviders=[env, apt, brew]).load()
print(ffmpeg) # Binary(name='ffmpeg', abspath=Path(...), version=SemVer(...), sha256='...')
print(ffmpeg.abspaths) # show all matching binaries found via each provider PATH
print(ffmpeg.model_dump(mode='json')) # JSON-ready dict
print(ffmpeg.model_json_schema()) # ... OpenAPI-ready JSON schema showing all available fieldsfrom pydantic import InstanceOf
from abx_pkg import Binary, BinProvider, BrewProvider, EnvProvider
# You can also instantiate provider classes manually for custom configuration,
# or define binaries as classes for type checking
class CurlBinary(Binary):
name: str = 'curl'
binproviders: list[InstanceOf[BinProvider]] = [BrewProvider(), EnvProvider()]
curl = CurlBinary().install()
assert isinstance(curl, CurlBinary) # CurlBinary is a unique type you can use in annotations now
print(curl.abspath, curl.version, curl.binprovider, curl.is_valid) # Path(...) SemVer(...) BrewProvider()/EnvProvider() True
curl.exec(cmd=['--version']) # curl 8.4.0 (x86_64-apple-darwin23.0) libcurl/8.4.0 ...import os
import platform
from pydantic import InstanceOf
from abx_pkg import BinProvider, Binary, BinProviderName, BinName, HandlerDict
from abx_pkg import env, apt
class DockerBinary(Binary):
name: BinName = 'docker'
binproviders: list[InstanceOf[BinProvider]] = [env, apt]
overrides: dict[BinProviderName, HandlerDict] = {
'env': {
# prefer podman if installed, fall back to docker
'abspath': lambda: os.which('podman') or os.which('docker') or os.which('docker-ce'),
},
'apt': {
# vary the installed package name based on CPU architecture
'install_args': {
'amd64': ['docker'],
'armv7l': ['docker-ce'],
'arm64': ['docker-ce'],
}.get(platform.machine(), 'docker'),
},
}
docker = DockerBinary().install()from pathlib import Path
from abx_pkg import (
BinProvider,
BinProviderName,
BinName,
HostBinPath,
InstallArgs,
SemVer,
bin_abspath,
)
class CargoProvider(BinProvider):
name: BinProviderName = 'cargo'
INSTALLER_BIN: BinName = 'cargo'
PATH: str = str(Path.home() / '.cargo/bin')
def default_install_args_handler(self, bin_name: BinName, **context) -> InstallArgs:
return [bin_name]
def default_install_handler(
self,
bin_name: BinName,
install_args: InstallArgs | None = None,
postinstall_scripts: bool | None = None,
min_release_age: float | None = None,
min_version: SemVer | None = None,
timeout: int | None = None,
) -> str:
install_args = install_args or self.get_install_args(bin_name)
installer = self._require_installer_bin()
proc = self.exec(bin_name=installer, cmd=['install', *install_args], timeout=timeout)
if proc.returncode != 0:
self._raise_proc_error('install', install_args, proc)
return proc.stdout.strip() or proc.stderr.strip()
def default_abspath_handler(self, bin_name: BinName, **context) -> HostBinPath | None:
return bin_abspath(bin_name, PATH=self.PATH)
def default_version_handler(
self,
bin_name: BinName,
abspath: HostBinPath | None = None,
timeout: int | None = None,
**context,
) -> SemVer | None:
return self._version_from_exec(bin_name, abspath=abspath, timeout=timeout)
cargo = CargoProvider()
rg = cargo.install(bin_name='ripgrep')
print(rg.binprovider) # CargoProvider(...)
print(rg.version) # SemVer(...)abx-pkg uses the standard Python logging module. By default it stays quiet unless your application configures logging explicitly.
import logging
from abx_pkg import Binary, env, configure_logging
configure_logging(logging.INFO)
python = Binary(name='python', binproviders=[env]).load()To enable Rich logging:
pip install "abx-pkg[rich]"import logging
from abx_pkg import Binary, EnvProvider, configure_rich_logging
configure_rich_logging(logging.DEBUG)
python = Binary(name='python', binproviders=[EnvProvider()]).load()Debug logging is hardened so logging itself does not become the failure. If a provider/model object has a broken or overly-expensive repr(), abx-pkg falls back to a short ClassName(...) summary instead of raising while formatting log output.
configure_rich_logging(...) uses rich.logging.RichHandler under the hood, so log levels, paths, arguments, and command lines render with terminal colors when supported.
You can also manage it with standard logging primitives:
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger("abx_pkg").setLevel(logging.DEBUG)With a few more packages, you get type-checked Django fields & forms that support BinProvider and Binary.
[!TIP] For the full Django experience, we recommend installing these 3 excellent packages:
django-admin-data-viewsdjango-pydantic-fielddjango-jsonformpip install abx-pkg django-admin-data-views django-pydantic-field django-jsonform
Django model fields:
from django.db import models
from abx_pkg import BinProvider, Binary, SemVer
from django_pydantic_field import SchemaField
class Dependency(models.Model):
label = models.CharField(max_length=63)
default_binprovider: BinProvider = SchemaField()
binaries: list[Binary] = SchemaField(default=[])
min_version: SemVer = SchemaField(default=(0, 0, 1))Saving a Binary using the model:
from abx_pkg import Binary, env
curl = Binary(name='curl').load()
obj = Dependency(
label='runtime tools',
default_binprovider=env, # store BinProvider values directly
binaries=[curl], # store Binary/SemVer values directly
)
obj.save()When fetching back from the DB, Binary fields are auto-deserialized and immediately usable:
obj = Dependency.objects.get(label='runtime tools')
assert obj.binaries[0].abspath == curl.abspath
obj.binaries[0].exec(cmd=['--version'])For a full example see the bundled django_example_project/.
Django Admin integration:
# settings.py
INSTALLED_APPS = [
# ...
'admin_data_views',
'abx_pkg',
]
ABX_PKG_GET_ALL_BINARIES = 'project.views.get_all_binaries'
ABX_PKG_GET_BINARY = 'project.views.get_binary'
ADMIN_DATA_VIEWS = {
"NAME": "Environment",
"URLS": [
{
"route": "binaries/",
"view": "abx_pkg.views.binaries_list_view",
"name": "binaries",
"items": {
"route": "<str:key>/",
"view": "abx_pkg.views.binary_detail_view",
"name": "binary",
},
},
],
}If you override the default site admin, register the views manually:
from abx_pkg.admin import register_admin_views
custom_admin = YourSiteAdmin()
register_admin_views(custom_admin)All abx-pkg env vars are read once at import time and only apply when set. Explicit constructor kwargs always override these defaults.
Behavioral controls (apply across all providers):
| Variable | Default | Effect |
|---|---|---|
ABX_PKG_DRY_RUN / DRY_RUN |
0 |
Flips the shared dry_run default. ABX_PKG_DRY_RUN wins if both are set. Provider subprocesses are logged and skipped, install() / update() return a placeholder, uninstall() returns True. |
ABX_PKG_NO_CACHE |
0 |
Flips the shared no_cache default. When enabled, install() skips the initial load() check and forces a fresh install path, while load() / update() / uninstall() bypass cached probe results. |
ABX_PKG_DEBUG |
0 |
Enables DEBUG-level CLI logging on stderr for abx-pkg / abx. The matching CLI flag is --debug. Default CLI logging level is INFO. |
ABX_PKG_INSTALL_TIMEOUT |
120 |
Seconds to wait for install() / update() / uninstall() handler subprocesses. |
ABX_PKG_VERSION_TIMEOUT |
10 |
Seconds to wait for version / metadata probes (--version, npm show, pip show, etc.). |
ABX_PKG_POSTINSTALL_SCRIPTS |
unset | Hydrates the provider-level default for the postinstall_scripts kwarg on every provider that supports it (pip, uv, npm, pnpm, yarn, bun, deno, brew, chromewebstore, puppeteer). |
ABX_PKG_MIN_RELEASE_AGE |
7 |
Hydrates the provider-level default (in days) for the min_release_age kwarg on every provider that supports it (pip, uv, npm, pnpm, yarn, bun, deno). |
ABX_PKG_BINPROVIDERS |
all | Comma-separated list of provider names to enable (and their order) for the abx-pkg CLI. |
Install-root controls (one global default + one per-provider override):
| Variable | Applies to | Effect |
|---|---|---|
ABX_PKG_LIB_DIR |
providers that use managed roots | Centralized install root. When set, each managed provider defaults its install_root to $ABX_PKG_LIB_DIR/<provider name> (e.g. <lib>/npm, <lib>/pip, <lib>/gem, <lib>/playwright). Accepts relative (./lib), tilde (~/.config/abx/lib), and absolute (/tmp/abxlib) paths. |
ABX_PKG_<BINPROVIDER>_ROOT |
the matching provider's install_root |
Generic per-provider override; beats ABX_PKG_LIB_DIR/<provider name>. Examples: ABX_PKG_PIP_ROOT, ABX_PKG_UV_ROOT, ABX_PKG_NPM_ROOT, ABX_PKG_GOGET_ROOT, ABX_PKG_CHROMEWEBSTORE_ROOT. The <BINPROVIDER> token is the provider name uppercased. |
Install-root precedence (most specific wins): explicit install_root= / provider alias kwarg > ABX_PKG_<NAME>_ROOT > ABX_PKG_LIB_DIR/<name> > provider-native global mode.
Provider-specific binary overrides:
Each provider also honors a <NAME>_BINARY=/abs/path/to/<name> env var to pin the exact executable it shells out to β PIP_BINARY, UV_BINARY, NPM_BINARY, PNPM_BINARY, YARN_BINARY, BUN_BINARY, DENO_BINARY, etc.
Per-Binary / per-BinProvider fields (constructor kwargs, most-specific wins):
min_versioncan be set on any individualBinary.min_release_agecan be set onBinaryorBinProvider, or viaABX_PKG_MIN_RELEASE_AGE(days).postinstall_scriptscan be set onBinaryorBinProvider, or viaABX_PKG_POSTINSTALL_SCRIPTS.no_cachecan be passed per-call toload()/install()/update()/uninstall(), or enabled globally for the CLI viaABX_PKG_NO_CACHE.install_root/bin_dircan be set on anyBinProviderwith an isolated install location, or default toABX_PKG_<NAME>_ROOT/ABX_PKG_LIB_DIR/<provider name>.dry_runcan be set onBinProvideror passed per-call toinstall()/update()/uninstall(), or viaABX_PKG_DRY_RUN/DRY_RUN.install_timeoutcan be set onBinProvideror viaABX_PKG_INSTALL_TIMEOUT(seconds).version_timeoutcan be set onBinProvideror viaABX_PKG_VERSION_TIMEOUT(seconds).euidcan be set onBinProviderto pin the UID used tosudo/drop into when running provider subprocesses; otherwise it's auto-detected frominstall_rootownership.overridesis adict[BinProviderName, HandlerDict](onBinary) ordict[BinName, HandlerDict](onBinProvider) mapping to per-binary / per-provider handler replacements (install_args,abspath,version,install,update,uninstall). See Advanced Usage for examples.
Precedence is always: explicit action kwarg > Binary(...) field > BinProvider(...) field > env var > built-in default.
Built-in implementations: EnvProvider, AptProvider, BrewProvider, PipProvider, UvProvider, NpmProvider, PnpmProvider, YarnProvider, BunProvider, DenoProvider, CargoProvider, GemProvider, GoGetProvider, NixProvider, DockerProvider, PyinfraProvider, AnsibleProvider, BashProvider, ChromeWebstoreProvider, PuppeteerProvider, PlaywrightProvider
This type represents a provider of binaries, e.g. a package manager like apt / pip / npm, or env (which only resolves binaries already present in $PATH).
Every provider exposes the same lifecycle surface:
load()/install()/update()/uninstall()get_install_args()to resolve package names / formulae / image refs / module specsget_abspath()/get_abspaths()/get_version()/get_sha256()
Shared base defaults come from abx_pkg/binprovider.py and apply unless a concrete provider overrides them:
INSTALLER_BIN = "env"
PATH = str(Path(sys.executable).parent)
postinstall_scripts = None # some providers override this with ABX_PKG_POSTINSTALL_SCRIPTS
min_release_age = None # some providers override this with ABX_PKG_MIN_RELEASE_AGE
install_timeout = 120 # or ABX_PKG_INSTALL_TIMEOUT=120
version_timeout = 10 # or ABX_PKG_VERSION_TIMEOUT=10
dry_run = False # or ABX_PKG_DRY_RUN=1 / DRY_RUN=1dry_run: useprovider.get_provider_with_overrides(dry_run=True), passdry_run=Truedirectly toinstall()/update()/uninstall(), or setABX_PKG_DRY_RUN=1/DRY_RUN=1. If both env vars are set,ABX_PKG_DRY_RUNwins. Provider subprocesses are logged and skipped,install()/update()return a placeholder loaded binary, anduninstall()returnsTruewithout mutating the host.no_cache: use--no-cache/ABX_PKG_NO_CACHE=1on the CLI, or passno_cache=Truedirectly toload()/install()/update()/uninstall(). Forinstall(), this skips the initialload()check and forces a fresh install path.install_timeout: shared provider-level timeout used byinstall(),update(), anduninstall()handler execution paths. Can also be set withABX_PKG_INSTALL_TIMEOUT.version_timeout: shared provider-level timeout used by version / metadata probes such as--version,npm show,npm list,pip show,go version -m, and brew lookups. Can also be set withABX_PKG_VERSION_TIMEOUT.postinstall_scriptsandmin_release_ageare standard provider/binary/action kwargs, but only supporting providers hydrate default values fromABX_PKG_POSTINSTALL_SCRIPTSandABX_PKG_MIN_RELEASE_AGE.- Providers that do not support one of those controls leave the provider default as
None. If you pass an explicit unsupported value duringinstall()/update(), it is logged as a warning and ignored. - Precedence is: explicit action args >
Binary(...)defaults > provider defaults.
For the full list of env vars that hydrate these defaults, see Configuration above.
Supported override keys are the same everywhere:
from pathlib import Path
from abx_pkg import PipProvider
provider = PipProvider(install_root=Path("/tmp/venv")).get_provider_with_overrides(
overrides={
"black": {
"install_args": ["black==24.4.2"],
"version": "self.default_version_handler",
"abspath": "self.default_abspath_handler",
},
},
dry_run=True,
version_timeout=30,
)install_args/packages: package-manager arguments for that provider.packagesis the legacy alias.abspath,version,install,update,uninstall: literal values, callables, or"self.method_name"references that replace the provider handler for a specific binary.
Providers with isolated install locations also expose a shared constructor surface:
install_root: shared alias for provider-specific roots such aspip_venv,npm_prefix,cargo_root,gem_home,gopath,nix_profile,docker_root, andbrew_prefix.bin_dir: shared alias for providers that separate package state from executable output, such asgem_bindir,gobin, anddocker_shim_dir.provider.install_root/provider.bin_dir: normalized computed properties you can inspect after construction, regardless of which provider-specific args were used.- Legacy provider-specific args still work. The shared aliases are additive, not replacements.
- Providers that do not have an isolated install location reject
install_root/bin_dirat construction time instead of silently ignoring them. - When an explicit install root or bin dir is configured, that provider-specific bin location wins during binary discovery and subprocess execution instead of being left behind ambient host
PATHentries.
Source: abx_pkg/binprovider.py β’ Tests: tests/test_envprovider.py
INSTALLER_BIN = "which"
PATH = DEFAULT_ENV_PATH # current PATH + current Python bin dir- Install root: none.
envis read-only and only searches existing binaries on$PATH. - Auto-switching: none.
- Security:
min_release_ageandpostinstall_scriptsare unsupported here and are ignored with a warning if explicitly passed toinstall()/update(). - Overrides:
abspath/versionare the useful ones here.pythonhas a built-in override to the currentsys.executableand interpreter version. - Notes:
install()/update()return explanatory no-op messages, anduninstall()returnsFalse.
Source: abx_pkg/binprovider_apt.py β’ Tests: tests/test_aptprovider.py
INSTALLER_BIN = "apt-get"
PATH = "" # populated from `dpkg -L bash` bin dirs
euid = 0 # always runs as root- Install root: no hermetic prefix support. Installs into the host package database.
- Auto-switching: tries
PyinfraProviderfirst, thenAnsibleProvider, then falls back to directapt-get. dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides: in the direct shell fallback,
install_argsbecomesapt-get install -y -qq --no-install-recommends ...;update()usesapt-get install --only-upgrade .... - Notes: direct mode runs
apt-get update -qqat most once per day and requests privilege escalation when needed.
Source: abx_pkg/binprovider_brew.py β’ Tests: tests/test_brewprovider.py
INSTALLER_BIN = "brew"
PATH = "/home/linuxbrew/.linuxbrew/bin:/opt/homebrew/bin:/usr/local/bin"
brew_prefix = guessed host prefix # /opt/homebrew, /usr/local, or linuxbrew- Install root:
brew_prefixis still the host Homebrew prefix for package discovery and installation. If you passinstall_root=..., abx-pkg uses<install_root>/binas a managed shim/symlink dir for the resolved formula binaries, but Homebrew itself still installs into the host prefix. - Auto-switching: if
postinstall_scripts=True, it prefersPyinfraProviderand thenAnsibleProvider; otherwise it falls back to directbrew. dry_run: shared behavior.- Security:
min_release_ageis unsupported and is ignored with a warning if explicitly requested.postinstall_scripts=Falseis supported for directbrewinstalls via--skip-post-install, andABX_PKG_POSTINSTALL_SCRIPTShydrates the provider default here. - Overrides: in the direct shell fallback,
install_argsmaps to formula / cask args passed tobrew install,brew upgrade, andbrew uninstall. - Notes: direct mode runs
brew updateat most once per day. Explicit--skip-post-installargs ininstall_argswin over derived defaults.
Source: abx_pkg/binprovider_pip.py β’ Tests: tests/test_pipprovider.py, tests/test_security_controls.py
INSTALLER_BIN = "pip"
PATH = "" # auto-built from global/user Python bin dirs
pip_venv = None # set this for hermetic installs
cache_dir = user_cache_path("pip", "abx-pkg") or <system temp>/pip-cache
pip_install_args = ["--no-input", "--disable-pip-version-check", "--quiet"]
pip_bootstrap_packages = ["pip", "setuptools"]- Install root:
install_root=Noneuses the system/user Python environment. Setinstall_root=Path(...)orinstall_root=Path(...)for a hermetic venv rooted at<pip_venv>/bin, and that venv bin dir becomes the provider's active executable search path. Whenpip_venvis set,setup()creates the venv on first use via Python's built-invenvmodule and bootstraps the latestpip_bootstrap_packagesinto it. - Auto-switching: none. Shells out to
pipdirectly. HonorsPIP_BINARY=/abs/path/to/pip. UseUvProviderfor uv-backed installs. dry_run: shared behavior.- Security: supports
postinstall_scripts=False(always) andmin_release_age(on pip >= 26.0 or in a freshly bootstrappedpip_venv). Hydrated fromABX_PKG_POSTINSTALL_SCRIPTSandABX_PKG_MIN_RELEASE_AGE. For stricter enforcement on hosts with older system pip, useUvProviderinstead. - Overrides:
install_argsis passed as pip requirement specs; unpinned specs get a>=min_versionfloor whenmin_versionis supplied. - Notes:
postinstall_scripts=Falseaddspip --only-binary :all:(wheels only, no arbitrary sdist build scripts).min_release_ageis enforced withpip --uploaded-prior-to=<ISO8601>on pip >= 26.0 (see pypa/pip#13625); older pip silently skips the flag. Explicit conflicting flags already present ininstall_argswin over the derived defaults.get_version/get_abspathfall back to parsingpip show <package>output when the console script can't report its own version.
Source: abx_pkg/binprovider_uv.py β’ Tests: tests/test_uvprovider.py
INSTALLER_BIN = "uv"
PATH = "" # prepends <uv_venv>/bin or uv_tool_bin_dir
uv_venv = None # None = global uv tool mode, Path(...) = hermetic venv
uv_tool_dir = None # mirrors $UV_TOOL_DIR (global mode only)
uv_tool_bin_dir = None # mirrors $UV_TOOL_BIN_DIR (global mode only)
cache_dir = user_cache_path("uv", "abx-pkg") or <system temp>/uv-cache
uv_install_args = []- Install root: two modes, picked by whether
uv_venvis set.- Hermetic venv mode (
install_root=Path(...)orinstall_root=Path(...)): creates a real venv at the requested path viauv venvand installs packages into it withuv pip install --python <venv>/bin/python .... Binaries land in<uv_venv>/bin/<name>. This is the idiomatic "install a Python library + its CLI entrypoints into an isolated environment" path and matchesPipProvider'spip_venvsemantics. - Global tool mode (
install_root=None): delegates touv tool installwhich creates a fresh venv per tool underUV_TOOL_DIR(default~/.local/share/uv/tools) and writes shims intoUV_TOOL_BIN_DIR(default~/.local/bin). Passuv_tool_dir=Path(...)/bin_dir=Path(...)to override those dirs hermetically. This is the idiomatic "install a CLI tool globally" path.
- Hermetic venv mode (
- Auto-switching: none. Honors
UV_BINARY=/abs/path/to/uv. Ifuvisn't on the host, the provider is unavailable. dry_run: shared behavior.- Security: supports both
min_release_ageandpostinstall_scripts=False, and hydrates their provider defaults fromABX_PKG_MIN_RELEASE_AGEandABX_PKG_POSTINSTALL_SCRIPTS. In both modes,postinstall_scripts=Falsebecomes--no-build(wheels-only, no arbitrary sdist build scripts) andmin_release_agebecomes--exclude-newer=<ISO8601>(uv 0.4+). Explicit conflicting flags already present ininstall_argswin over the derived defaults. - Overrides:
install_argsis passed as requirement specs; unpinned specs get a>=min_versionfloor whenmin_versionis supplied. - Notes: update in venv mode is
uv pip install --upgrade; update in global mode isuv tool install --force(re-installs the tool's venv). Uninstall in venv mode usesuv pip uninstall --python <venv>/bin/python; in global mode it usesuv tool uninstall <name>.
Source: abx_pkg/binprovider_npm.py β’ Tests: tests/test_npmprovider.py, tests/test_security_controls.py
INSTALLER_BIN = "npm"
PATH = "" # auto-built from npm local + global bin dirs
npm_prefix = None # None = global install, Path(...) = hermetic-ish prefix
cache_dir = user_cache_path("npm", "abx-pkg") or <system temp>/npm-cache
npm_install_args = ["--force", "--no-audit", "--no-fund", "--loglevel=error"]- Install root:
install_root=Noneinstalls globally (walks up from the host'snpm prefix/npm prefix -gto seedPATH). Setinstall_root=Path(...)orinstall_root=Path(...)to install under<prefix>/node_modules/.bin; that prefix bin dir becomes the provider's active executable search path. - Auto-switching: none. Shells out to
npmdirectly and expectsnpmto be installed on the host. HonorsNPM_BINARY=/abs/path/to/npm. UsePnpmProviderfor pnpm. dry_run: shared behavior.- Security: supports both
postinstall_scripts=Falseandmin_release_age, hydrated fromABX_PKG_POSTINSTALL_SCRIPTSandABX_PKG_MIN_RELEASE_AGE.min_release_agerequires an npm build that ships--min-release-age(detected once by probingnpm install --help). - Overrides:
install_argsis passed as npm package specs; unpinned specs get rewritten topkg@>=<min_version>whenmin_versionis supplied. - Notes:
postinstall_scripts=Falseadds--ignore-scripts;min_release_ageadds--min-release-age=<days>. Explicit conflicting flags already present ininstall_argswin over the derived defaults.get_version/get_abspathfall back to parsingnpm show --json <package>andnpm list --json --depth=0output when the console script can't report its own version.
Source: abx_pkg/binprovider_pnpm.py β’ Tests: tests/test_pnpmprovider.py
INSTALLER_BIN = "pnpm"
PATH = "" # auto-built from pnpm local + global bin dirs
pnpm_prefix = None # None = global install, Path(...) = hermetic-ish prefix
cache_dir = user_cache_path("pnpm", "abx-pkg") or <system temp>/pnpm-cache
pnpm_install_args = ["--loglevel=error"]- Install root:
install_root=Noneinstalls globally. Setinstall_root=Path(...)orinstall_root=Path(...)to install under<prefix>/node_modules/.bin; that prefix bin dir becomes the provider's active executable search path. - Shells out to
pnpmdirectly. HonorsPNPM_BINARY=/abs/path/to/pnpm. UseNpmProviderfornpm. dry_run: shared behavior.- Security: supports both
min_release_ageandpostinstall_scripts=False, and hydrates their provider defaults fromABX_PKG_MIN_RELEASE_AGEandABX_PKG_POSTINSTALL_SCRIPTS.min_release_agerequires pnpm 10.16+, andsupports_min_release_age()returnsFalseon older hosts (then it logs a warning and continues). - Overrides:
install_argsis passed as pnpm package specs; unpinned specs get rewritten topkg@>=<min_version>whenmin_versionis supplied. - Notes: pnpm has no
--min-release-ageCLI flag; this provider passes--config.minimumReleaseAge=<minutes>(the camelCase / kebab-case form pnpm exposes via its--config.<key>=<value>override).PNPM_HOMEis auto-populated sopnpm add -gworks without polluting the user's shell config.
Source: abx_pkg/binprovider_yarn.py β’ Tests: tests/test_yarnprovider.py
INSTALLER_BIN = "yarn"
PATH = "" # prepends <yarn_prefix>/node_modules/.bin
yarn_prefix = None # workspace dir, defaults to ABX_PKG_YARN_ROOT or ~/.cache/abx-pkg/yarn
cache_dir = user_cache_path("yarn", "abx-pkg") or <system temp>/yarn-cache
yarn_install_args = []- Install root: Yarn 4 / Yarn Berry is workspace-based, so the provider always operates inside a project directory. Set
install_root=Path(...)orinstall_root=Path(...)for a hermetic workspace; the workspace is auto-initialized with a stubpackage.jsonand.yarnrc.yml(nodeLinker: node-modulesso binaries land in<workspace>/node_modules/.bin). When unset, the provider uses$ABX_PKG_YARN_ROOTor~/.cache/abx-pkg/yarn. - Auto-switching: none. Honors
YARN_BINARY=/abs/path/to/yarn. Both Yarn classic (1.x) and Yarn Berry (2+) work for basic install/update/uninstall, but only Yarn 4.10+ supports the security flags. dry_run: shared behavior.- Security: supports both
min_release_ageandpostinstall_scripts=False, and hydrates their provider defaults fromABX_PKG_MIN_RELEASE_AGEandABX_PKG_POSTINSTALL_SCRIPTS. Both controls require Yarn 4.10+; on older hostssupports_min_release_age()/supports_postinstall_disable()returnFalseand explicit values are logged-and-ignored. - Overrides:
install_argsis passed as Yarn package specs; unpinned specs get rewritten topkg@>=<min_version>whenmin_versionis supplied. - Notes: Yarn has no
--ignore-scripts/--minimum-release-ageCLI flags; the provider writesnpmMinimalAgeGate: 7d(or whatever days value is configured) andenableScripts: falseinto<yarn_prefix>/.yarnrc.ymland additionally passes--mode skip-buildtoyarn add/yarn upwhenpostinstall_scripts=False. Updates useyarn up <pkg>(Berry) oryarn upgrade <pkg>(classic).YARN_GLOBAL_FOLDERandYARN_CACHE_FOLDERare pointed atcache_dirso installs share a single cache across workspaces.
Source: abx_pkg/binprovider_bun.py β’ Tests: tests/test_bunprovider.py
INSTALLER_BIN = "bun"
PATH = "" # prepends <bun_prefix>/bin
bun_prefix = None # mirrors $BUN_INSTALL, None = ~/.bun (host-default)
cache_dir = user_cache_path("bun", "abx-pkg") or <system temp>/bun-cache
bun_install_args = []- Install root:
bun_prefix=Nonewrites into the host$BUN_INSTALL(default~/.bun). Setbun_prefix=Path(...)orinstall_root=Path(...)to install under<prefix>/bin; the provider also creates<prefix>/install/globalfor the globalnode_modulesdir, which is where bun puts the actual package state. The prefix bin dir becomes the provider's active executable search path. - Auto-switching: none. Honors
BUN_BINARY=/abs/path/to/bun. dry_run: shared behavior.- Security: supports both
min_release_ageandpostinstall_scripts=False, and hydrates their provider defaults fromABX_PKG_MIN_RELEASE_AGEandABX_PKG_POSTINSTALL_SCRIPTS.min_release_agerequires Bun 1.3+, andsupports_min_release_age()returnsFalseon older hosts. - Overrides:
install_argsis passed as Bun package specs; unpinned specs get rewritten topkg@>=<min_version>whenmin_versionis supplied. - Notes: install/update use
bun add -g(with--forceas the update fallback). The provider passes--ignore-scriptsforpostinstall_scripts=Falseand--minimum-release-age=<seconds>(Bun's unit is seconds; this provider converts from days). Explicit conflicting flags already present ininstall_argswin over the derived defaults.
Source: abx_pkg/binprovider_deno.py β’ Tests: tests/test_denoprovider.py
INSTALLER_BIN = "deno"
PATH = "" # prepends <deno_root>/bin
deno_root = None # mirrors $DENO_INSTALL_ROOT, None = ~/.deno
deno_dir = None # mirrors $DENO_DIR for cache isolation
cache_dir = user_cache_path("deno", "abx-pkg") or <system temp>/deno-cache
deno_install_args = ["--allow-all"]
deno_default_scheme = "npm" # 'npm' or 'jsr'- Install root:
deno_root=Nonewrites into the host$DENO_INSTALL_ROOT(default~/.deno). Setdeno_root=Path(...)orinstall_root=Path(...)for a hermetic root with executables under<deno_root>/bin. Setdeno_dir=Path(...)to also isolate the module cache. - Auto-switching: none. Honors
DENO_BINARY=/abs/path/to/deno. dry_run: shared behavior.- Security: supports both
min_release_ageandpostinstall_scripts=False/True, and hydrates their provider defaults fromABX_PKG_MIN_RELEASE_AGEandABX_PKG_POSTINSTALL_SCRIPTS.min_release_agerequires Deno 2.5+, andsupports_min_release_age()returnsFalseon older hosts. - Overrides:
install_argsis passed asdeno installpackage specs and is auto-prefixed withnpm:(orjsr:ifdeno_default_scheme="jsr") when an unqualified bare name is supplied. Already-qualified specs (npm:,jsr:,https://...) are passed through verbatim. Unpinned specs get rewritten topkg@>=<min_version>whenmin_versionis supplied. - Notes: install / update both run
deno install -g --force --allow-all -n <bin_name> <pkg>because Deno's idiomatic update path is just a fresh global install. Deno's npm lifecycle scripts are opt-in (the opposite of npm), so the provider only adds--allow-scriptswhenpostinstall_scripts=True.min_release_ageis passed as--minimum-dependency-age=<minutes>(Deno's preferred unit; this provider converts from days).DENO_TLS_CA_STORE=systemis set so installs work on hosts with corporate / sandboxed CA bundles.
Source: abx_pkg/binprovider_bash.py β’ Tests: tests/test_bashprovider.py
INSTALLER_BIN = "sh"
PATH = ""
bash_root = $ABX_PKG_BASH_ROOT or ~/.cache/abx-pkg/bash
bash_bin_dir = <bash_root>/bin- Install root: set
bash_root/install_rootfor the managed state dir, andbash_bin_dir/bin_dirfor the executable output dir. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides: this provider is driven by literal per-binary shell overrides for
install,update, anduninstall. - Notes: the provider exports
INSTALL_ROOT,BIN_DIR,BASH_INSTALL_ROOT, andBASH_BIN_DIRinto the shell environment for those commands.
Source: abx_pkg/binprovider_cargo.py β’ Tests: tests/test_cargoprovider.py
INSTALLER_BIN = "cargo"
PATH = "" # prepends cargo_root/bin and cargo_home/bin
cargo_root = None # set this for hermetic installs
cargo_home = $CARGO_HOME or ~/.cargo
cargo_install_args = ["--locked"]- Install root: set
install_root=Path(...)orinstall_root=Path(...)for isolated installs under<cargo_root>/bin; otherwise installs go throughcargo_home. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsis passed tocargo install;min_versionbecomescargo install --version >=.... - Notes: the provider also sets
CARGO_HOME,CARGO_TARGET_DIR, andCARGO_INSTALL_ROOTwhen applicable.
Source: abx_pkg/binprovider_gem.py β’ Tests: tests/test_gemprovider.py
INSTALLER_BIN = "gem"
PATH = DEFAULT_ENV_PATH
gem_home = None # defaults to $GEM_HOME or ~/.local/share/gem
gem_bindir = None # defaults to <gem_home>/bin
gem_install_args = ["--no-document"]- Install root: set
gem_homeorinstall_root, and optionallygem_bindirorbin_dir, for hermetic installs; otherwise it uses$GEM_HOMEor~/.local/share/gem. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsmaps togem install ...,gem update ..., andgem uninstall ...;min_versionbecomes--version >=.... - Notes: generated wrapper scripts are patched so they activate the configured
GEM_HOMEinstead of the host default.
Source: abx_pkg/binprovider_goget.py β’ Tests: tests/test_gogetprovider.py
INSTALLER_BIN = "go"
PATH = DEFAULT_ENV_PATH
gobin = None # defaults to <gopath>/bin
gopath = $GOPATH or ~/go
go_install_args = []- Install root: set
gopathorinstall_rootfor the Go workspace, andgobinorbin_dirfor the executable dir; otherwise installs land in<gopath>/bin. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsis passed togo install ...; the default is["<bin_name>@latest"]. - Notes:
update()is justinstall()again. Version detection prefersgo version -m <binary>and falls back to the generic version probe. The provider name isgoget, notgo_get.
Source: abx_pkg/binprovider_nix.py β’ Tests: tests/test_nixprovider.py
INSTALLER_BIN = "nix"
PATH = "" # prepends <nix_profile>/bin
nix_profile = $ABX_PKG_NIX_PROFILE or ~/.nix-profile
nix_state_dir = None # optional XDG state/cache isolation
nix_install_args = [
"--extra-experimental-features", "nix-command",
"--extra-experimental-features", "flakes",
]- Install root: set
nix_profile=Path(...)orinstall_root=Path(...)for a custom profile; addnix_state_dir=Path(...)to isolate state/cache paths too. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsis passed tonix profile install ...; default is["nixpkgs#<bin_name>"]. - Notes: update/uninstall operate on the resolved profile element name rather than reusing the full flake ref.
Source: abx_pkg/binprovider_docker.py β’ Tests: tests/test_dockerprovider.py
INSTALLER_BIN = "docker"
PATH = "" # prepends docker_shim_dir
docker_shim_dir = ($ABX_PKG_DOCKER_ROOT or ~/.cache/abx-pkg/docker) / "bin"
docker_run_args = ["--rm", "-i"]- Install root: partial only. Images are pulled into Docker's host-managed image store; the provider only controls the local shim dir and metadata dir. Use
install_root=Path(...)for the shim/metadata root orbin_dir=Path(...)for the shim dir directly. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsis a list of Docker image refs. The first item is treated as the main image and becomes the generated shim target. - Notes: default install args are
["<bin_name>:latest"].install()/update()rundocker pull, write metadata JSON, and create an executable wrapper that runsdocker run .... Expects image refs as install args, typically via overrides on aBinary. It writes a local wrapper script for the binary and executes it viadocker run ...; the binary version is parsed from the image tag, so semver-like tags work best.
Source: abx_pkg/binprovider_chromewebstore.py β’ Tests: tests/test_chromewebstoreprovider.py
INSTALLER_BIN = "node"
PATH = ""
extensions_root = $ABX_PKG_CHROMEWEBSTORE_ROOT or ~/.cache/abx-pkg/chromewebstore
extensions_dir = <extensions_root>/extensions- Install root: set
extensions_root/install_rootfor the managed extension cache root, andextensions_dir/bin_dirfor the unpacked extension output dir. - Auto-switching: none.
dry_run: shared behavior.- Security:
min_release_ageis unsupported and is ignored with a warning if explicitly requested.postinstall_scripts=Falseis supported as a standard kwarg andABX_PKG_POSTINSTALL_SCRIPTShydrates the provider default here, but there is no extra install-time toggle beyond the packaged JS runtime path this provider already uses. - Overrides:
install_argsare[webstore_id, "--name=<extension_name>"]. - Notes: the packaged JS runtime under
abx_pkg/js/chrome/is used to download, unpack, and cache the extension, and the resolved binary path is the unpackedmanifest.json.no_cache=Truebypasses that metadata cache on the next install/update without deleting the unpacked extension tree.
Source: abx_pkg/binprovider_puppeteer.py β’ Tests: tests/test_puppeteerprovider.py
INSTALLER_BIN = "puppeteer-browsers"
PATH = ""
puppeteer_root = $ABX_PKG_PUPPETEER_ROOT or ~/.cache/abx-pkg/puppeteer
browser_bin_dir = <puppeteer_root>/bin
browser_cache_dir = <puppeteer_root>/cache- Install root: set
puppeteer_root/install_rootfor the managed root,browser_bin_dir/bin_dirfor symlinked executables, andbrowser_cache_dirfor downloaded browser artifacts. - Auto-switching: bootstraps
@puppeteer/browsersthroughNpmProviderand then uses that CLI for browser installs. dry_run: shared behavior.- Security:
min_release_ageis unsupported for browser installs and is ignored with a warning if explicitly requested.postinstall_scripts=Falseis supported for the underlying npm bootstrap path, andABX_PKG_POSTINSTALL_SCRIPTShydrates the provider default here. - Overrides:
install_argsare passed through to@puppeteer/browsers install ..., with the provider appending its managed--path=<cache_dir>. - Notes: installed-browser resolution uses semantic version ordering, not lexicographic string sorting.
Source: abx_pkg/binprovider_playwright.py β’ Tests: tests/test_playwrightprovider.py
INSTALLER_BIN = "playwright"
PATH = ""
playwright_root = None # when set, doubles as PLAYWRIGHT_BROWSERS_PATH
browser_bin_dir = <playwright_root>/bin # symlink dir for resolved browsers
playwright_install_args = ["--with-deps"]
euid = 0 # routes exec() through sudo-first-then-fallback- Install root: set
playwright_root/install_rootto pin both the abx-pkg managed root ANDPLAYWRIGHT_BROWSERS_PATHto the same directory. Leave it unset to let playwright use its own OS-default browsers path (~/.cache/ms-playwrighton Linux etc.) β in that case abx-pkg maintains no managed symlink dir or npm prefix at all, theplaywrightnpm CLI bootstraps against the host's npm default, andload()returns the resolvedexecutablePath()directly.browser_bin_dir/bin_diroverrides the symlink directory whenplaywright_rootis pinned. - Auto-switching: bootstraps the
playwrightnpm package throughNpmProvider, then runsplaywright install --with-deps <install_args>against it. Resolves each installed browser's real executable via theplaywright-coreNode.js API (chromium.executablePath()etc.) and writes a symlink intobin_dirwhen one is configured. dry_run: shared behavior β the install handler short-circuits to a placeholder without touching the host.- Privilege handling:
--with-depsinstalls system packages and requires root on Linux.euiddefaults to0, which routes everyexec()call through the baseBinProvider.execsudo-first-then-fallback path β it triessudo -n -- playwright install --with-deps ...first on non-root hosts, falls back to running the command directly if sudo fails or isn't available, and merges both stderr outputs into the final error if both attempts fail. - Security:
min_release_ageandpostinstall_scripts=Falseare unsupported for browser installs and are ignored with a warning if explicitly requested. - Overrides:
install_argsare appended ontoplaywright installafterplaywright_install_args(defaults to["--with-deps"]) and passed through verbatim β use whatever browser names / flags theplaywright installCLI accepts (chromium,firefox,webkit,--no-shell,--only-shell,--force, etc.). - Notes:
update()bumps the managedplaywrightnpm package first (viaNpmProvider.update) so its pinned browser versions refresh, then re-runsplaywright install --force <install_args>to pull any new browser builds.uninstall()removes the relevant<bin_name>-*/directories fromplaywright_rootalongside the bin-dir symlink, sinceplaywright uninstallonly drops unused browsers on its own. Bothupdate()anduninstall()leave playwright's OS-default cache untouched whenplaywright_rootis unset.
Source: abx_pkg/binprovider_pyinfra.py β’ Tests: tests/test_pyinfraprovider.py
INSTALLER_BIN = "pyinfra"
PATH = os.environ.get("PATH", DEFAULT_PATH)
pyinfra_installer_module = "auto"
pyinfra_installer_kwargs = {}- Install root: no hermetic prefix support. It delegates to host package managers through pyinfra operations.
- Auto-switching:
installer_module="auto"resolves tooperations.brew.packageson macOS andoperations.server.packageson Linux. dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsis the package list passed to the selected pyinfra operation. - Notes: privilege requirements depend on the underlying package manager and selected module. When pyinfra tries a privileged sudo path and then falls back, both error outputs are preserved if the final attempt also fails.
Source: abx_pkg/binprovider_ansible.py β’ Tests: tests/test_ansibleprovider.py
INSTALLER_BIN = "ansible"
PATH = os.environ.get("PATH", DEFAULT_PATH)
ansible_installer_module = "auto"
ansible_playbook_template = ANSIBLE_INSTALL_PLAYBOOK_TEMPLATE- Install root: no hermetic prefix support. It delegates to the host via
ansible-runner. - Auto-switching:
installer_module="auto"resolves tocommunity.general.homebrewon macOS andansible.builtin.packageon Linux. dry_run: shared behavior.- Security:
min_release_ageandpostinstall_scripts=Falseare unsupported and are ignored with a warning if explicitly requested. - Overrides:
install_argsbecomes the playbook loop input for the chosen Ansible module. - Notes: when using the Homebrew module, the provider auto-injects the detected brew search path into module kwargs. Privilege requirements still come from the underlying package manager, and failed sudo attempts are included in the final error if the fallback attempt also fails.
Represents a single binary dependency aka a package (e.g. wget, curl, ffmpeg). Each Binary can declare one or more BinProviders it supports, along with per-provider overrides.
Binarys implement the following interface:
load(),install(),update(),uninstall()->Binarybinprovidersbinprovider/loaded_binproviderabspath/loaded_abspathabspaths/loaded_abspathsversion/loaded_versionsha256/loaded_sha256
Binary.install() and Binary.update() return a fresh loaded Binary. Binary.uninstall() returns a Binary with binprovider, abspath, version, and sha256 cleared after removal. Binary.load(), Binary.install(), and Binary.update() all enforce min_version consistently. All four lifecycle methods also accept no_cache=True to bypass cached/current-state checks.
from abx_pkg import Binary, SemVer, env, brew
curl = Binary(
name="curl",
min_version=SemVer("8.0.0"),
binproviders=[env, brew],
).install()
print(curl.binprovider) # EnvProvider(...) or BrewProvider(...)
print(curl.abspath) # Path('/usr/local/bin/curl')
print(curl.version) # SemVer(8, 4, 0)
print(curl.is_valid) # True
curl = curl.update()
curl = curl.uninstall()For reusable Binary subclasses with per-provider overrides, see Advanced Usage above.
from abx_pkg import SemVer
### Example: Use the SemVer type directly for parsing & verifying version strings
SemVer.parse('Google Chrome 124.0.6367.208+beta_234. 234.234.123') # SemVer(124, 0, 6367)
SemVer.parse('2024.04.05') # SemVer(2024, 4, 5)
SemVer.parse('1.9+beta') # SemVer(1, 9, 0)
str(SemVer(1, 9, 0)) # '1.9.0'These types are all meant to be used library-style to make writing your own apps easier.
e.g. you can use it to build things likeplaywright install --with-deps.
abx-pkg uses uv for local development, dependency sync, linting, and tests.
git clone https://github.com/ArchiveBox/abx-pkg && cd abx-pkg
# setup the venv and install packages
uv sync --all-extras
source .venv/bin/activate
# run formatting/lint/type checks
uv run prek run --all-files
# run the full test suite from tests/
uv run pytest -sx tests/
# build distributions
uv build && uv publish --username=__token__- Tests live under
tests/. - Use
uv run pytest -sx tests/test_npmprovider.pyor a specific node likeuv run pytest -sx tests/test_npmprovider.py::TestNpmProvider::test_provider_dry_run_does_not_install_zxwhen iterating on one provider.
Note: this package used to be called pydantic-pkgr, it was renamed to abx-pkg on 2024-11-12.

