Firstly, mise tool stubs #

I’m enjoying using mise.

I’m especially enjoying using mise’s “tool stubs”.

Here’s how it works:

  1. Pick a directory to plop tool stubs, and tell mise to prepend/append it to the PATH
  2. Take the single-file binary you want to use (along with the version)
  3. mise generate tool-stub <path> --platform-url <url1> --platform-url <url2> ...
  4. Now that you have an executable on PATH with the name of the tool, you can just ./tool-name

mise will handle the downloading, checksumming, and all that jazz for you.

Under the hood the file on disk is just a “lockfile” containing the URLs, checksums, etc… for the tool.

Example uv tool stub:

#!/usr/bin/env -S mise tool-stub

version = "0.11.1"

[platforms.macos-arm64]
url = "https://github.com/astral-sh/uv/releases/download/0.11.1/uv-aarch64-apple-darwin.tar.gz"
checksum = "blake3:3827382f68acaf640bf0749b80dfde35b62c81c7b70255e2221bcefe8a5d12bb"
size = 20474937 # 19.53 MiB

[platforms.linux-arm64]
url = "https://github.com/astral-sh/uv/releases/download/0.11.1/uv-aarch64-unknown-linux-gnu.tar.gz"
checksum = "blake3:10f74d9d7de56a88ef8e5dbda47a27b7a7a915429812ebdb99f82c4afe50f1d9"
size = 22320966 # 21.29 MiB

[platforms.linux-x64]
url = "https://github.com/astral-sh/uv/releases/download/0.11.1/uv-x86_64-unknown-linux-gnu.tar.gz"
checksum = "blake3:840cd02af39074f1bc3187f3a9802f27a1ed0c1a99e768de067a4ab35bca0be8"
size = 23844436 # 22.74 MiB

Centrally, uv tool stubs #

I wished I had the same thing for Python tools.

I tried at first to get it working using just really clever she-bangs. (This led to me filing this really embarassing issue on the uv repo)

Alas, since you need some arguments after the filename, it can’t be done just in a she-bang (someone remind me to turn this blog post into a uv feature request).

So the next best thing is a Bash script:

#!/usr/bin/env bash

# A uv-powered tool stub executor (similar to mise tool stubs)
# Usage:
#
#   1. Make a "stub" file: `uvx-stub generate <requirement> <location>`
#   2. That file is now runnable!

if [[ "$1" == "generate" ]]; then
    requirement="$2"
    location="$3"

    echo "#!/usr/bin/env uvx-stub" > "$location"
    chmod +x "$location"
    echo "$requirement" | uv pip compile --no-annotate --custom-compile-command "$(basename "$0") $*" - >> "$location"
    exit 0
fi

exec uv tool run --isolated --with-requirements "$1" "$(basename "$1")" -- "${@:2}"

Slap that sucker in an executable file called uvx-stub (somewhere on PATH), and now you’re cooking!

How it works: #

  1. The generate command takes a requirement (like foobar >= 2.3) and a location and makes an executable file at the location
    1. (The “executable file” is a just a she-bang and a requirements file - why not pylock.toml? More later)
  2. When you run the file, it uses uvx-stub to “run” it
  3. uvx-stub just uses a long and clever uv tool run command, with “the file” as the requirements

In other words, it’s a uv-powered “tool stub”.

Example file (for black):

#!/usr/bin/env uvx-stub
# This file was autogenerated by uv via the following command:
#    uvx-stub generate black .config/black
black==26.5.1
click==8.4.1
mypy-extensions==1.1.0
packaging==26.2
pathspec==1.1.1
platformdirs==4.10.0
pytokens==0.4.1

Nextly #

Two things:

  1. uv really ought to allow a pylock.toml with --with-requirements (or similar). (#19117)
  2. It’d be nice if this was just a subcommand of uv/uvx (e.g. uvx-stub <location>).