Skip to main content

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:

  1. Collect โ€” Discovers declared dependencies and locates their installed source code via importlib.metadata and site-packages.
  2. Inspect โ€” Parses your project's source with ast to find import statements, attribute accesses, and string references (e.g. Django settings).
  3. Trace โ€” Indexes each dependency's source, builds a call graph, and walks it transitively from your entry points to compute activated LLOC.
  4. 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):

  1. pyproject.toml โ€” PEP 621 [project] dependencies
  2. pyproject.toml โ€” Poetry [tool.poetry.dependencies]
  3. setup.py โ€” install_requires via AST (never executed)
  4. setup.cfg โ€” [options] install_requires
  5. requirements.txt โ€” with -r/--requirement include 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() resolves get as 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, .dylib files counted as opaque mass
  • Re-export shims: packages like cattrs/cattr where the import name differs from the implementation package
  • Private aliases: def _init(); init = _init patterns (common in sentry-sdk and 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.walk with directory pruning instead of pathlib.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: unladen trusts top_level.txt and RECORD from 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unladen-0.1.0.tar.gz (38.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unladen-0.1.0-py3-none-any.whl (44.7 kB view details)

Uploaded Python 3

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

Hashes for unladen-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9dc46c8b4ed2e697216e12625520342ee584ca5ed824caea30cd4fc929141f02
MD5 a39523bad3b3871c6b332a63a02dfd04
BLAKE2b-256 e6ca6d5224a11f7942823cacf336353615cef563f855dbc6ea840961dffb8c9f

See more details on using hashes here.

Provenance

The following attestation bundles were made for unladen-0.1.0.tar.gz:

Publisher: release.yml on miketheman/unladen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for unladen-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 39682aa04d5ce936673569a2594cfc8ce69ee98e945c2b9b970937500de4ba78
MD5 273de3312d942dfe1399de0fbb51101b
BLAKE2b-256 9a356a1200fb4b387c6dbf9e354a510e8a79143a9b6a347bb14924cecf235ed8

See more details on using hashes here.

Provenance

The following attestation bundles were made for unladen-0.1.0-py3-none-any.whl:

Publisher: release.yml on miketheman/unladen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page