Open-source project
rendeirolab/LazySlide avatar
rendeirolab/LazySlide

LazySlide: a version read from git tags, fourteen dependency floors, and a licence link with the wrong spelling

Accessible and interoperable whole slide image analysis

328 stars32 forksPythonMIT

At a glance

What is it?
A whole slide image framework that borrows its data structures from the single cell ecosystem and gets its features through PyTorch models. Its build derives the version from git, its base install pulls a pre one sibling package, and the one worked example reaches for the dependency's own file opener.
Who is it for?
LazySlide fits a histology group already working in SpatialData and scverse who wants one pipeline call for tissue finding, tiling and feature extraction instead of three libraries. It does not fit anyone who needs a pinned, reproducible install, because every runtime dependency is a floor with no ceiling and the version itself is computed at build time from a tag.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The version does not exist in pyproject.toml and the Docker image fakes one

`pyproject.toml` declares `dynamic = ["version"]` and builds through hatchling with `uv-dynamic-versioning` in the build requirements. No version number is written anywhere in the file. The number you install therefore comes from whatever git tag the build sees, which means a wheel built from a commit with no tag on it resolves to something other than a release.

The Dockerfile makes the gap explicit. It takes a build argument called `VERSION` defaulting to `0.0.0+docker` and passes it through an environment variable named `UV_DYNAMIC_VERSIONING_BYPASS`, with a comment saying the value is injected by CI or a local build and that the git lookup is bypassed. So a container built by hand reports `0.0.0+docker` while the same source built by the release pipeline reports the tag.

The release list shows the gap between tags and work. `v0.11.1` on 2026-05-30, `v0.12.0rc0` on 2026-06-20, `v0.12.0` on 2026-06-22, two days after the candidate. The last push is 2026-09-30, more than three months after the newest tag, so the default branch is well ahead of anything installable and nothing in the metadata distinguishes the two.

The package itself is classified as `Development Status :: 3 - Alpha`, which is the only maturity signal anywhere in the metadata.

Fourteen runtime dependencies, every one a floor with no ceiling

The base dependency list holds fourteen entries and every one is a lower bound with no upper limit. They range from light utilities, `matplotlib>=3.9.0`, `rich>=13.0.0`, `cyclopts>=3.0.0`, `seaborn>=0.12.2`, `psutil>=5.9.0`, through the imaging stack `scikit-learn>=1.0`, `scikit-image>=0.23.1`, `matplotlib-scalebar>=0.9.0`, `legendkit>=0.3.4`, to `torch>=2.0.0`, `timm>=1.0.3`, `wsidata>=0.12.0` and `lazyslide-models>=0.0.4`.

The last of those deserves attention. `lazyslide-models` is a sibling package still at a zero version, and it is in the base install, not in an extra. So the floor of the framework is a pre one package with no ceiling, and a breaking release of it will be picked up by any new install.

The build requirements take the opposite approach and state no bounds at all, `hatchling` and `uv-dynamic-versioning` with no version constraints. Runtime dependencies have floors, the tools that build them have nothing.

The extras overlap. `torchstain` appears in both `all` and `docker`. `scanpy` appears in both `all` and `docker`, once with a floor of `1.10.4` and once with none. `transformers>=4.49.0` appears in both `all` and `models`. The `all` extra also carries `torchvision>=0.15` followed by a comment reading `# >0.15`, which restates the constraint instead of adding anything, and `docker` pins nothing at all, listing jupyterlab, ipython, torchstain, scanpy and a further entry the file truncates.

The quick start opens your own slide through the dependency, not through LazySlide

The worked example runs the full pipeline in three calls and then reads the result back:

python
import lazyslide as zs

wsi = zs.datasets.sample()

# Pipeline
zs.pp.find_tissues(wsi)
zs.pp.tile_tissues(wsi, tile_px=256, mpp=0.5)
zs.tl.feature_extraction(wsi, model="resnet50")

# Access the features
features = wsi["resnet50_tiles"]

# Visualize the 1st and 99th features
zs.pl.tiles(wsi, feature_key="resnet50", color=["1", "99"])

The slide you bring is opened by something else. The section for your own file is four lines long and imports from the dependency directly, `from wsidata import open_wsi`, then calls `open_wsi("path_to_slide")`. So the documented entry point for real data is `wsidata`'s function, and LazySlide's own surface for opening a slide is never shown.

Two naming details sit next to each other. The features are stored under the key `resnet50_tiles`, with a suffix, while the plotting call asks for `feature_key="resnet50"`, without it. Two spellings for one artefact, in four adjacent lines.

