Measure the logical heft of your Python dependencies
Project description
๐ฅฅ unladen
[!WARNING] This project is in early alpha. Results are approximate and should be treated as starting points for investigation, not concrete recommendations. Static analysis has inherent limitations โ it cannot detect dynamic imports, plugin systems, or runtime-only usage patterns.
"What is the airspeed velocity of an unladen codebase?"
unladen measures the "logical mass" of your Python dependencies.
It doesn't just tell you that you use a library โ
it tells you how much of it you actually use.
By mapping your call graphs to a dependency's internal structure,
it provides data-driven recommendations to keep your codebase lean
and avoid carrying unnecessary "coconuts" in your project.
The Problem
Modern Python applications often pull in large libraries for a handful of functions. This "dependency bloat" increases:
- Attack surface โ unused code can still contain vulnerabilities.
- Cold boot times โ more code to find and load into memory.
- Deployment size โ heavier containers and slower CI/CD pipelines.
How It Works
unladen performs a four-phase static analysis:
- Collect โ Discovers declared dependencies and locates their installed
source code via
importlib.metadataandsite-packages. - Inspect โ Parses your project's source with
astto find import statements, attribute accesses, and string references (e.g. Django settings). - Trace โ Indexes each dependency's source, builds a call graph, and walks it transitively from your entry points to compute activated LLOC.
- Report โ Renders a table of results with per-dependency recommendations.
The Heft Metric
The core metric is the Heft Ratio: the percentage of a dependency's Logical Lines of Code (LLOC) that your project actually activates.
$$Heft = \frac{\text{Active LLOC}}{\text{Total LLOC}} \times 100%$$
LLOC counts executable statements (via ast),
excluding comments, blank lines, and docstrings.
if TYPE_CHECKING: blocks are excluded
since they never execute at runtime.
Recommendations
| Heft | Mass | Recommendation |
|---|---|---|
| > 25% | Any | Keep โ you use a significant portion |
| 5โ25% | Any | Review โ moderate usage, worth examining |
| 1โ5% | Low | Vendor โ consider copying the code you need |
| < 1% | High | Rewrite โ drop the coconut |
| < 1% | Low | Vendor โ small enough to inline |
| Unused | โ | Remove? โ declared but never imported |
| Native | โ | Keep (native) โ compiled extensions, LLOC analysis doesn't apply |
Mass threshold: Low < 500 LLOC, High >= 500 LLOC.
Usage
# Analyze all dependencies in the current project
unladen check
# Analyze a specific project path
unladen check /path/to/project
# Drill into a single dependency with file:line detail
unladen show requests
# Use an explicit requirements file
unladen check -r requirements/prod.txt
# Use an explicit site-packages directory
unladen check --site-packages /path/to/site-packages
Configuration
Project-level settings live in pyproject.toml
under [tool.unladen]:
[tool.unladen]
exclude = [
"setuptools",
"pip",
"wheel",
]
| Key | Type | Description |
|---|---|---|
exclude |
list[str] |
Dependencies to skip during analysis. Names are PEP 503 normalized, so "Jinja2", "jinja2", and "jinja-2" all match. |
When dependencies are excluded,
a footnote appears on the check table and treemap
noting the count,
and the show command marks excluded deps in the header.
Example Output
unladen - Dependency Heft Report
โโโโโโโโโโโโโโโโณโโโโโโโโโโณโโโโโโโโณโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโโโโโโณโโโโโโโโโโโโโโโโโ
โ โ โ Names โ โ โ LLOC โ โ
โ Dependency โ Version โ Used โ Files โ Heft % โ (active/total) โ Recommendation โ
โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
โ click โ 8.1.7 โ 5 โ 3 โ 32.1% โ 210/654 โ Keep โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโผโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโค
โ requests โ 2.31.0 โ 2 โ 1 โ 5.0% โ 50/1000 โ Review โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโผโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโค
โ nh3 โ 0.3.2 โ 1 โ 1 โ n/a โ 1 extension โ Keep (native) โ
โโโโโโโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโ
unladen show
The show command provides detailed per-dependency analysis:
- Where the dependency is declared (pyproject.toml, setup.py, setup.cfg)
- A table of every import statement with file:line locations
- String references
(e.g. Django
INSTALLED_APPS,MIDDLEWARE) - Heft computation with LLOC breakdown
- Warnings for dynamic dispatch patterns that may underestimate actual usage
Dependency Source Support
unladen discovers declared dependencies from
(in priority order):
pyproject.tomlโ PEP 621[project] dependenciespyproject.tomlโ Poetry[tool.poetry.dependencies]setup.pyโinstall_requiresvia AST (never executed)setup.cfgโ[options] install_requiresrequirements.txtโ with-r/--requirementinclude traversal
Use -r/--requirements to point at a specific file,
bypassing auto-detection.
This is useful for projects with split requirements
(e.g. requirements/prod.txt, requirements/dev.txt).
What It Detects
- Direct imports:
import requests,from click import echo - Bare import attribute access:
requests.get()resolvesgetas a used name - Submodule imports:
import pygments.lexers; pygments.lexers.get_lexer_by_name() - Aliased imports:
import pandas as pd; pd.DataFrame() - String references:
Django-style activation via
INSTALLED_APPS,MIDDLEWARE, and dotted path strings in settings files - Compiled extensions:
.so,.pyd,.dylibfiles counted as opaque mass - Re-export shims:
packages like
cattrs/cattrwhere the import name differs from the implementation package - Private aliases:
def _init(); init = _initpatterns (common insentry-sdkand similar)
Performance
unladen is designed to be fast on real-world projects:
- Parse-once โ each source file is parsed once and the AST tree is shared across all analysis passes.
- Parallel indexing โ large dependency trees (100+ files)
are indexed using Python 3.14's
InterpreterPoolExecutor(lightweight subinterpreters, no process overhead). - Bulk computation โ heft for all dependencies is computed in a single worker pool rather than per-dependency.
- Fast file discovery โ uses
os.walkwith directory pruning instead ofpathlib.rglob.
Limitations
- Static analysis only โ
cannot detect
importlib.import_module(),__import__(), or other dynamic imports at runtime. - Direct dependencies only โ does not analyze transitive dependency chains.
- Python 3.14+ required โ uses modern syntax features.
Requirements
- Python 3.14+
rich(the only runtime dependency)
Installation
Run directly, like:
pipx run unladen check
uvx unladen check
Or install it:
# With pip
pip install unladen
# With uv
uv tool install unladen
Architecture
| Phase | Module | Responsibility |
|---|---|---|
| 1. Collect (Nest) | collector.py |
Discover dependencies and locate installed source |
| 2. Inspect (Flight) | inspector.py |
Parse project code to find imports and usage |
| 3. Trace (Weight) | tracer.py |
Index dependency source, build call graph, compute heft |
| 4. Report (Coconut) | reporter.py |
Format findings as a rich CLI table |
Security
unladen is a read-only analysis tool.
It never executes third-party code โ
all source analysis uses ast.parse(),
which parses without compiling or running.
There are no eval(), exec(), subprocess,
or shell invocations anywhere in the codebase.
Known Limitations
- Metadata trust:
unladentruststop_level.txtandRECORDfrom installed distributions, mirroring pip's own behavior. A malicious package could ship metadata claiming import names belonging to another package, which would affect analysis accuracy but not execute code. - Deeply nested files: Some AST traversal functions use recursion. A crafted Python file with extreme nesting (1000+ levels) could hit Python's recursion limit and crash the tool. This cannot cause data loss or code execution.
Authors
License
MIT License. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unladen-0.1.0.tar.gz.
File metadata
- Download URL: unladen-0.1.0.tar.gz
- Upload date:
- Size: 38.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9dc46c8b4ed2e697216e12625520342ee584ca5ed824caea30cd4fc929141f02
|
|
| MD5 |
a39523bad3b3871c6b332a63a02dfd04
|
|
| BLAKE2b-256 |
e6ca6d5224a11f7942823cacf336353615cef563f855dbc6ea840961dffb8c9f
|
Provenance
The following attestation bundles were made for unladen-0.1.0.tar.gz:
Publisher:
release.yml on miketheman/unladen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
unladen-0.1.0.tar.gz -
Subject digest:
9dc46c8b4ed2e697216e12625520342ee584ca5ed824caea30cd4fc929141f02 - Sigstore transparency entry: 1110525111
- Sigstore integration time:
-
Permalink:
miketheman/unladen@9f0b92f4d773f72fd4f8ea3ea06a35ebde450832 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/miketheman
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9f0b92f4d773f72fd4f8ea3ea06a35ebde450832 -
Trigger Event:
push
-
Statement type:
File details
Details for the file unladen-0.1.0-py3-none-any.whl.
File metadata
- Download URL: unladen-0.1.0-py3-none-any.whl
- Upload date:
- Size: 44.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39682aa04d5ce936673569a2594cfc8ce69ee98e945c2b9b970937500de4ba78
|
|
| MD5 |
273de3312d942dfe1399de0fbb51101b
|
|
| BLAKE2b-256 |
9a356a1200fb4b387c6dbf9e354a510e8a79143a9b6a347bb14924cecf235ed8
|
Provenance
The following attestation bundles were made for unladen-0.1.0-py3-none-any.whl:
Publisher:
release.yml on miketheman/unladen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
unladen-0.1.0-py3-none-any.whl -
Subject digest:
39682aa04d5ce936673569a2594cfc8ce69ee98e945c2b9b970937500de4ba78 - Sigstore transparency entry: 1110525190
- Sigstore integration time:
-
Permalink:
miketheman/unladen@9f0b92f4d773f72fd4f8ea3ea06a35ebde450832 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/miketheman
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9f0b92f4d773f72fd4f8ea3ea06a35ebde450832 -
Trigger Event:
push
-
Statement type: