DEV Community

Mohammad Raziei
Mohammad Raziei

Posted on

Your Package Is Not As Lightweight As You Think

There's a claim you've probably seen in a README before:

"Zero dependencies. Lightweight. Minimal footprint."

It sounds great. But most of the time, it's only half the story.


The Weight You Don't See

When you run pip install some-package, you're not installing one thing. You're installing that package plus every library it depends on, plus every library those libraries depend on. The size printed on PyPI is just the tip of the iceberg.

This matters a lot in constrained environments: embedded systems, Docker containers you want to keep slim, serverless functions with cold start sensitivity, CI pipelines that install fresh on every run, or HPC clusters where storage quotas are real and network bandwidth costs time.

And yet, almost no one measures this before claiming their package is "lightweight."


A Real Example: XML Parsing in Python

I was building pygixml, a Python binding for the pugixml C++ library, aimed at high-performance XML parsing. At some point, I claimed it was lighter than the alternatives.

But lighter compared to what, exactly? And measured how?

I wrote a small tool called pip-size to find out. It queries the PyPI JSON API and calculates the real download size of a package — the wheel file itself — along with the complete transitive dependency tree. No downloads, no installs, no guesswork.

Here's what it showed for the three main Python XML parsing libraries:

$ pip-size pygixml
  pygixml==0.6.0  167.3 KB

$ pip-size pugixml
  pugixml==0.7.0  375.1 KB

$ pip-size lxml
  lxml==6.0.2  5.0 MB
Enter fullscreen mode Exit fullscreen mode

The comparison holds up: pygixml is about 2.2× lighter than pugixml and roughly 30× lighter than lxml. In this case none of the three have significant Python-level dependencies, so the package itself is the story.

But that's not always the case.


When Dependencies Change Everything

Let me show you a more dramatic scenario. Imagine you're choosing an HTTP client for a minimal service:

$ pip-size httpie
  httpie==3.2.4  119.2 KB  (total: 4.1 MB)
  ├── requests==2.32.5  63.2 KB  (total: 834.8 KB)
  │   ├── urllib3==2.3.0  341.8 KB
  │   ├── charset-normalizer==3.4.1  204.8 KB
  │   ├── certifi==2025.1.31  164.0 KB
  │   └── idna==3.10  61.4 KB
  ├── rich==13.9.4  238.1 KB  (total: 1.2 MB)
  │   ├── markdown-it-py==3.0.0  87.3 KB
  │   └── pygments==2.19.1  4.4 MB
  └── ...
Enter fullscreen mode Exit fullscreen mode

The package itself is 119 KB. Its total footprint is 4.1 MB. That's a 34× multiplier hidden behind a single pip install.

This is not a criticism of httpie — it's a fully-featured CLI tool and those dependencies are justified. The point is that the number on the PyPI page is almost never the number that matters.


The Fairness Problem

Here's the thing that bothered me when I started thinking about this:

If library A claims to be "lightweight" and library B doesn't make that claim, but A pulls in 800 KB of dependencies while B pulls in 200 KB — who's actually lighter?

The "lightweight" claim is often made based on the package's own size, or the number of dependencies, rather than the actual bytes that land on disk. Neither of those is a fair measure.

A fair comparison looks at the full dependency tree. And that's what pip-size is designed to make easy — no installation required, just a quick query against PyPI's public API.


How pip-size Works

The tool uses PyPI's JSON API (https://pypi.org/pypi/{package}/json) to:

  1. Resolve the correct version based on your specifier
  2. Select the right wheel for your platform, using the same priority logic as pip itself
  3. Walk the requires_dist metadata to find all dependencies
  4. Resolve each dependency recursively, in concurrent BFS layers
  5. Report the size at every level of the tree

The output is a tree where every intermediate node shows both its own size and the total weight of its subtree — so you can see at a glance which dependency is responsible for the bulk of the footprint.

  fastapi==0.115.12  276.3 KB  (total: 1.1 MB)
  ├── starlette==0.46.1  254.0 KB  (total: 481.2 KB)
  │   └── anyio==4.9.0  227.2 KB
  ├── pydantic==2.11.3  440.5 KB  (total: 1.6 MB)  ← here's your culprit
  │   ├── pydantic-core==2.33.1  1.8 MB
  │   └── ...
  └── ...
Enter fullscreen mode Exit fullscreen mode

When to Use This

A few concrete situations where this kind of measurement is useful:

Before publishing a library. If you're telling users your library is lightweight, measure it. Run pip-size your-package and check whether the claim survives contact with reality.

Choosing between alternatives. pip-size requests vs pip-size httpx vs pip-size aiohttp gives you a side-by-side cost comparison without installing anything.

Auditing a project's dependencies. pip-size your-project before a Docker build tells you where the size is coming from and which dependency is worth optimizing.

CI size budgets. The --quiet --bytes flags output a raw number, which you can compare against a threshold in a shell script or GitHub Action.

SIZE=$(pip-size my-package --quiet --bytes)
if [ "$SIZE" -gt 5000000 ]; then
  echo "Package exceeds 5 MB size budget"
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Optional Dependencies

One more thing worth mentioning: pip-size handles optional dependencies (extras) correctly.

By default, it only includes dependencies that are always required — the same ones pip would install for a plain pip install package. If you want to see the cost of enabling specific extras:

$ pip-size "requests[security]"
  requests==2.32.5  63.2 KB  (total: 1.2 MB)
  ├── urllib3==2.3.0  341.8 KB
  ├── ...
  ├── cryptography==44.0.3  518.2 KB  [extra: security]
  └── pyOpenSSL==25.0.0  112.4 KB    [extra: security]
Enter fullscreen mode Exit fullscreen mode

Or if you want the absolute worst-case footprint — every optional dependency across the entire tree — use --all-extras.


Conclusion

The size shown on a PyPI package page is that package's size. The size that actually matters is the size of everything it brings with it.

Before claiming a package is lightweight, measure it. Before choosing between libraries, compare their full footprint. Before shipping a container, know what's in it.

pip-size is available on PyPI:

pip install pip-size
Enter fullscreen mode Exit fullscreen mode

Source: github.com/mohammadraziei/pip-size


The numbers in this article were obtained on Python 3.11 / Linux x86_64. Sizes vary by platform and Python version because pip selects different wheels.

Top comments (0)