The tiling call passes `tile_px=256` together with `mpp=0.5`, so the tissue is resampled to half a micron per pixel before being cut into 256 pixel tiles. Nothing in the file states what `mpp` does to a slide already stored at a different resolution, or whether the original magnification is preserved anywhere.

The visualisation call passes two strings, `"1"` and `"99"`, to a parameter named `color`, and the comment above it says it draws the first and the ninety ninth feature. The parameter holds ordinal positions rather than colours.

Four seconds to install with torch in the base list, seven seconds to extract features

Two timings are given, both on a MacBook Pro. Installation with uv takes about four seconds. The quick start pipeline, which covers tissue segmentation, tiling and feature extraction, takes about seven seconds.

The four second figure covers an install whose base dependencies include `torch>=2.0.0`, `timm>=1.0.3`, `scikit-image>=0.23.1` and `lazyslide-models>=0.0.4`. No wheel sizes, no cache state, no machine specification and no note about whether the figure assumes a warm uv cache appear alongside it, so the number cannot be checked against anything.

The seven second figure covers a call that loads a model and runs it over every tile of a slide. The slide itself comes from `zs.datasets.sample()`, and neither the size of that sample nor whether it is fetched over the network on first use is stated. If it is fetched, the seven seconds covers a download.

Both figures are also unattributed to a configuration. Nothing says whether the GPU or Apple silicon acceleration path was used, and the key features list does claim scalability through efficient handling of large slides, which is a separate claim from either timing.

System requirements are stated more carefully than the timings: tested from Python 3.11 to 3.13 with a GitHub Action, on Windows, Linux and MacOS. The metadata floor is `requires-python = ">=3.11"` with no ceiling, so a Python newer than 3.13 will install without having been tested.

The licence link points at LICENCE while the repository ships LICENSE

The final section reads that LazySlide is released under the MIT License, and the words MIT License are a link to a relative path spelled `LICENCE`, with the c before the e. The repository root contains a file spelled the other way round, `LICENSE`, in American spelling.

So the one licence link in the README does not resolve to the file in the tree. Everything else about the licence is correct: `pyproject.toml` sets `license = "MIT"`, the classifier list carries `License :: OSI Approved :: MIT License`, and a licence file is present. It is the path that is wrong.

The documentation is hosted at two different hosts in two different places. The repository record lists the homepage as `lazyslide.rtfd.io`, while the badge row and the documentation section both point at `lazyslide.readthedocs.io`. Two hostnames for what is presumably the same build.

And the documentation links split across two version trees. The installation link, the tutorials link and the badge all use the `en/stable` path, while the contributing guide uses `en/latest/contributing/index.html`. A contributor following that link lands on a different build of the instructions than a user following installation, and a `.readthedocs.yaml` sits in the root configuring which of them is served where.

The classifier list also carries `Topic :: File Formats`, which classifies file extensions rather than a research area, alongside the more expected `Topic :: Scientific/Engineering :: Bio-Informatics`. `Operating System :: OS Independent` is the third claim about the operating systems, after the three platforms named in the system requirements section and the Dockerfile.

Four foundation models are advertised and no extra is named for them

The key features claim native integration with what it calls state of the art foundation models, naming four, UNI, CONCH, Gigapath and Virchow, for zero-shot classification and captioning. The quick start uses none of them. It passes `model="resnet50"` to the feature extraction call.

Those are two different paths through the library, and the file never says how to reach the first one. There is no function name, no example, and no statement of which extra is required. The `models` extra exists and carries `transformers>=4.49.0`, `open-clip-torch>=2.32.0`, `einops>=0.8.1`, `einops-exts>=0.0.4`, `environs>=14.1.1` and `sacremoses>=0.1.1`, which are the libraries an open_clip or transformers backed model would need, but the README does not connect that extra to any of the four named models.

The same ambiguity applies to the multimodal claim. Combining histological data with transcriptomics, genomics and textual annotations is listed as a key feature, and `scanpy>=1.10.4` sits in the `all` extra, but nothing states whether transcriptomic data is joined by an identifier LazySlide reads, and nothing in the quick start shows a join.

Interoperability is the claim with the most detail behind it: the framework is built on SpatialData and is named as compatible with scanpy, anndata and squidpy. That is the part of the description a reader can act on directly, and it is also the part that tells you the expected data model, which is why the dependency on `wsidata` for opening a slide matters more than it first appears.

The image keeps uv in the runtime stage and puts the model cache in a temp directory

The Dockerfile is multi-stage, with a builder and a runtime stage, both starting from `python:3.13-slim`. The builder installs a default JDK, build-essential and git through apt before running `uv sync --extra all --extra models --extra docker --no-install-project`, so all three extras are resolved even though the project itself is excluded from that step.

