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
.pthhooks have no special lazy handling. - Set
UV_PYTHONto 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)
| 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)
| File | Actions Reset | |||
|---|---|---|---|---|
| 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.