Skip to main content

uvs

You have a useful, self-contained Python script under source control. You want to run it as a normal command from anywhere, without maintaining a package project just to put it on PATH.

uvs fills that narrow gap. It turns one supported Python file into a small, disposable package and asks uv to install it as a persistent tool. It also remembers the source file so that a later uvs update can install a new snapshot.

Project status: uvs 0.1 is experimental and seeking public validation. It works for the deliberately small contract below, but its usefulness beyond one person's workflow is still an open question. If you try it, please share whether it fits your work, including why it does not.

Is uvs a fit?

Choose uvs when:

  • your tool is one self-contained, UTF-8 Python file with a synchronous, top-level main();
  • you want an explicit installed snapshot, not a command that changes whenever its source file changes; and
  • you already use uv and want it to keep owning the tool environment and executable.

Do not choose uvs when:

  • your code needs sibling modules, data files, or a project directory: package it as a normal Python project instead;
  • you want to run directly from changing source: uv run path/to/script.py or a shell alias is a better fit;
  • you need an asynchronous, nested, or method-based entry point; or
  • you want a replacement for uv tool or a way to publish packages.

Try it, then remove the installed state

Requirements are Python 3.10 or newer and uv available on PATH. Install the 0.1.0 release from PyPI:

uv tool install uvs-cli
uvs --help

The distribution is named uvs-cli: the PyPI project uvs is an unrelated tool. The installed command is still uvs, and uv tool uninstall uvs-cli removes it. Because uv gives each tool its own isolated environment, the shared name inside the packages does not interfere with the install above. Do not pip install both distributions into one environment.

To track main instead of the released version, install "git+https://github.com/maphew/uvs.git".

If the shell cannot find uvs (or a tool installed later), run uv tool update-shell and restart the shell. uv tool dir --bin prints the executable directory if you prefer to add it to PATH yourself.

Save this as hello.py:

# /// script
# requires-python = ">=3.10"
# dependencies = []
# ///

def main() -> None:
    print("Hello from uvs")


if __name__ == "__main__":
    main()

Test the source, install it, and run the installed command:

uv run hello.py
uvs install hello.py
hello

Now edit hello.py so that its function prints different text:

def main() -> None:
    print("Hello from a new snapshot")

Install and run the new snapshot:

uvs update hello.py
hello

The first command removes both the installed hello tool and its uvs registry entry; the second removes uvs itself. Your source file remains untouched:

uvs uninstall hello
uv tool uninstall uvs-cli

The test matrix covers Windows and Linux. macOS has not been verified.

Supported script contract

uvs supports one UTF-8 Python file at a time. The file must be valid Python and define a synchronous main() function directly at module scope. An async def main, a method, or a function nested inside another function is not a supported entry point. A conventional if __name__ == "__main__": main() guard is fine.

PEP 723 metadata is optional. When present, it may contain dependencies, requires-python, and a tool table. Dependencies and the Python constraint are copied into the generated package. Other top-level metadata fields are rejected. The script, including its metadata comments, is copied verbatim into the snapshot; uvs does not collect sibling modules, data files, or a project directory.

The command name comes from the filename, with underscores changed to hyphens. Use uvs install --name another-name hello.py to choose it explicitly.

Snapshots, updates, and ownership

The installed command is a snapshot. Editing the original .py file does not change it. uvs update finds the registry entry by the source file's canonical path and reinstalls the tool only when the source hash changed. The original source path must still exist.

uvs install hello.py             # create and install the first snapshot
uvs list                         # list entries tracked by uvs
uvs show hello                   # show source, version, hash, and install time
uvs update hello.py              # reinstall if the source hash changed
uvs uninstall hello              # remove the uv tool and uvs registry entry

Each changed update advances the generated package's PEP 440 version to the next final release. Release tuples shorter than three components are padded before their final component is incremented; epochs are preserved, while pre, post, development, and local qualifiers are removed. For example, 1.2rc1 updates to 1.2.1, and 2!1.2.3.post1 updates to 2!1.2.4.

uv owns the installed tool environment and executable. uvs owns a small, platform-specific registry.json containing the source path, source hash, version, and install time for tools it installed. uvs list and uvs show describe that registry, not every tool known to uv; use uv tool list for the latter. Direct uv tool install operations do not appear in uvs list, and removing a managed tool directly with uv can leave a stale uvs entry. Prefer uvs uninstall for tools managed by uvs.

uvs show NAME --format simple emits five undecorated Label: value lines in this fixed order: Name, Source, Version, Installed, and Hash. Unlike the default Rich table, the simple format includes the complete source hash and is stable for plain-text consumers.

The CLI exposes additional flags on some commands. They are not part of the narrow contract documented here; consult uvs COMMAND --help for the current interface.

Troubleshooting

  • uv is not found: install it using the official uv instructions and ensure it is on PATH.
  • An installed command is not found: run uv tool update-shell, restart the shell, or use uv tool dir --bin to find the directory to add to PATH.
  • The script is rejected: run uv run path/to/script.py, check that it is UTF-8 and syntactically valid, and verify that it has a synchronous, top-level def main(...):.
  • PEP 723 metadata is rejected: use one closed # /// script block with valid TOML and only the supported top-level fields above.
  • uvs update cannot find the tool: pass the same source file used for installation. If it moved, uninstall by command name and install it again from the new path.
  • uvs list and uv tool list disagree: the former is uvs bookkeeping; the latter reports environments owned by uv. Reinstall with uvs or use uvs uninstall to reconcile a tracked tool.

Feedback and project policy

Success here means learning whether this small workflow is useful, and where it fails. Please use the focused form to share usefulness feedback or report a bug. Contributions are welcome; read CONTRIBUTING.md before starting. Report security concerns through SECURITY.md, not a public issue.

Project records: changelog, product contract, test inventory, and MIT license.

Development and tests

Create the locked development environment:

uv sync --dev --locked

Run the fast suite (the repository configuration excludes integration tests):

uv run pytest

Run only mocked subprocess workflow tests (these are component tests, not end-to-end tests):

uv run pytest -m mocked_workflow

Run the isolated lifecycle test against the real uv executable, serially:

uv run pytest -m integration -n 0

See examples/ for the minimal supported example.

Metadata

Release files for uvs-cli 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uvs-cli 0.1.1
File Size Uploaded Provenance Actions
uvs_cli-0.1.1.tar.gz 18.8 kB Without provenance Details for uvs_cli-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for uvs-cli 0.1.1
File Interpreter ABI Platform Actions
uvs_cli-0.1.1-py3-none-any.whl Python 3 none any Details for uvs_cli-0.1.1-py3-none-any.whl

Total release size: 39.4 kB

Release files / uvs_cli-0.1.1.tar.gz

Download URL uvs_cli-0.1.1.tar.gz
Size 18.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1f46fa526106a98a950eb86f2e61238820c424b0f07825ea86d55d28bed64959
BLAKE2b-256 checksum
How to use checksums
3f74dc1fbf729f9e5876edcaf451e79a200faf114c501f9ce84bfc4e3abb41b6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / uvs_cli-0.1.1-py3-none-any.whl

Download URL uvs_cli-0.1.1-py3-none-any.whl
Size 20.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
37e352649e595bad6500eaabbd56da7eccc9be98994b1f05975cc9507d099ffb
BLAKE2b-256 checksum
How to use checksums
8fd05486617161c041cfed3c25bb959c480e8e7c0b1ae15243f62a80d57cac0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for uvs-cli 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page