Standalone python script with uv
Uv is a neat package manager, which took off last year in python land. Being written in Rust™ immediately gives it +100 street credibility.
As it supports reading inline script metadata (see PEP 723) it's easy to write scripts that specify their own dependencies, in a single file.
You still need to install uv itself though, and while it's simple it's still an extra step, which is exactly one too many. So here is how I inline that as well into the script:
#!/usr/bin/env sh
#!/usr/bin/env -S uv run --script --quiet
# /// script
# requires-python = "~=3.12"
# dependencies = [
# "numpy==2.1.1",
# ]
# ///
""":"
which uv >/dev/null \
|| curl -LsSf https://astral.sh/uv/install.sh | sh \
&& tail -n +3 $0 | $(head -n 2 $0 | tail -n 1 | cut -c 3-) - "$@"
exit $?
":"""
from fractions import Fraction as F
import sys
import numpy as np
print(f"""Python script running with:
{sys.executable}
{sys.argv[1:]}
{np.__version__=}""")
# Idiomatic python, print numbers 0 to 96
print(
*map(lambda x: x[0]+x[1], zip((z := f"{F(1, 9801):.192f}")[2::2], z[3::2]))
)
The trick is simple, we set the shebang to be our shell #!/usr/bin/env sh
, meaning that the rest of the file will be interpreted as a shell script.
Conveniently, both python and POSIX shells treat lines starting with #
as comments.
The shell interprets """:"
as an empty pair of quotes, then a quoted no-op :
command and symmetrically for ":"""
.
In python, the whole section in between triple quotes is an unassigned string.
The script part commented a bit is simply:
# Check if `uv` is available
if $(which uv >/dev/null); then
# If yes, nothing to do
else
# Otherwise download and install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
fi
# Then remove the first two lines from the current file and pipe that
# to the command from the second shebang (here `uv run --script --quiet`), with any given arguments
tail -n +3 $0 | $(head -n 2 $0 | tail -n 1 | cut -c 3-) - "$@"
# Exit now –with whatever the python script returned– to avoid trying to execute the rest of the file
exit $?
Bonus: to remove the shell part after checking/installing uv, use this instead:
which uv >/dev/null \
|| curl -LsSf https://astral.sh/uv/install.sh | sh \
&& tail -n +2 $0 | awk '/^.?"":"$/,/^":"".?$/ {next} {print}' > /tmp/toswap.txt \
&& mv /tmp/toswap.txt $0 \
&& chmod +x $0 \
&& exec $0 $@
exit $?