# 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](https://pydevtools.com/handbook/reference/uv.md) 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](https://pyo3.rs) and [meson-python](https://mesonbuild.com/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:

```bash
# 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-only
```

### Reverse 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:

```console
$ 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:

```console
$ 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](https://pydevtools.com/handbook/explanation/what-is-core-metadata.md) fields to each node, and `--computed` appends fields pipdeptree derives by inspecting installed files:

```console
$ 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](https://pydevtools.com/handbook/explanation/what-is-pep-751.md) `pylock.toml` with no network access, and `from-index` resolves a requirement against a package index without installing anything:

```console
$ 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](https://github.com/notatallshaw/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.

```bash
# 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 pipdeptree
```

> [!WARNING]
> 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:

```bash
pipdeptree --python .venv/bin/python --warn fail
```

A 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
- `--path` reads a package directory whose interpreter is missing or broken
- `--warn fail` gives a nonzero exit for CI health checks without a second command

## Cons

- A separate install alongside uv, whose `uv pip tree` already covers trees, reverse lookup, and specifiers
- Depends on `nab-index` and `nab-python`, both pre-1.0 and documented on PyPI as under rapid experimentation
- `from-lock` renders 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](https://pydevtools.com/handbook/how-to/how-to-inspect-an-existing-virtual-environment.md)
- [How to debug uv dependency resolution failures](https://pydevtools.com/handbook/how-to/how-to-debug-uv-dependency-resolution-failures.md)
- [How to create a pylock.toml lockfile](https://pydevtools.com/handbook/how-to/how-to-create-a-pylock-toml-lockfile.md)
- [What is a lockfile?](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md)
- [What is a virtual environment?](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md)

### Official documentation

- [pipdeptree documentation](https://pipdeptree.readthedocs.io/en/latest/)
- [pipdeptree on GitHub](https://github.com/tox-dev/pipdeptree)
- [pipdeptree on PyPI](https://pypi.org/project/pipdeptree/)
- [Rust engine pull request](https://github.com/tox-dev/pipdeptree/pull/618)
