Running Python 2 today: interpreter, pip and virtualenv

Get a working 2.7 environment, install packages with the right pip, and understand exactly what you are accepting by keeping it alive.

Getting a 2.7 interpreter

Python 2.7 reached end of life in January 2020. It receives no security fixes, no build fixes for new compilers, and no support from most libraries. You run it because something you cannot change requires it, and that is a decision with consequences.

python2 --version          # 2.7.18 is the final release
python2.7 -c "import sys; print(sys.version_info)"

# containers are the cleanest way to get 2.7 today
docker run --rm -it -v "$PWD":/app -w /app python:2.7.18-slim python --version

# the last pip that supports 2.7
python2 -m pip --version
ConcernReality in 2.7
Security patchesNone since January 2020; CVEs stay unfixed
TLSLinked against an old OpenSSL in most distro builds, so modern ciphers may be unavailable
Build toolingCompiling C extensions on a current toolchain often fails
Package indexMany projects dropped 2.7; pip install may resolve to an old release or nothing
CertificatesBundled CA lists and ssl defaults are old enough to break HTTPS calls

Isolating a legacy project

python2 -m virtualenv venv2        # note: virtualenv, not the venv module
source venv2/bin/activate

pip install --upgrade "pip<21" "setuptools<45" "wheel<0.38"
pip install -r requirements-legacy.txt

python -c "import sys; print(sys.prefix)"
  • Python 2 has no venv module in the standard library, so you need the third-party virtualenv package.
  • Cap pip below 21 and setuptools below 45: newer versions dropped 2.7 support and fail in confusing ways.
  • Freeze the environment with pip freeze > requirements-legacy.txt so you can rebuild it, because PyPI keeps changing underneath you.
⚠️
Never let a Python 2 interpreter handle untrusted input or make outbound TLS connections to a modern service. An unpatched interpreter plus an old OpenSSL build is a liability, and the failure mode is silent: the code runs happily and the transport is not what you assumed.

Installation problems you will hit

# a resolution failure usually means "no 2.7-compatible release exists"
pip install requests
# ERROR: Could not find a version that satisfies the requirement requests

# pin the last release that supported 2.7
pip install "requests==2.27.1"

# build a wheel on a newer machine only if the ABI matches
pip wheel --no-deps -w wheels/ "cffi==1.15.1"
  • Read the error precisely: it usually says no matching distribution rather than a compile failure.
  • Wheels for 2.7 exist for many old versions; the package metadata has simply been updated to exclude them.
  • Prefer copying a working environment (a container image or a saved wheel directory) over re-resolving packages from the index.

FAQ

Can I still install Python 2.7 from python.org?
The installers are still published, including 2.7.18, but they are unmaintained. In practice a container built from the official 2.7 image is more reproducible than installing on a modern host.
Why does pip refuse to install a package that definitely supports 2.7?
The project may have added a newer release that declares python_requires>=3 while older 2.7-compatible releases exist. Pin the version explicitly, or install from a saved wheel.

The print statement and integer division Testing legacy Python 2 code before you change it

Last refreshed 2026-09-18.