Skip to main content

uvlazy

Run the tool you need, at the project's locked version, without installing the rest of the project.

uvlazy run rumdl check .

uvlazy finds the declared package providing rumdl, reads the version constraints from uv.lock, installs that package and its dependency tree into a dedicated environment, and runs its executable. Declared application dependencies are installed if the tool or its Python subprocesses import them; unrelated packages stay uninstalled. Python console scripts and native executables both work.

This is experimental software for macOS and Linux. It requires Python 3.11+ and uv 0.6.6+ on PATH. Cargo and PyPI install the same Rust executable, which bundles a Python engine with no third-party runtime dependencies.

Install

Install through either registry:

cargo install uvlazy --locked
# or
uv tool install uvlazy
# or, in a Python environment
pip install uvlazy

Or install from this checkout (both routes require Rust):

cargo install --path . --locked
# or
uv tool install .

Or run directly from source without installing uvlazy:

PYTHONPATH=/path/to/uvlazy/src python3 -m uvlazy run rumdl check .

The executable uses uv python find --system --no-project to locate an installed Python 3.11+ without syncing a project. Set UV_PYTHON to choose a specific interpreter, for example UV_PYTHON=3.12 uvlazy run rumdl check .. If no compatible Python is installed, run uv python install 3.11 first.

uvlazy invokes the uv executable on PATH, including version-manager shims. If a mise shim reports an untrusted project configuration, review that file and use mise trust to approve it before retrying. A shim or uv configuration failure does not mean Python is missing; the underlying diagnostic explains what needs fixing.

Use in CI

Declare the tool in your project's pyproject.toml, alongside your existing application dependencies:

[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["rich>=13", "Pillow>=10", "rumdl"]

No dependency groups or separate tool configuration are required when the command and package names match.

Generate or update uv.lock during development with uv lock, and commit it with your project. In CI, with uv and uvlazy available, run:

uvlazy run rumdl check .

There is no preceding uv sync step. Tool commands require an existing, current lockfile; a missing or stale lock fails without updating it. The selected tool's exit status, signals, stdin, stdout, stderr, arguments, and working directory are preserved, so lint failures fail the job normally. Installation diagnostics go to stderr; --quiet suppresses installation progress.

The CI example includes a lockfile and declares Rich, Pillow, and rumdl. Running the following installs only rumdl into the example's environment:

uvlazy run --project examples/ci rumdl check examples/ci

--project selects the dependency configuration without changing the current working directory. Every option for uvlazy goes before the tool name; everything after it is passed to the tool unchanged.

Package names and dependency groups

Matching command/package names such as rumdl need no configuration. The known alias cdk also selects aws-cdk-cli automatically when that package is declared:

uvlazy run cdk --version

This installs aws-cdk-cli and its dependencies. When cdk ls launches python3 app.py, that child Python process also installs declared dependencies on import. In particular, import aws_cdk selects a declared aws-cdk-lib dependency automatically. The SDK is installed only when imported; unrelated application dependencies and tools stay uninstalled.

This works with an ordinary "app": "python3 app.py" in cdk.json. An application runner containing uv run still has uv's usual sync behavior.

For other commands whose package name differs, select the declared provider explicitly:

uvlazy run --from httpie http --help

Or save that mapping:

[dependency-groups]
dev = ["httpie"]

[tool.uvlazy.commands]
http = "httpie"

Then use uvlazy run http --help. Providers must be declared in [project].dependencies, [dependency-groups], or legacy [tool.uv].dev-dependencies. Unknown commands fail without installing arbitrary packages or falling back to executables from the system PATH. The executable must belong to the selected package.

By default, a tool declared in project dependencies uses those declarations. Otherwise, uvlazy automatically selects groups that directly declare the tool. Only the requested package and its dependency tree are installed, including when its group contains other tools. Nested {include-group = "lint"} declarations are supported.

To select a particular group, use:

uvlazy run --group lint rumdl check .

--group can be repeated. This is also useful when the same tool has different requirements in different groups: choose compatible groups explicitly. uv validates the selected combination against the project's lockfile.

Lazy Python imports

Scripts and modules retain the original import-based behavior:

uvlazy run app.py --your-argument
uvlazy run -m your_module
uvlazy run --group dev app.py
uvlazy run --locked app.py

A script starts in a separate environment and installs declared packages when execution first reaches their missing imports. Each imported package brings its normal dependency tree. The script is never restarted, and exceptions inside imported packages propagate normally. Existing modules and your local code take precedence.

The import hook also applies to Python tools and their Python subprocesses in the managed environment, including children started by native executables or shell scripts. They inherit the same dependency selection and lock constraints. Ordinary python/python3 commands on PATH and sys.executable use that environment. A child that selects another environment, clears the inherited activation variable, or disables Python's site initialization with -S does not activate the hook.

By default, scripts select [project].dependencies; --group adds groups. Mappings handle differing import and distribution names:

[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["Pillow>=10"]

[tool.uvlazy.imports]
PIL = "Pillow"

from PIL import Image triggers Pillow's installation. An undeclared missing import raises ModuleNotFoundError normally. Top-level imports count as use, even when no functions from the package are subsequently called. Import probes such as importlib.util.find_spec() can also trigger installation.

The conditional example has plain, pretty, and image branches. From this checkout:

uvlazy run --project examples/conditional examples/conditional/demo.py plain
uvlazy run --project examples/conditional examples/conditional/demo.py pretty
uvlazy run --project examples/conditional examples/conditional/demo.py image

Unlike tool commands, scripts may run without a lockfile. At the first missing import, uv pip compile resolves all selected requirements and caches the pins. That resolution can fetch metadata or build artifacts for unused dependencies, and an unsatisfiable unused dependency can prevent it. With uv.lock, the pins come from uv export --locked. Add --locked to require and validate the lock before any application code runs, even on paths that import nothing.

Environments and caching

Environments and constraints live in .uvlazy/<fingerprint>/. Each tool package and group selection has its own environment, separate from Python script runs. Subsequent runs reuse installed packages. Concurrent runs serialize environment creation and installation. A warm tool environment can run offline.

Changes to pyproject.toml, uv.lock, local uv.toml, the launcher interpreter, selected groups, uvlazy version, or checkout path select a fresh environment. This avoids reusing virtualenv scripts whose absolute paths point to another checkout. Existing .venv and uv.lock files are left alone.

For CI, cache uv's download cache to reuse package artifacts across jobs. .uvlazy environments contain absolute paths and should only be reused at the same checkout path with a compatible interpreter. Add .uvlazy/ to the project's .gitignore. Delete it while no uvlazy processes are running to start fresh.

The native executable also caches its bundled engine under $XDG_CACHE_HOME/uvlazy (default: ~/Library/Caches/uvlazy on macOS or ~/.cache/uvlazy on Linux). UVLAZY_CACHE_DIR overrides that location. The archive is keyed by its contents and recreated locally when needed.

Current boundaries

  • Tool runs install the tool's normal dependency tree. Missing imports in Python tools or their managed Python children can install declared dependencies, but undeclared plugins are not installed automatically. Tools that require project installation or discover plugins through package metadata still need that setup separately.
  • Lock export supplies version/source constraints without enforcing lockfile artifact hashes. This is not a complete replacement for uv's locked sync.
  • The project itself is not installed. Editable installs and uv sources/workspaces remain unsupported; uv sources/workspaces are rejected explicitly.
  • Project optional-dependency groups cannot be selected yet. Extras within a selected requirement and environment markers are passed through to uv.
  • For Python scripts, metadata queries, plugin discovery, namespace-package mappings, and newly installed .pth hooks have no special lazy handling.
  • Set UV_PYTHON to an installed interpreter compatible with the project and selected groups. The default selects an installed Python 3.11+; it does not infer the project's Python requirement. Windows is not supported yet.

Development

The integration suite generates local wheels and exercises real uv with network access disabled, including command execution and selective installation.

cargo build --locked
UVLAZY_TEST_BINARY=target/debug/uvlazy PYTHONPATH=src \
  python3 -m unittest discover -s tests -v
PYTHONPATH=src python3 -m unittest discover -s tests -v
cargo fmt --check
cargo clippy --locked --all-targets -- -D warnings
ruff check src tests scripts examples
ruff format --check src tests scripts examples

Cargo.toml is the version source for both registries. maturin uses bindings = "bin" to put the native executable into platform wheels. Cargo builds embed the Python sources without requiring Python at build time; running the installed command needs no checkout or separately installed uvlazy Python package.

Verify both source packages before a release:

cargo package --locked
maturin build --release --sdist --locked --out dist

CI installs and tests both the Cargo executable and a wheel rebuilt from the source distribution, with offline local-wheel fixtures. It covers Linux and macOS, Python 3.11 and 3.14, and the minimum and latest uv versions.

The release workflow builds Linux and macOS wheels for x86_64 and aarch64, plus a source distribution. A matching v<version> tag publishes to crates.io and PyPI in independent jobs using CARGO_REGISTRY_TOKEN and PYPI_API_TOKEN repository secrets. Manual runs default to dry_run: true; publishing requires a matching version tag. After both registries succeed, the workflow creates a GitHub release with the packages and SHA-256 checksums.

The implementation uses uv's lockfile export and constraints, and Python's import finder API.

Metadata

Release files for uvlazy 0.1.2

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

Source distribution (sdist)

Source distribution for uvlazy 0.1.2
File Size Uploaded Provenance Actions
uvlazy-0.1.2.tar.gz 24.6 kB Without provenance Details for uvlazy-0.1.2.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for uvlazy 0.1.2
File Actions
uvlazy-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for uvlazy-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
uvlazy-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for uvlazy-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
uvlazy-0.1.2-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for uvlazy-0.1.2-py3-none-macosx_11_0_arm64.whl
uvlazy-0.1.2-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details for uvlazy-0.1.2-py3-none-macosx_10_12_x86_64.whl

Total release size: 830.7 kB

Release files / uvlazy-0.1.2.tar.gz

Download URL uvlazy-0.1.2.tar.gz
Size 24.6 kB
Tags Source
SHA-256 checksum
How to use checksums
9771f29bea50836959e41dcb0b027e8dce9454220c8cab9afaa61b6d681bb0f9
BLAKE2b-256 checksum
How to use checksums
1eb57676da3b18001e8bb94c7852faed57ef9e690f07b2832ce1fc896b889bcc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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 / uvlazy-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL uvlazy-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 212.5 kB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
1fa1232a27b8261010aaccb80e2588b38ad41b2fcb7739cf2b752fb6d0642092
BLAKE2b-256 checksum
How to use checksums
4261b7cd57c265813f98ed8966d20075b112189b521de8fc38855fd3edab6e2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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 / uvlazy-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL uvlazy-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 202.5 kB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
635748045985c32f24cdb5f7d68362deaca69cca53c94a787bd2c4814260af48
BLAKE2b-256 checksum
How to use checksums
338614d0af81aede6d68eeb05de68202134528e3aec6e64aee3741a7f3e7f719
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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 / uvlazy-0.1.2-py3-none-macosx_11_0_arm64.whl

Download URL uvlazy-0.1.2-py3-none-macosx_11_0_arm64.whl
Size 190.2 kB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
3ebeb7f4a81a02d790104ee63b97ce0cf5148af3e3f1b070262c547292554d22
BLAKE2b-256 checksum
How to use checksums
5558b4fd93f1ea53c4283de19b105d2565b2ac2907c2e3e2676fb647b4030d1c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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 / uvlazy-0.1.2-py3-none-macosx_10_12_x86_64.whl

Download URL uvlazy-0.1.2-py3-none-macosx_10_12_x86_64.whl
Size 201.0 kB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
0f15fea54fa50e51a3828b34db1e491f43012ae05954a870351a9da2363bea12
BLAKE2b-256 checksum
How to use checksums
54a4f8eb27be9b221a85fe3f9b8db9836912b0204078b89db33a83d019d273c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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 uvlazy 0.1.2

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.

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