A tool for managing uv scripts more easily.
Unlike uv, uvrs adds a shebang line, sets an executable bit, runs scripts with exact dependency syncing, and timestamp-pins requirements by default.
While uv has excellent support for inline script metadata (PEP 723), there are several rough edges when managing scripts:
-
Non-portable shebang: The recommended shebang uses
#!/usr/bin/env -S uv run --script, which relies on the non-standard-Sflag that doesn't work on some systems (uv issue #11876) and uv does not currently have a single command shortcut to run a script (uv issue #16241). -
No executable bit:
uv init --scriptcreates files that aren't executable by default, requiring a manualchmod +x. -
Inconsistent syncing:
uv run --scriptis deliberately inexact by default (uv issue #16334). It may implicitly sync some changes, but doesn't guarantee that the installed dependencies match the virtual environment consistently. -
No reproducibility by default: Scripts don't include
exclude-newertimestamps, so running the same script weeks later may use different package versions, breaking reproducibility. There is also no easy command for adding or updating theexclude-newertimestamp in a script (uv issue #13123)
uvrs addresses all of these issues:
- Portable shebang that works everywhere:
#!/usr/bin/env uvrs - Executable by default for new and fixed scripts
- Consistent syncing via
uv run --exact --scriptwhich ensures the environment always matches metadata - Reproducible by default with automatic
exclude-newertimestamps
This should be installed as a globally available tool (so the above shebang line works):
uv tool install -p 3.14 uvrsThat will install uvrs using Python 3.14 (for nicely colorized help text).
Here's what uvrs does under the hood, compared to the equivalent uv workflow:
This is equivalent to:
uv init --script <path>
# Add #!/usr/bin/env uvrs shebang
# Add exclude-newer timestamp to metadata
chmod +x <path>This is equivalent to:
# Update shebang to #!/usr/bin/env uvrs
# Add exclude-newer timestamp if missing
uv sync --script <path> --upgrade
chmod +x <path>This is equivalent to:
uv add --script <path> <package>This is equivalent to:
uv remove --script <path> <package>This is equivalent to:
# Update exclude-newer timestamp to current time
uv sync --script <path> --upgradeThis is equivalent to:
# Find the Python executable for the script's environment
python_path=$(uv python find --script <path>)
# Run the Python executable with the provided arguments
$python_path [args...]This allows you to run Python commands in the context of the script's virtual environment, such as:
uvrs python <path>to launch a Python REPLuvrs python <path> -m pdb <path>to launch PDB
This is equivalent to:
# Find the Python executable for the script's environment
python_path=$(uv python find --script <path>)
# Run uv pip with the script's Python
uv pip [args...] --python $python_pathThis allows you to use pip commands in the context of the script's virtual environment, such as:
uvrs pip <path> listto list installed packagesuvrs pip <path> show <package>to show package detailsuvrs pip <path> freezeto output installed packages in requirements format
This is equivalent to:
uv run --exact --script <path>Using --exact guarantees the managed virtual environment always matches the script's inline metadata before execution.
This means any dependency changes, including those introduced by uvrs add or uvrs remove, are respected the next time you run the script.
To initialize a new uv script with a uvrs shebang line use the init command:
uvrs init ~/bin/my-script --python 3.12This will create the file ~/bin/my-script using uv init --script ~/bin/my-script --python 3.12 and then add an appropriate shebang line to the beginning of the script.
By default, uvrs init also adds an exclude-newer timestamp to improve reproducibility:
#!/usr/bin/env uvrs
# /// script
# requires-python = ">=3.12"
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2025-10-15T20:30:45Z"
# ///
def main() -> None:
print("Hello from my-script!")
if __name__ == "__main__":
main()To skip adding the timestamp, use --no-stamp:
uvrs init ~/bin/my-script --no-stampTo update an existing Python script to use the uvrs shebang, use the fix command:
uvrs fix ~/bin/my-scriptThis command:
- Updates the shebang to
#!/usr/bin/env uvrs - Adds PEP 723 metadata with an
exclude-newertimestamp (if not present) - Runs
uv sync --script --upgradeto ensure the environment is up to date
For example, a plain Python script like:
#!/usr/bin/env python
print("Hello!")Will be transformed to:
#!/usr/bin/env uvrs
# /// script
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2025-10-16T00:25:00Z"
# ///
print("Hello!")To skip adding the timestamp and metadata, use --no-stamp:
uvrs fix ~/bin/my-script --no-stampTo update the dependencies within inline script metadata, use uvrs add and uvrs remove.
To add a new dependency:
uvrs add ~/bin/my-script 'rich'To remove a dependency:
uvrs remove ~/bin/my-script 'rich'The environment will sync automatically the next time you run the script (uvrs uses uv run --exact which ensures the environment matches the metadata exactly).
To update the exclude-newer timestamp and upgrade all dependencies to the latest versions allowed by your constraints, use the stamp command:
uvrs stamp ~/bin/my-scriptThis command:
- Updates the
exclude-newerfield in the script's[tool.uv]section to the current UTC timestamp - Runs
uv sync --script --upgradeto upgrade dependencies and rebuild the environment
The exclude-newer field limits package versions to those published before the specified timestamp, which improves reproducibility by preventing unexpected updates.
To inspect what's installed in a script's environment or run Python commands in its context, use the python and pip commands.
Run Python commands or a Python REPL in the script's environment:
uvrs python ~/bin/my-scriptInspect packages in the script's environment:
uvrs pip ~/bin/my-script listNote: For adding or removing dependencies, use uvrs add and uvrs remove instead, as they properly update the script's inline metadata.
Eventually, I would like to see a similar tool integrated into uv.
Until that time, I plan to maintain this uvrs tool.
Using uv scripts with inline-metadata already feels close to magical. From my viewpoint, uvrs mainly bridges three gaps that I hope uv will eventually close:
-
Exact execution by default: Update the documentation to recommend
uv run --exact --scriptinstead ofuv run --scriptin script shebang lines (uv issue #16334) -
Script bootstrapping: Update
uv init --scriptto add a shebang and set an executable bit. The shebang would either relying on/usr/bin/env -S(uv issue #11876) or use a new dedicated command (uv issue #16241). -
Timestamp management: Add a command to more easily "soft pin" to the current time using
exclude-newer: ideallyuv timestamp --scriptor even a generaluv config(uv issue #13123).