| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Use abxpkg to detect & auto-install dependencies at runtime, serialize your dependencies to DB/config, and manage bins across many ecosystems.
This is a Python library and all-in-one CLI for managing packages locally with a variety of package managers.
It's designed for when you have to detect or install binary or source dependencies at runtime.
Stop distributing your apps via curl | sh! Instead you can bake package installation into your app, or use our uv-style abxpkg run --script shebang headers to auto-install dependencies for you.
pip install abxpkg # uv tool install abxpkg
abxpkg --versionfrom pathlib import Path
from tempfile import TemporaryDirectory
from abxpkg import Binary, env, npm, brew
prettier = env.load('prettier') or npm.install('prettier') or brew.install('prettier')
# or equivalent:
prettier = Binary(name='prettier', binproviders=[env, npm, brew]).install()
print(prettier.abspath, prettier.version)
# ~/.cache/abx/lib/npm/bin/prettier 2.2.1
with TemporaryDirectory() as temp_dir:
example = Path(temp_dir) / 'example.js'
example.write_text('const answer=42\n')
prettier.exec(cmd=['--write', str(example)])
# Search a provider's package index for matches:
matches = npm.search('puppeteer') # -> list[Binary] with name + install_args populated
assert isinstance(matches, list)📦 Provides consistent interfaces for runtime dependency resolution & installation across multiple package managers & OSs ✨ Built with pydantic v2 for strong static typing guarantees and easy conversion to/from json 🌈 Usable with django >= 4.0, django-ninja, and OpenAPI + django-jsonform to build UIs & APIs 🦄 Driver layer can be pyinfra / ansible / or built-in abxpkg engine
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/abxpkg/
Documentation: https://github.com/ArchiveBox/abxpkg/blob/main/README.md
from abxpkg import Binary, apt, brew, docker, env, npm, pip, playwright, pnpm, puppeteer, uv
# 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=[env, playwright, puppeteer, apt]),
Binary(name='postgres', binproviders=[env, docker, apt, brew]),
]
assert dependencies[0].binproviders == [env, apt, brew]
assert dependencies[1].binproviders == [env, pip, uv, apt, brew]Tip
🔒 Stay safe from supply-chain attcaks with abxpkg: We default to safe behavior (when providers allow):
You can customize these defaults on Binary or BinProvider, or with ABXPKG_MIN_RELEASE_AGE/ABXPKG_POSTINSTALL_SCRIPTS/ABXPKG_LIB_DIR (see Configuration below).
pip install abxpkg
abxpkg --versionOr install the isolated CLI tool:
uv tool install abxpkg
abxpkg --versionInstalling abxpkg also provides an abxpkg CLI entrypoint:
abxpkg --version
abxpkg version
abxpkg list
abxpkg install yt-dlp
abxpkg load yt-dlp
abxpkg env yt-dlp
abxpkg activate yt-dlp
abxpkg update yt-dlp
abxpkg uninstall yt-dlpSearch package indexes:
abxpkg search chromium # search all providers in parallel
abxpkg --binproviders=apt,npm,brew search node # restrict to specific providersabxpkg --version and abxpkg version stream the package version first, then a host/env summary line, then one section per selected provider showing its current resolved runtime state (INSTALLER_BINARY, PATH, ENV, install_root, bin_dir, and any active cached dependency / installed binaries).
abxpkg version <binary> is a thin alias for abxpkg load <binary>.
abxpkg list prints the full active cache for the selected providers, grouping provider installer binaries first and normal cached binaries after a blank line. You can optionally pass binary names and/or provider names positionally to filter the output:
abxpkg list
abxpkg list yt-dlp chromium
abxpkg list env puppeteer chromiumabxpkg run yt-dlp --help # resolves yt-dlp via the configured providers and execs it
abxpkg --binproviders=pip,brew run pip show black # restrict provider resolution (exercises PipProvider.exec)
abxpkg --binproviders=pip --install run yt-dlp # load first, then install via selected providers if needed
abxpkg --binproviders=pip --update run yt-dlp # ensure the binary is available, then update before exec
abxpkg --binproviders=pip --no-cache --install run yt-dlp # bypass cached/current-state checks during resolution + install
abxpkg env yt-dlp # print dotenv-style KEY=value lines for yt-dlp's runtime env
abxpkg --binproviders=pip env --install black # install if needed, then print the runtime env in .env format
eval "$(abxpkg --binproviders=pip activate black)" # emit bash export lines and apply them to the current shell
abxpkg activate --fish black | source # emit fish set -x lines and source them into fish
eval "$(abxpkg --binproviders=pip activate --zsh black)" # emit zsh export linesabxpkg env resolves binaries the same way as run, then prints the runtime env as dotenv-style KEY=value lines. abxpkg activate emits a short usage comment plus shell-specific activation commands: bash by default, --zsh for zsh export KEY=value, and --fish for fish set -x KEY VALUE.
abxpkg 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. The same install/update flags also apply to env / activate. run exits with the child's exit code, passes its stdout/stderr through unbuffered, and routes any abxpkg install/load logs to stderr only — no headers, no footers, no parsing.
Think npx / uvx / pipx run — but for every package manager abxpkg supports. abx is a thin alias for abxpkg --install run ...: it resolves the binary via the configured providers, installs it if missing, then execs it with the forwarded arguments.
abx --binproviders=env python3 --version # run an existing host binary through the same resolution path
abx --binproviders=env python3 -c 'print("abx works")'Options before the binary name (--lib, --binproviders, --dry-run, --debug, --no-cache, --update) are forwarded to abxpkg; 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.
abxpkg --min-version=1.2.3 --min-release-age=7 install yt-dlp
abxpkg --postinstall-scripts=False --binproviders=apt,uv,pip install black
abxpkg --no-cache install black
abxpkg --install-root=/tmp/yt-dlp-root --bin-dir=/tmp/yt-dlp-bin install yt-dlp
abxpkg --overrides='{"pip":{"install_args":["yt-dlp[default]"]}}' install yt-dlp
abxpkg --install-timeout=600 --version-timeout=20 --euid=1000 install yt-dlp
abxpkg --global install yt-dlp
abx --min-version=2024.1.1 --min-release-age=3 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 patches for shared provider fields (PATH, INSTALLER_BIN, install_root, bin_dir, euid, postinstall_scripts, min_release_age, dry_run, install_timeout, version_timeout) plus per-binary handler replacements (install_args, abspath, version, install, update, uninstall). |
| --global[=BOOL] | bool | Thin alias for --lib=None. Bare --global = True. |
| --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 ABXPKG_DEBUG or False. |
Every value-taking flag also accepts the literal string None / null / "" to reset to the provider's default resolution path. For postinstall_scripts / min_release_age, that means the action-specific effective default for that provider (False / 7 on supporting providers, True / 0 otherwise). The precedence is: explicit per-subcommand flag > group-level flag > environment variable > built-in default.
abxpkg install --binproviders=env,uv,pip,apt,brew prettier
# or
env ABXPKG_BINPROVIDERS=env,uv,pip,apt,brew abxpkg install yt-dlpabxpkg --lib=./vendor/abxpkg --binproviders=env load python3
env ABXPKG_LIB_DIR=./vendor/abxpkg abxpkg --binproviders=env load python3abxpkg install --dry-run some-dangerous-package # outputs commands that would be run without executing them
# or
env ABXPKG_DRY_RUN=1 abxpkg install some-dangerous-packageCLI result lines are written to stdout. Progress logging is written to stderr at INFO by default. Enable DEBUG logging with ABXPKG_DEBUG=1 or --debug.
⚡️ Inspired by uv's inline script metadata, abxpkg lets you declare arbitrary package dependencies at the top of any script.
It will automatically fetch, install, and make the packages available to your script across a wide variety of languages.
#!/usr/bin/env -S abxpkg run --script node
// /// script
// dependencies = [
// {name = "node", binproviders = ["env", "apt", "brew"], min_version = "22.0.0"},
// {name = "playwright", binproviders = ["env", "pnpm", "npm"], install_args = ["playwright@next"]},
// {name = "chromium", binproviders = ["env", "playwright", "puppeteer", "apt"], min_version = "131.0.0"},
// ]
// [tool.abxpkg]
// ABXPKG_POSTINSTALL_SCRIPTS = true
// ///
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
await browser.close();
})();<30ms cold-start overhead once cached.
The metadata parser is comment-syntax-agnostic — it looks for /// script and /// delimiters and strips the first whitespace-delimited token from each line, so #, //, --, ;, and any other single-token comment prefix all work.
All built-in providers are available as lazy singletons — just import them by name:
from abxpkg 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 abxpkg import PipProvider
custom_pip = PipProvider(install_root=Path("/tmp/abxpkg-pip"), min_release_age=3)Use the Binary class to declare a package that can be installed by one of several ordered providers, with an optional version floor:
from abxpkg import Binary, SemVer, env, brew
curl = Binary(
name="curl",
min_version=SemVer("7.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 ABXPKG_NO_CACHE=1.
Provider installer binaries also resolve lazily through the active provider chain. If a provider needs pip, npm, cargo, or another installer tool and it is missing, abxpkg will auto-install that dependency using the currently selected providers and the same ABXPKG_LIB_DIR / --lib / --global settings.
from pydantic import InstanceOf
from abxpkg import BinProvider, Binary, BinProviderName, BinName, HandlerDict, BrewProvider
from abxpkg 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()
assert [provider.name for provider in ytdlp.binproviders] == ['env', 'pip', 'apt', 'custom_brew']
assert ytdlp.overrides['pip']['install_args'] == ['yt-dlp[default,curl-cffi]']from abxpkg import Binary, env
# Use providers directly for host binary discovery
python = env.load('python3')
assert python and python.is_valid
print(env.PATH, env.get_abspaths('python3'), env.get_version('python3'))
# our Binary API provides a nice type-checkable, validated, serializable handle
python = Binary(name='python3', binproviders=[env]).load()
print(python) # Binary(name='python3', abspath=Path(...), version=SemVer(...), sha256='...', mtime=1712890123456789000)
print(python.abspaths) # show all matching binaries found via each provider PATH
print(python.model_dump(mode='json')) # JSON-ready dict
print(python.model_json_schema()) # ... OpenAPI-ready JSON schema showing all available fieldsfrom pydantic import InstanceOf
from abxpkg 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]] = [EnvProvider(), BrewProvider()]
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 platform
import shutil
from pydantic import InstanceOf
from abxpkg import BinProvider, Binary, BinProviderName, BinName, HandlerDict
from abxpkg 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: shutil.which('podman') or shutil.which('docker') or shutil.which('docker-ce'),
},
'apt': {
# vary the installed package name based on CPU architecture
'install_args': {
'x86_64': ['docker.io'],
'armv7l': ['docker.io'],
'aarch64': ['docker.io'],
}.get(platform.machine(), ['docker.io']),
},
}
docker = DockerBinary().install()from pathlib import Path
from abxpkg 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.INSTALLER_BINARY()
assert installer and installer.loaded_abspath
proc = self.exec(bin_name=installer.loaded_abspath, 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()
assert cargo.get_install_args('ripgrep') == ('ripgrep',)
assert cargo.default_install_args_handler('ripgrep') == ['ripgrep']abxpkg uses the standard Python logging module. By default it stays quiet unless your application configures logging explicitly.
import logging
from abxpkg import Binary, env, configure_logging
configure_logging(logging.INFO)
python = Binary(name='python', binproviders=[env]).load()To enable Rich logging:
pip install "abxpkg[rich]"import logging
from abxpkg 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(), abxpkg 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("abxpkg").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-views
- django-pydantic-field
- django-jsonform pip install abxpkg django-admin-data-views django-pydantic-field django-jsonform
Django model fields:
from django.conf import settings
if not settings.configured:
settings.configure(
INSTALLED_APPS=[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
SECRET_KEY='abxpkg-docs',
)
import django
django.setup()
from django.db import models
from django.db import connection
from abxpkg import Binary, EnvProvider, SemVer
from django_pydantic_field import SchemaField
provider_runtime_fields = {name: True for name in EnvProvider.model_computed_fields}
binary_runtime_fields = {name: True for name in Binary.model_computed_fields}
binary_runtime_fields['binproviders'] = True
binary_runtime_fields['loaded_binprovider'] = True
class Dependency(models.Model):
label = models.CharField(max_length=63)
default_binprovider: EnvProvider = SchemaField(exclude=provider_runtime_fields)
binaries: list[Binary] = SchemaField(
default=[],
exclude={'__all__': binary_runtime_fields},
)
min_version: SemVer = SchemaField(default=(0, 0, 1))
class Meta:
app_label = 'abxpkg_docs'
with connection.schema_editor() as schema_editor:
schema_editor.create_model(Dependency)Saving a Binary using the model:
from abxpkg import Binary, env
python = Binary(name='python3', binproviders=[env]).load()
obj = Dependency(
label='runtime tools',
default_binprovider=env, # store BinProvider values directly
binaries=[python], # 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 == python.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',
'abxpkg',
]
ABXPKG_GET_ALL_BINARIES = 'project.views.get_all_binaries'
ABXPKG_GET_BINARY = 'project.views.get_binary'
ADMIN_DATA_VIEWS = {
"NAME": "Environment",
"URLS": [
{
"route": "binaries/",
"view": "abxpkg.views.binaries_list_view",
"name": "binaries",
"items": {
"route": "<str:key>/",
"view": "abxpkg.views.binary_detail_view",
"name": "binary",
},
},
],
}If you override the default site admin, register the views manually:
from django.conf import settings
if not settings.configured:
settings.configure(INSTALLED_APPS=[])
import django
django.setup()
from django.contrib.admin import AdminSite
from abxpkg.admin import register_admin_views
custom_admin = AdminSite(name='custom')
register_admin_views(custom_admin)All abxpkg 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 |
|---|---|---|
| ABXPKG_DRY_RUN / DRY_RUN | 0 | Flips the shared dry_run default. ABXPKG_DRY_RUN wins if both are set. Provider subprocesses are logged and skipped, install() / update() return a placeholder, uninstall() returns True. |
| ABXPKG_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. |
| ABXPKG_DEBUG | 0 | Enables DEBUG-level CLI logging on stderr for abxpkg / abx. The matching CLI flag is --debug. Default CLI logging level is INFO. |
| ABXPKG_INSTALL_TIMEOUT | 120 | Seconds to wait for install() / update() / uninstall() handler subprocesses. |
| ABXPKG_VERSION_TIMEOUT | 10 | Seconds to wait for version / metadata probes (--version, npm show, pip show, etc.). |
| ABXPKG_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). When left unset, action execution resolves to the provider/action default (False on supporting providers, True otherwise). |
| ABXPKG_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). When left unset, action execution resolves to the provider/action default (7 on supporting providers, 0 otherwise). |
| ABXPKG_BINPROVIDERS | shared default order | Comma-separated list of provider names to enable (and their order) for the abxpkg CLI. By default this uses DEFAULT_PROVIDER_NAMES from abxpkg.__init__ (which excludes ansible / pyinfra, and also excludes apt on macOS). |
Install-root controls (one global default + one per-provider override):
| Variable | Applies to | Effect |
|---|---|---|
| ABXPKG_LIB_DIR | providers whose default install_root is abxpkg-managed | Centralized library root. When set, each matching provider points its default install_root at $ABXPKG_LIB_DIR/<provider name> (e.g. <lib>/env, <lib>/npm, <lib>/pip, <lib>/gem, <lib>/playwright). Accepts relative (./lib), tilde (~/.config/abx/lib), and absolute (/tmp/abxlib) paths. --global is a thin alias for --lib=None, which clears this root for the current CLI invocation. |
| ABXPKG_<BINPROVIDER>_ROOT | the matching provider's install_root | Generic per-provider override; beats ABXPKG_LIB_DIR/<provider name>. Examples: ABXPKG_PIP_ROOT, ABXPKG_UV_ROOT, ABXPKG_NPM_ROOT, ABXPKG_GOGET_ROOT, ABXPKG_CHROMEWEBSTORE_ROOT. The <BINPROVIDER> token is the provider name uppercased. |
Install-root precedence (most specific wins): explicit install_root= / provider alias kwarg > ABXPKG_<NAME>_ROOT > ABXPKG_LIB_DIR/<name> > provider-specific built-in default / 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):
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:
Shared base defaults come from abxpkg/binprovider.py and apply unless a concrete provider overrides them:
import sys
from pathlib import Path
INSTALLER_BIN = "env" # base-class placeholder; real providers override this
PATH = str(Path(sys.executable).parent)
postinstall_scripts = None # some providers override this with ABXPKG_POSTINSTALL_SCRIPTS
min_release_age = None # some providers override this with ABXPKG_MIN_RELEASE_AGE
install_timeout = 120 # or ABXPKG_INSTALL_TIMEOUT=120
version_timeout = 10 # or ABXPKG_VERSION_TIMEOUT=10
dry_run = False # or ABXPKG_DRY_RUN=1 / DRY_RUN=1For 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 abxpkg 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",
"docs_url": "self.default_docs_url_handler",
"search": "self.default_search_handler",
},
},
dry_run=True,
version_timeout=30,
)Providers with isolated install locations also expose a shared constructor surface:
Source: abxpkg/binprovider.py • Tests: tests/test_envprovider.py
from abxpkg.binprovider import DEFAULT_ENV_PATH
INSTALLER_BIN = "which"
PATH = DEFAULT_ENV_PATH # current PATH + current Python bin dirSource: abxpkg/binprovider_apt.py • Tests: tests/test_aptprovider.py
INSTALLER_BIN = "apt-get"
PATH = "" # populated from `dpkg -L bash` bin/sbin roots
euid = 0 # always runs as rootSource: abxpkg/binprovider_brew.py • Tests: tests/test_brewprovider.py
INSTALLER_BIN = "brew"
PATH = "/home/linuxbrew/.linuxbrew/bin:/opt/homebrew/bin:/usr/local/bin"
brew_prefix = "/opt/homebrew" # guessed host prefix: /opt/homebrew, /usr/local, or linuxbrewSource: abxpkg/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
install_root = None # None = ambient/global mode, Path(...) = provider rootSource: abxpkg/binprovider_uv.py • Tests: tests/test_uvprovider.py
INSTALLER_BIN = "uv"
PATH = "" # prepends <install_root>/venv/bin or the uv tool bin dir
install_root = None # None = global uv tool mode, Path(...) = provider rootSource: abxpkg/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
install_root = None # None = global install, Path(...) = prefix/project rootSource: abxpkg/binprovider_pnpm.py • Tests: tests/test_pnpmprovider.py
INSTALLER_BIN = "pnpm"
PATH = "" # auto-built from pnpm local + global bin dirs
install_root = None # None = global install, Path(...) = prefix/project rootSource: abxpkg/binprovider_yarn.py • Tests: tests/test_yarnprovider.py
INSTALLER_BIN = "yarn"
PATH = "" # prepends <install_root>/node_modules/.bin
install_root = None # project dir, defaults to ABXPKG_YARN_ROOT or ABXPKG_LIB_DIR/yarnSource: abxpkg/binprovider_bun.py • Tests: tests/test_bunprovider.py
INSTALLER_BIN = "bun"
PATH = "" # prepends <install_root>/bin
install_root = None # mirrors $BUN_INSTALL, None = ~/.bun (host-default)Source: abxpkg/binprovider_deno.py • Tests: tests/test_denoprovider.py
INSTALLER_BIN = "deno"
PATH = "" # prepends <install_root>/bin
install_root = None # mirrors $DENO_INSTALL_ROOT, None = ~/.denoSource: abxpkg/binprovider_bash.py • Tests: tests/test_bashprovider.py
import os
from pathlib import Path
INSTALLER_BIN = "bash"
PATH = ""
lib_dir = Path(os.environ.get("ABXPKG_LIB_DIR", "~/.config/abx/lib")).expanduser()
install_root = Path(os.environ.get("ABXPKG_BASH_ROOT", lib_dir / "bash"))
bin_dir = install_root / "bin"Source: abxpkg/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 installsSource: abxpkg/binprovider_gem.py • Tests: tests/test_gemprovider.py
from abxpkg.binprovider import DEFAULT_ENV_PATH
INSTALLER_BIN = "gem"
PATH = DEFAULT_ENV_PATH
install_root = None # defaults to $GEM_HOME or ~/.local/share/gem
bin_dir = None # defaults to <install_root>/binSource: abxpkg/binprovider_goget.py • Tests: tests/test_gogetprovider.py
from abxpkg.binprovider import DEFAULT_ENV_PATH
INSTALLER_BIN = "go"
PATH = DEFAULT_ENV_PATH
install_root = None # defaults to $GOPATH or ~/go
bin_dir = None # defaults to <install_root>/binSource: abxpkg/binprovider_nix.py • Tests: tests/test_nixprovider.py
import os
from pathlib import Path
INSTALLER_BIN = "nix"
PATH = "" # prepends <install_root>/bin
install_root = Path(os.environ.get("ABXPKG_NIX_PROFILE", "~/.nix-profile")).expanduser()Source: abxpkg/binprovider_docker.py • Tests: tests/test_dockerprovider.py
import os
from pathlib import Path
INSTALLER_BIN = "docker"
PATH = "" # prepends bin_dir
lib_dir = Path(os.environ.get("ABXPKG_LIB_DIR", "~/.config/abx/lib")).expanduser()
docker_root = Path(os.environ.get("ABXPKG_DOCKER_ROOT", lib_dir / "docker"))
bin_dir = docker_root / "bin"Source: abxpkg/binprovider_chromewebstore.py • Tests: tests/test_chromewebstoreprovider.py
import os
from pathlib import Path
INSTALLER_BIN = "node"
PATH = ""
lib_dir = Path(os.environ.get("ABXPKG_LIB_DIR", "~/.config/abx/lib")).expanduser()
install_root = Path(os.environ.get("ABXPKG_CHROMEWEBSTORE_ROOT", lib_dir / "chromewebstore"))
bin_dir = install_root / "extensions"Source: abxpkg/binprovider_puppeteer.py • Tests: tests/test_puppeteerprovider.py
import os
from pathlib import Path
INSTALLER_BIN = "puppeteer-browsers"
PATH = ""
lib_dir = Path(os.environ.get("ABXPKG_LIB_DIR", "~/.config/abx/lib")).expanduser()
install_root = Path(os.environ.get("ABXPKG_PUPPETEER_ROOT", lib_dir / "puppeteer"))
bin_dir = install_root / "bin"Source: abxpkg/binprovider_playwright.py • Tests: tests/test_playwrightprovider.py
from pathlib import Path
INSTALLER_BIN = "playwright"
PATH = ""
install_root = None # abxpkg-managed root dir for bin_dir / nested npm prefix
bin_dir = Path("/tmp/abxpkg-playwright/bin") # symlink dir when install_root is configured
euid = 0 # routes exec() through sudo-first-then-fallbackSource: abxpkg/binprovider_pyinfra.py • Tests: tests/test_pyinfraprovider.py
import os
from abxpkg.binprovider import DEFAULT_PATH
INSTALLER_BIN = "pyinfra"
PATH = os.environ.get("PATH", DEFAULT_PATH)
pyinfra_installer_module = "auto"
pyinfra_installer_kwargs = {}Source: abxpkg/binprovider_ansible.py • Tests: tests/test_ansibleprovider.py
import os
from abxpkg.binprovider import DEFAULT_PATH
from abxpkg.binprovider_ansible import ANSIBLE_INSTALL_PLAYBOOK_TEMPLATE
INSTALLER_BIN = "ansible"
PATH = os.environ.get("PATH", DEFAULT_PATH)
ansible_installer_module = "auto"
ansible_playbook_template = ANSIBLE_INSTALL_PLAYBOOK_TEMPLATERepresents 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:
Binary.install() and Binary.update() return a fresh loaded Binary. Binary.uninstall() returns a Binary with binprovider, abspath, version, sha256, mtime, and euid 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 abxpkg import Binary, SemVer, env, brew
curl = Binary(
name="curl",
min_version=SemVer("7.0.0"),
binproviders=[env, brew],
).load()
print(curl.binprovider) # EnvProvider(...) or BrewProvider(...)
print(curl.abspath) # Path('/usr/local/bin/curl')
print(curl.version) # SemVer(7, 88, 1) or newer
print(curl.is_valid) # True
assert curl.is_validFor reusable Binary subclasses with per-provider overrides, see Advanced Usage above.
from abxpkg 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 like playwright install --with-deps.
abxpkg uses uv for local development, dependency sync, linting, and tests.
checkout_dir="$(mktemp -d)"
trap 'rm -rf "$checkout_dir"' EXIT
git clone --depth=1 https://github.com/ArchiveBox/abxpkg "$checkout_dir"
cd "$checkout_dir"
# setup the venv and install packages
uv sync --all-extras
# run formatting/lint/type checks
uv run prek run --all-files# Exercise representative core, environment, and provider behavior.
uv run pytest -s \
tests/test_semver.py \
tests/test_binary.py \
tests/test_envprovider.py \
tests/test_module_api.pyThe mandatory per-file CI matrix runs the complete standard suite, the host-mutating provider files, and every root_required and docker_required file on equipped isolated runners.
# build distributions and validate the publish command without uploading
uv build
uv publish --dry-run dist/*Note: this package used to be called pydantic-pkgr, it was renamed to abxpkg on 2024-11-12.
| Back | FazBrowse Home | New Git URL |