Knowing exactly which version of a Python package is installed in your environment is a fundamental skill for every developer. Whether you are debugging a compatibility issue, replicating a production environment, or writing a requirements.txt file for deployment, the ability to check the version of a Python package quickly and accurately saves hours of troubleshooting. This guide covers every reliable method—from command-line tools to programmatic inspection—ensuring you have the right technique for any situation And that's really what it comes down to..
Why Checking Package Versions Matters
Before diving into the commands, it helps to understand why this task is so frequent in a developer's workflow. Consider this: python’s ecosystem relies heavily on semantic versioning (SemVer). A minor update might introduce a new feature you need, while a major update could break your existing code due to deprecated APIs It's one of those things that adds up..
Easier said than done, but still worth knowing.
Common scenarios include:
- Dependency Conflicts: Two libraries require different versions of the same dependency.
- Reproducibility: Pinning exact versions in
requirements.Practically speaking, txtorpyproject. tomlensures your team and CI/CD pipeline run identical code. In real terms, * Security Auditing: Verifying you have patched versions for known vulnerabilities (CVEs). * Feature Availability: Confirming a specific function or class exists in your installed version before writing code that uses it.
Method 1: Using the Command Line (Terminal/Shell)
The fastest way to check a version without opening a Python interpreter is via the shell. This is ideal for quick checks during development or inside CI/CD scripts Less friction, more output..
Using pip show
The most informative command is pip show. It displays metadata including version, location, installer, and dependencies.
pip show requests
Output example:
Name: requests
Version: 2.31.0
Summary: Python HTTP for Humans.
Home-page: https://requests.readthedocs.io
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Location: /home/user/.local/lib/python3.11/site-packages
Requires: charset-normalizer, idna, urllib3, certifi
Required-by:
Pro Tip: If you only want the version number (useful for scripting), combine it with grep (Linux/macOS) or findstr (Windows):
# Linux / macOS
pip show requests | grep Version
# Windows (Command Prompt)
pip show requests | findstr Version
# Windows (PowerShell)
pip show requests | Select-String Version
Using pip list or pip freeze
If you need to check multiple packages or generate a snapshot of the entire environment:
pip list: Returns a formatted table of all installed packages. Great for human readability.pip freeze: Outputs inrequirements.txtformat (package==version). This is the standard for freezing dependencies.
pip list | grep pandas
# Output: pandas 2.1.4
pip freeze | grep numpy
# Output: numpy==1.26.2
Using python -m pip
Best practice dictates using python -m pip instead of calling pip directly. This guarantees you are using the pip associated with the specific Python interpreter you are targeting, avoiding the common "wrong environment" pitfall.
python -m pip show django
python3 -m pip list
Method 2: Programmatic Checks (Inside a Python Script)
Often, you need to check a version inside your application code—for example, to enable conditional logic based on library features or to log the environment state at runtime.
The Modern Standard: importlib.metadata (Python 3.8+)
Since Python 3.This is the recommended, zero-dependency way to inspect installed distributions. Here's the thing — metadata. 8, the standard library includes importlib.It is fast because it reads metadata files directly without importing the package code itself.
from importlib.metadata import version, PackageNotFoundError
def get_package_version(package_name: str) -> str:
try:
return version(package_name)
except PackageNotFoundError:
return "Not installed"
print(f"Requests version: {get_package_version('requests')}")
print(f"Non-existent version: {get_package_version('this-package-does-not-exist')}")
Why prefer this?
- No Import Side Effects: It does not execute the package's
__init__.py. This avoids triggering heavy imports, registration logic, or potential errors if the package is broken. - Standard Library: No external dependencies required for Python 3.8+.
- Handles Namespaces: Works correctly with namespace packages.
The Legacy Standard: pkg_resources (setuptools)
For projects stuck on Python 3.Practically speaking, 7 or older, pkg_resources (part of setuptools) was the go-to solution. It is still widely seen in older codebases.
import pkg_resources
try:
dist = pkg_resources.get_distribution("requests")
print(f"Version: {dist.version}")
except pkg_resources.
*Note: `pkg_resources` is significantly slower at startup because it scans all entry points and metadata on initialization. Avoid it in performance-sensitive CLI tools.*
### The "Quick and Dirty": `package.__version__`
Many packages expose a `__version__` attribute in their top-level `__init__.py`.
```python
import requests
import numpy
print(requests.__version__)
print(numpy.__version__)
Caveats:
- Not a Standard: PEP 396 suggests this convention, but it is not enforced. Some packages use
__version_info__,version, orVERSION. - Import Overhead: You must fully import the package. For heavy libraries like
torch,tensorflow, orpandas, this adds noticeable latency and memory usage just to read a string. - Editable Installs: In development mode (
pip install -e .), this attribute might be missing or incorrect if the build metadata hasn't been generated.
Method 3: Inspecting Specific Environments
Modern Python development almost always involves virtual environments (venv, conda, poetry, pipenv). Checking the version globally (system Python) often yields misleading results.
Conda Environments
If you use Anaconda or Miniconda, conda list is the primary tool. It shows packages installed via conda and pip (marked with pypi in the channel column) Simple as that..
conda list numpy
# Or for the exact env:
conda activate my_env
conda list
Poetry / Pipenv / PDM
These modern dependency managers lock versions in a lock file (poetry.lock, Pipfile.Now, lock, pdm. lock).
- Poetry:
poetry show requestsorpoetry show --tree(shows dependency graph). - Pipenv:
pipenv run pip show requestsorpipenv graph. - PDM:
pdm listorpdm show requests.
Docker / CI Containers
Inside a Dockerfile or CI pipeline (GitHub Actions, GitLab CI), you typically run the standard pip commands during the build step to verify the install layer:
RUN python -m pip install --no-cache-dir -r requirements.txt \
&& python -m pip list --format=freeze > /tmp/installed_versions.txt
Method 4: Checking Versions Before Installation
Sometimes you need to know the latest available version on PyPI without installing
Querying the latest release without installing the package
When you only need to know which version is currently published on PyPI, pulling the package into your environment just to read its __version__ attribute defeats the purpose. Fortunately, the metadata that PyPI publishes is publicly reachable and can be inspected with a few lightweight calls.
Most guides skip this. Don't Not complicated — just consistent..
Using the standard library (importlib.metadata)
Since Python 3.That's why 8, the importlib. Plus, metadata package (built‑in as of 3. 10) provides a clean API for reading distribution metadata. While it reports the version of an installed distribution, you can combine it with a direct HTTP request to the PyPI JSON endpoint to obtain the newest release Most people skip this — try not to..
import json
import urllib.request
def latest_version_on_pypi(name: str) -> str:
url = f"https://pypi.org/pypi/{name}/json"
with urllib.Here's the thing — request. urlopen(url) as resp:
data = json.
print(latest_version_on_pypi("requests")) # → 2.31.0 (as of writing)
The json payload contains an info object where the version field holds the most recent release. This approach avoids any import‑time side effects and works even for packages that are not yet installed in the current environment Worth keeping that in mind..
The pip index versions command
Starting with pip 21, the pip index versions sub‑command can enumerate all available releases for a given project and highlight the newest one Surprisingly effective..
pip index versions requests
# Output (truncated):
# requests (2.31.0) (2.30.1) (2.29.23) …
# Latest: 2.31.0
The command prints a space‑separated list of versions; the trailing “Latest:” marker makes it trivial to extract the current maximum. Because it runs entirely in the subprocess, there is no need to import the target package at all.
Quick one‑liner with requests (if you already depend on it)
import requests
def latest_version_on_pypi(name: str) -> str:
resp = requests.get(f"https://pypi.Now, org/pypi/{name}/json", timeout=10)
resp. raise_for_status()
return resp.
print(latest_version_on_pypi("requests"))
Because requests handles redirects, encoding, and error states more gracefully than urllib, this is often the preferred snippet for scripts that already carry the dependency.
Method 5: Detecting Outdated Packages in an Environment
Knowing the installed version is only half the battle; the real operational question is usually “Is there a newer version available?”
pip list --outdated (Built-in)
The simplest way to audit an entire environment is the native flag:
pip list --outdated --format=columns
# Package Version Latest Type
# ---------- ------- ------ ----
# requests 2.28.1 2.31.0 wheel
# urllib3 1.26.15 2.0.3 wheel
--format=freezeor--format=jsonmakes the output machine-readable for CI gates.--localrestricts checks to packages installed in the current environment (ignoring system/global site-packages if running in a virtualenv).
pip-review (Interactive & Automated Updates)
For a more interactive workflow—especially useful during local development—pip-review wraps the outdated check with update prompts:
pip install pip-review
pip-review --auto # Upgrade everything automatically
pip-review --interactive # Prompt per package
pur (Parallel Update Reviewer)
If you manage large monorepos or many virtual environments, pur (Python Update Reviewer) parallelizes the PyPI queries, making it significantly faster than sequential pip calls:
pipx install pur # or pip install pur
pur # Shows outdated packages with color-coded version diffs
pur -a # Auto-updates requirements.txt / pyproject.toml pins
Method 6: Programmatic Version Comparison & Constraints
When writing automation (e.g., a custom dependency bot or a pre-commit hook), you rarely want raw strings; you need PEP 440 compliant comparison logic.
The packaging Library (Standard for Tooling)
packaging.version is the reference implementation used by pip, setuptools, and pdm. It correctly handles pre-releases, post-releases, dev releases, and local version identifiers.
from packaging.version import parse as parse_version
from packaging.specifiers import SpecifierSet
installed = parse_version("2.28.1")
latest = parse_version("2.31.0")
print(installed < latest) # True
# Check if a version satisfies a requirement specifier
spec = SpecifierSet(">=2.28.0,<3.0.0")
print(spec.contains("2.31.0")) # True
print(spec.contains("3.0.0")) # False
print(spec.contains("2.27.0")) # False
Checking Compatibility Before Upgrading
A common pattern in CI is to verify that the latest version on PyPI still satisfies your project's declared constraints (e.Practically speaking, g. So , in pyproject. Even so, toml or requirements. txt) before allowing a dependabot PR to merge.
import tomllib
from packaging.specifiers import SpecifierSet
from packaging.version import parse as parse_version
import requests
def get_latest_version(pkg_name: str) -> str:
return requests.get(f"https://pypi.org/pypi/{pkg_name}/json", timeout=10).json()["info"]["version"]
def check_constraints(pyproject_path: str = "pyproject.toml"):
with open(pyproject_path, "rb") as f:
data = tomllib.load(f)
# Handles both [project] dependencies and [tool.In real terms, requirements. On the flip side, dependencies] etc. deps = data.Requirement`
from packaging.name)
if not req.On the flip side, requirements import Requirement
req = Requirement(dep)
latest = get_latest_version(req. poetry.28,<3" -> name="requests", spec=">=2.get("project", {}).28,<3"
# In production, use `packaging.get("dependencies", [])
for dep in deps:
# Naive split: "requests>=2.specifier.