How to Inspect a Virtual Environment You Did Not Create
Inheriting a project often means inheriting a .venv directory with no pyproject.toml, no requirements.txt, and no record of why anything in it is installed. uv reads that environment directly, without a project file and without modifying it.
Prerequisites
- uv installed. See How to install uv.
- An existing virtual environment, referred to below as
.venv.
Point uv at the environment, not the directory
uv tree is the wrong command here. It resolves a project’s dependencies, writing a lockfileA file that records the exact version of every installed package, so everyone working on the project gets identical installs.
if none exists, and stops when there is no project at all:
$ uv tree
error: No `pyproject.toml` found in current directory or any parent directory
uv pip tree reads installed packages instead. The --python flag names the interpreter to inspect, so the environment never has to be activated. The path is resolved relative to the working directory, so run these from the directory holding .venv or pass an absolute path:
uv pip tree --python .venv/bin/pythonTop-level entries are packages nothing else requires, which is the closest thing to a list of what someone chose to install. Ignore pip and anything else venv seeded when it created the environment:
flask v3.1.3
├── blinker v1.9.0
├── click v8.4.2
├── itsdangerous v2.2.0
├── jinja2 v3.1.6
│ └── markupsafe v3.0.3
├── markupsafe v3.0.3
└── werkzeug v3.1.8
└── markupsafe v3.0.3
pip v26.0.1
requests v2.34.2
├── certifi v2026.7.22
├── charset-normalizer v3.5.1
├── idna v3.18
└── urllib3 v2.7.0
Trace a package back to whatever required it
--invert flips the tree so a package appears above the packages that depend on it. Adding --show-version-specifiers prints the range each dependent declared:
uv pip tree --python .venv/bin/python --invert --package markupsafe --show-version-specifiersmarkupsafe v3.0.3
├── flask v3.1.3 [requires: markupsafe >=2.1.1]
├── jinja2 v3.1.6 [requires: markupsafe >=2.0]
│ └── flask v3.1.3 [requires: jinja2 >=3.1.2]
└── werkzeug v3.1.8 [requires: markupsafe >=2.1.1]
└── flask v3.1.3 [requires: werkzeug >=3.1.0]
The specifiers decide whether a package can be upgraded. Every constraint on markupsafe here is a lower bound, and no dependent pins an exact version or caps the top of the range, so nothing blocks an upgrade. Lower bounds still block a downgrade: installing markupsafe==2.0.1 in this environment violates the >=2.1.1 that Flask and Werkzeug both declare.
Confirm nothing is missing or conflicting
An inherited environment may have been edited by hand. uv pip check verifies that every installed package has its requirements satisfied:
uv pip check --python .venv/bin/pythonChecked 13 packages in 0.28ms
All installed packages are compatible
The command exits 0 when the environment is consistent and 1 when it is not, so it works as a gate in a script or CI job. uv pip tree --strict reports the same problems as warnings but still exits 0, which makes it the wrong choice for automation.
Reach for pipdeptree for licenses and diagrams
uv pip tree covers the tree, the reverse lookup, and the specifiers. pipdeptree adds license reporting, a one-command environment summary, Mermaid or Graphviz export, and a --path mode that needs no working interpreter:
uvx pipdeptree --python .venv/bin/python --summaryRun it through uvx rather than installing it into the environment under inspection, which would add pipdeptree and its own dependencies to the tree being reported.
Read an environment whose interpreter is gone
A copied or abandoned .venv often points at a Python that no longer exists. Every uv command here fails on it, and the suggested remedy would overwrite the environment:
$ uv pip tree --python .venv/bin/python
error: No virtual environment or system Python installation found for path `.venv/bin/python`; run `uv venv` to create an environment
Do not run uv venv. It replaces the environment being investigated. pipdeptree reads the package directory itself, with no interpreter involved:
uvx pipdeptree --path .venv/lib/python3.13/site-packagesSubstitute the python3.x directory that exists in the venv. The tree comes back exactly as it would from a working interpreter, which is often enough to reconstruct a requirements file before rebuilding.
Warning
Always pass --python to pipdeptree. With no environment active it falls back to its own interpreter, prints a plausible tree for the wrong environment, and exits 0. When an environment is active it honors VIRTUAL_ENV and prints the interpreter it resolved, so the banner is worth reading before trusting the tree.