Create and run uv scripts with POSIX standardized shebang line
Project description
uvrs
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.
Why this exists
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
Installation
This should be installed as a globally available tool (so the above shebang line works):
uv tool install -p 3.14 uvrs
That will install uvrs using Python 3.14 (for nicely colorized help text).
What each command does
Here's what uvrs does under the hood, compared to the equivalent uv workflow:
uvrs init <path>
This is equivalent to:
uv init --script <path>
# Add #!/usr/bin/env uvrs shebang
# Add exclude-newer timestamp to metadata
chmod +x <path>
uvrs fix <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>
uvrs add <path> <package>
This is equivalent to:
uv add --script <path> <package>
uvrs remove <path> <package>
This is equivalent to:
uv remove --script <path> <package>
uvrs stamp <path>
This is equivalent to:
# Update exclude-newer timestamp to current time
uv sync --script <path> --upgrade
uvrs python <path> [args...]
This 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
uvrs pip <path> [args...]
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_path
This 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
uvrs <path>
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.
Creating new uv scripts
To initialize a new uv script with a uvrs shebang line use the init command:
uvrs init ~/bin/my-script --python 3.12
This 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-stamp
Updating existing scripts
To update an existing Python script to use the uvrs shebang, use the fix command:
uvrs fix ~/bin/my-script
This 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-stamp
Managing dependencies
To 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).
Updating timestamps and upgrading dependencies
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-script
This 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.
Inspecting a script's environment
To inspect what's installed in a script's environment or run Python commands in its context, use the python and pip commands.
Using uvrs python
Run Python commands or a Python REPL in the script's environment:
uvrs python ~/bin/my-script
Using uvrs pip
Inspect packages in the script's environment:
uvrs pip ~/bin/my-script list
Note: For adding or removing dependencies, use uvrs add and uvrs remove instead, as they properly update the script's inline metadata.
The goal
Eventually, I would like to see a similar tool integrated into uv.
Until that time, I plan to maintain this uvrs tool.
How uv could make uvrs largely unnecessary
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).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uvrs-0.9.0.tar.gz.
File metadata
- Download URL: uvrs-0.9.0.tar.gz
- Upload date:
- Size: 8.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d90f1aac1e784994bf67daaaaf4092555290135ac2c67f3b1ef910f4c0ceefd4
|
|
| MD5 |
7aa968ff5d458c8fde0a9892f0321f3b
|
|
| BLAKE2b-256 |
c765efd03887d4cac5663e4e9ea56e5248c67eba52adb55bb42a4177736a4091
|
File details
Details for the file uvrs-0.9.0-py3-none-any.whl.
File metadata
- Download URL: uvrs-0.9.0-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ee5643835cc3b8bbc31c8df0da597c9161ce98f5f0af706ea80239be0f88c4b
|
|
| MD5 |
27ce0e04e7e5907230eb8900c1f6258f
|
|
| BLAKE2b-256 |
6f0753d9d600265a42a232d4d70c7612f746fcd8dc665fcc06af5c2ce6daedc2
|