The runtime stage copies uv back in. The comment says to include uv in case a user wants to use it, and the line copies `/uv` and `/uvx` from `ghcr.io/astral-sh/uv:latest` into `/usr/local/bin`. That is the uv binary from a floating tag, in both stages, so the toolchain that builds the image is not pinned to a version. It also means the runtime image carries a package manager that the minimal stage was meant to shed.

Two more environment variables decide behaviour. `HF_HOME=/tmp/hf_home` points any Hugging Face download at a temporary directory inside the container, so any model weights fetched during a run disappear with the container. `MAX_JOBS=4` caps parallel compilation, which is the setting that decides how long the torch build in the builder stage takes.

`PYTORCH_ENABLE_MPS_FALLBACK=1` is also set, which is a macOS acceleration path in a Linux image, and `DEBIAN_FRONTEND=noninteractive` is set while `default-jdk` is installed, a combination where apt will accept a default rather than prompt.

The usage comments at the top of the file give three invocations: plain `python`, `jupyter lab` bound to port 8888 with `--allow-root`, and `ipython`. The image runs as root, which is why that flag is needed.

The logo element in the README is empty and the badge row has no text

The header wraps an empty picture element in a centred paragraph, opening a picture tag and closing it with nothing between. A second centred paragraph appears after the description and is also empty. So the two decorative blocks in the README render as blank space, while the repository carries an `assets/` directory.

The badge row above them is six links with no alt text between the brackets: the readthedocs entry, a DOI at Nature Methods, and then four more, followed by a link row for installation, tutorials, a preprint DOI, and the Nature Methods DOI again. The Nature Methods identifier appears twice, once in the badge row and once in the link row, and the preprint is the other published reference.

None of that is a citation. There is no author list, no year, no how to cite block, and no mapping between the paper and the version. The author field in the metadata names three people with institutional addresses at the same domain, and there is no maintainer field.

The contribution note asks for help with documentation, tests and suggestions, and points at issues and pull requests. The repository already carries `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` and a `.pre-commit-config.yaml`, none of which is linked from the README, and the README's own contributing link goes to a documentation page rather than to the file at the root.

The documentation section describes the docs as including tutorials, API references and guides. What it does not include is a migration or changelog page, which for a package whose version moves by tag is the one reference that would help most.

Editorial conclusion

LazySlide fits a histology group already working in SpatialData and scverse who wants one pipeline call for tissue finding, tiling and feature extraction instead of three libraries. It does not fit anyone who needs a pinned, reproducible install, because every runtime dependency is a floor with no ceiling and the version itself is computed at build time from a tag. Before you adopt it, check the `wsidata` floor against what you have, decide whether the Docker image's synthetic version is acceptable for your provenance, and read `pyproject.toml` rather than the README for the extras, because the documentation never says which extra carries the foundation models it advertises.

Frequently asked questions

How do I install LazySlide and what Python versions are supported?

From PyPI with `pip install lazyslide` or `uv add lazyslide`, on Python 3.11 and newer. The metadata floor is `requires-python = ">=3.11"` with no ceiling, and the system requirements section says it has been tested from 3.11 to 3.13 with a GitHub Action on Windows, Linux and MacOS.

How do I open my own slide with LazySlide?

Through its dependency, not through its own API. The documentation shows `from wsidata import open_wsi` followed by `open_wsi("path_to_slide")`, and `wsidata` is a base dependency with a floor of 0.12.0. LazySlide's own surface for opening a file is not shown anywhere.

Which models does LazySlide use for feature extraction?

The quick start passes `model="resnet50"` to `zs.tl.feature_extraction`. Separately, the feature list claims native integration with four foundation models, UNI, CONCH, Gigapath and Virchow, for zero-shot classification and captioning, without naming a function or an extra for any of them. The `models` extra carries transformers and open-clip-torch.

What version of LazySlide does a Docker build report?

The default is `0.0.0+docker`. The version comes from git tags through a dynamic backend, and the Dockerfile passes an injected `VERSION` argument through a bypass variable specifically to skip the git lookup. The default branch was last pushed on 2026-09-30, after the newest tag v0.12.0 on 2026-06-22.

Under which licence is LazySlide released?

MIT. The metadata sets `license = "MIT"` and carries an MIT classifier, and a LICENSE file sits at the repository root. The README's own licence link points at a relative path spelled `LICENCE`, which is not the name of the file in the tree.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. rendeirolab/LazySlide on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rendeirolab-lazyslide.svg)](https://hysenlabs.com/projects/rendeirolab-lazyslide)