pipdeptree: Python Dependency Tree Inspector
pipdeptree is a command-line tool that renders the packages installed in a Python environment as a dependency tree. Where pip list prints a flat alphabetical list, pipdeptree shows the parent-child structure: which package required each transitive dependency, and the version specifier it declared.
Note
uv ships two tree commands, and uv pip tree is the closer comparison. uv tree resolves a project or a PEP 723 script; uv pip tree reads an installed environment and accepts --python, so it already covers the tree, reverse lookup, and version specifiers on any interpreter with no extra install. Reach for pipdeptree for what uv omits: license reporting, a one-command environment summary, Mermaid and Graphviz export, and reading a package directory with no working interpreter.
A Rust extension built with PyO3 and meson-python does the discovery, parsing, and rendering, behind a thin Python shim that keeps the CLI and public API. The project’s published benchmarks put it 2.2x to 15.2x ahead of the previous pure-Python implementation, with the widest margins on large environments, and 16.9% to 24.7% lower in peak memory on the large environment.
Key Features
Environment Inspection
The --python flag points pipdeptree at any interpreter and --path at a package directory directly, so one installation can inspect many environments, including those whose interpreter no longer runs:
# Inspect a virtual environment from outside it
pipdeptree --python .venv/bin/python
# Inspect an explicit search path
pipdeptree --path /usr/lib/python3/dist-packages
# Exclude packages inherited from the system interpreter
pipdeptree --local-onlyReverse Lookup
The --reverse flag inverts the tree so each package is listed with the packages that require it. Combined with --packages, it shows what pulled a given dependency into the environment:
$ pipdeptree --python .venv/bin/python --reverse --packages markupsafe
MarkupSafe==3.0.3
├── Flask==3.1.3 [requires: MarkupSafe>=2.1.1]
├── Jinja2==3.1.6 [requires: MarkupSafe>=2.0]
│ └── Flask==3.1.3 [requires: Jinja2>=3.1.2]
└── Werkzeug==3.1.8 [requires: MarkupSafe>=2.1.1]
└── Flask==3.1.3 [requires: Werkzeug>=3.1.0]
Each edge carries the version specifier the dependent declared, separating a package one parent pinned tightly from one another parent accepts at any version.
Output Formats
The -o flag selects the renderer. The default is rich on color terminals and text elsewhere:
| Format | Purpose |
|---|---|
text |
Plain aligned tree |
rich |
Colored tree for interactive terminals |
freeze |
pip freeze-compatible requirement lines |
json |
Flat array of package records |
json-tree |
Nested records preserving the tree structure |
mermaid |
Mermaid flowchart TD source |
graphviz-FMT |
Graphviz output, where FMT is dot, svg, png, pdf, or another format the installed dot binary supports |
The dot source is generated natively; binary formats shell out to an installed dot.
Environment Health Summary
The --summary flag replaces the tree with a report over the whole environment:
$ pipdeptree --python .venv/bin/python --summary
total packages: 7
direct dependencies: 1
transitive dependencies: 6
max depth: 3
cyclic dependencies: 0
missing dependencies: 0
conflicting dependencies: 0 (0 edges)
licenses: (BSD License): 2, (BSD-3-Clause): 4, (MIT License): 1
unknown licenses: 0
copyleft licenses: no
min requires-python: 3.10
total size: 2.1 MB
Metadata and Computed Fields
The --metadata flag appends core metadata fields to each node, and --computed appends fields pipdeptree derives by inspecting installed files:
$ pipdeptree --python .venv/bin/python --depth 1 --computed size
Flask==3.1.3 (337.4 KB)
├── blinker [required: >=1.9.0, installed: 1.9.0] (24.1 KB)
├── click [required: >=8.1.3, installed: 8.4.2] (418.6 KB)
├── itsdangerous [required: >=2.2.0, installed: 2.2.0] (45.9 KB)
├── Jinja2 [required: >=3.1.2, installed: 3.1.6] (485.4 KB)
├── MarkupSafe [required: >=2.1.1, installed: 3.0.3] (72.1 KB)
└── Werkzeug [required: >=3.1.0, installed: 3.1.8] (748.5 KB)
Computed fields are size, size-raw, unique-deps-count, unique-deps-names, and unique-deps-size. The unique-deps-* fields count dependencies reachable only through the package in question, which identifies what an environment would shed if that package were removed.
Offline Lock and Index Rendering
Two subcommands render dependency data without inspecting an installed environment. from-lock reads a PEP 751 pylock.toml with no network access, and from-index resolves a requirement against a package index without installing anything:
$ pipdeptree from-index "flask"
flask==3.1.3
├── blinker [candidate: 1.9.0]
├── click [candidate: 8.4.2]
├── itsdangerous [candidate: 2.2.0]
├── jinja2 [candidate: 3.1.6]
│ └── markupsafe [candidate: 3.0.3]
├── markupsafe [candidate: 3.0.3]
└── werkzeug [candidate: 3.1.8]
└── markupsafe [candidate: 3.0.3]
from-index also accepts --requirements and a pyproject.toml, and is backed by nab, the experimental resolver pipdeptree declares through its nab-index and nab-python dependencies. from-lock parses the lockfile natively and works without them, but draws a tree only when the lockfile carries PEP 751’s optional [[packages.dependencies]] tables. Neither uv export --format pylock.toml nor pip lock writes that field, so locks from those tools render as a flat list.
Installation
Running pipdeptree from outside the target environment keeps the inspected tree clean. Installing it into a virtual environment holding Flask grows that environment from 7 packages to 20, and pipdeptree plus its nab-index and nab-python dependencies then appear in the tree it reports.
# Run without installing (preferred)
uvx pipdeptree --python .venv/bin/python
# Install once as a user-level tool
uv tool install pipdeptree
# Install into the environment under inspection
pip install pipdeptreeWarning
An externally installed pipdeptree defaults to the interpreter running it, not to a virtual environment in the working directory. Running uvx pipdeptree next to an unactivated .venv reports pipdeptree’s own environment and exits successfully, so the wrong answer arrives without an error. Pass --python .venv/bin/python, or activate the environment first, in which case pipdeptree prints the interpreter it resolved.
A cp310-abi3 wheel covers CPython 3.10 and later, with separate free-threaded builds for 3.14t and 3.15t, across Linux x86-64 and aarch64 (manylinux and musllinux), macOS x86-64 and arm64, and Windows x86-64. PyPy 3.11 wheels cover the same set except musllinux.
Continuous Integration Use
Setting --warn to fail turns a broken environment into a nonzero exit, which makes pipdeptree a CI health gate:
pipdeptree --python .venv/bin/python --warn failA consistent environment exits 0. Remove a package others still require and the tree marks the gap installed: ? and exits 1.
Comparison with the uv Tree Commands
uv tree resolves a project or a PEP 723 script. uv pip tree reports what is installed in an environment, which is the same job pipdeptree does.
| Capability | uv tree |
uv pip tree |
pipdeptree |
|---|---|---|---|
| Source of truth | Resolved project or script | Installed metadata | Installed metadata |
| Works with no project file | Only via --script |
Yes | Yes |
| Inspects an arbitrary interpreter | No | Yes, --python |
Yes, --python |
| Reads a package directory with no interpreter | No | No | Yes, --path |
| Reverse lookup | Yes, --invert |
Yes, --invert |
Yes, --reverse |
| Shows declared version specifiers | No | Yes, --show-version-specifiers |
Yes, by default |
| Nonzero exit on a broken environment | No | No, use uv pip check |
Yes, --warn fail |
| License reporting | No | No | Yes |
| Environment summary in one command | No | No | Yes, --summary |
| Mermaid and Graphviz export | No | No | Yes |
| JSON output | Yes, --format json (preview) |
No | Yes |
Reads pylock.toml |
No | No | Yes |
Pros
- Reports licenses and a full environment summary in one command
- Mermaid and Graphviz export for documentation and diagrams
--pathreads a package directory whose interpreter is missing or broken--warn failgives a nonzero exit for CI health checks without a second command
Cons
- A separate install alongside uv, whose
uv pip treealready covers trees, reverse lookup, and specifiers - Depends on
nab-indexandnab-python, both pre-1.0 and documented on PyPI as under rapid experimentation from-lockrenders flat for locks written by uv or pip, which omit PEP 751’s optional dependency tables- Installing it into the environment under inspection changes that environment
Learn More
Handbook guides
- How to inspect a virtual environment you did not create
- How to debug uv dependency resolution failures
- How to create a pylock.toml lockfile
- What is a lockfile?
- What is a virtual environment?