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. Application dependencies and unrelated tools 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.

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. For a command whose package name differs, select its 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.

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, 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. They do not inject a lazy import hook into the executable or automatically install undeclared plugins. Tools that need the application installed, such as many pytest workflows, still need a complete project environment.
  • 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. New Python subprocesses do not inherit the import hook.
  • 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.0

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.0
File Size Uploaded Provenance Actions
uvlazy-0.1.0.tar.gz 21.0 kB Without provenance Details for uvlazy-0.1.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for uvlazy 0.1.0
File Actions
uvlazy-0.1.0-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.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
uvlazy-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for uvlazy-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
uvlazy-0.1.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for uvlazy-0.1.0-py3-none-macosx_11_0_arm64.whl
uvlazy-0.1.0-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details for uvlazy-0.1.0-py3-none-macosx_10_12_x86_64.whl

Total release size: 816.6 kB

Release files / uvlazy-0.1.0.tar.gz

Download URL uvlazy-0.1.0.tar.gz
Size 21.0 kB
Tags Source
SHA-256 checksum
How to use checksums
84905ae7a77b35de8e0a496ae7f8d90aa12ea92db4b6afb2ac448ba3bc1ea98c
BLAKE2b-256 checksum
How to use checksums
30b31c38300331e588fe6b8566be75664ecd83a5ec060c2fe11c47f500233c0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","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.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

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

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

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

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

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