You create a virtual environment called py_venv310, activate it, and get this:
$ which python
/usr/bin/python
$ python --version
Python 3.8.10
A folder named for 3.10. An interpreter that is 3.8. And a path pointing outside the venv completely.
Nothing you pip install goes where you expect, and pytest runs against a different set of
packages than you think. Here is how to find out what is wrong, fix it on either platform, and stop
it happening again.
Versions and package availability checked on 2026-10-04.
1. Check it in one command
Do not guess. Run this:
python -c "import sys; print(sys.prefix); print(sys.base_prefix)"
Two different paths means you are inside a venv. Two identical paths means you are not — whatever your prompt says.
For the full picture:
echo "VIRTUAL_ENV: ${VIRTUAL_ENV:-<unset>}"
command -v python
python --version
PowerShell:
$env:VIRTUAL_ENV
(Get-Command python).Source
python --version
Read it like this:
| What you see | What it means |
|---|---|
VIRTUAL_ENV is empty |
Not activated. Fix that first. |
VIRTUAL_ENV set, but python is /usr/bin/python |
Activated, but something else is ahead on PATH |
sys.prefix same as sys.base_prefix |
You are not in a venv at all |
| Both paths differ, but the version is wrong | The venv is fine. It was built on the wrong Python. |
That last row is the interesting one, and it is the example above.
2. Why it happens
It was built on the wrong Python. This is the common one, and people misread it as “activation is broken”.
A venv is not a Python version. It is a small folder that points at the interpreter that created it — permanently. When you run:
python -m venv py_venv310
you get whatever python meant at that moment. On Ubuntu 20.04 that is 3.8. The name py_venv310
is just a folder name. It does not ask for a version, and nothing checks it.
You can see this for yourself:
cat py_venv310/pyvenv.cfg
home = /usr/bin
version = 3.8.10
executable = /usr/bin/python3.8
There it is, in writing. A venv’s Python version cannot be changed afterwards. Delete it and make a new one — there is no repair.
The shell remembered the old path. If you deleted and recreated the venv and it still looks wrong, bash may be caching the old location:
hash -r
3. Fix it on Ubuntu
deactivate 2>/dev/null || true
cd ~/projects/my-tests # your project folder, NOT inside the venv
rm -rf py_venv310
python3.12 -m venv .venv # name the version — this is the whole point
source .venv/bin/activate
python --version # should say 3.12.x
Watch step 2. If you delete a venv while your shell is sitting inside it, you end up in a folder that no longer exists, which causes its own strange errors.
If you get “ensurepip is not available”, Ubuntu ships venv separately:
sudo apt install python3.12-venv
The advice that does not work on Ubuntu 20.04
Nearly every guide says this:
sudo apt install python3.10 python3.10-venv # fails on 20.04
Ubuntu 20.04 has no python3.10 package. I checked the Ubuntu package archive for focal on
2026-10-04: searching for python3.10 returns “Sorry, your search gave no results”. The command
fails with Unable to locate package, and people assume their apt sources are broken. They are not.
The old answer was the deadsnakes PPA. That no longer covers 20.04 either — as of 2026-10-04 it publishes for jammy (22.04), noble (24.04) and resolute (26.04) only. Focal was dropped.
What each Ubuntu release ships by default:
| Ubuntu | Codename | Default Python |
|---|---|---|
| 20.04 | Focal Fossa | 3.8 |
| 22.04 | Jammy Jellyfish | 3.10 |
| 24.04 | Noble Numbat | 3.12 |
What works on 20.04 today
Use uv. It downloads a ready-made interpreter. No apt, no PPA, no compiler, no root:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv python install 3.12
uv venv --python 3.12 .venv
source .venv/bin/activate
uv pip install -r requirements.txt
The useful part: uv venv --python 3.12 fails loudly if it cannot get 3.12, instead of quietly
handing you 3.8. That one behaviour removes this whole class of bug. (uv was 0.12.23 when I wrote
this.)
Or use pyenv, which builds from source — slower, and you need the build dependencies first:
curl -fsSL https://pyenv.run | bash
pyenv install 3.12
pyenv local 3.12
python -m venv .venv
Or upgrade the machine. Ubuntu 20.04 is old, and its default Python 3.8 stopped getting fixes in October 2024. If you can move to 24.04, that is the fix that stops costing you time.
Either tool puts a modern Python on an old box without touching the system one. That matters:
Ubuntu’s own tools depend on /usr/bin/python3. Never apt remove it, and never repoint it.
4. Fix it on Windows
Same idea, different commands:
deactivate
Remove-Item -Recurse -Force .venv
py -V:3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
python --version
Note the activate path is .\.venv\Scripts\Activate.ps1 — one dot, then a backslash. Typing
..venv\... makes PowerShell look in the parent folder, and the error does not explain that.
If activation is blocked with “running scripts is disabled on this system”, that is the PowerShell
execution policy. I covered that one, and the Microsoft Store python alias, in
Setting Up Python on Windows 11.
Installing versions properly
Hardcoding C:\...\Python312\python.exe works but gets messy with several versions. Python now
ships an Install Manager for Windows:
winget install 9NQ7512CXL7T -e # or install "Python" from the Microsoft Store
py install 3.12 # download and register 3.12
py list # what you have
py -V:3.12 -m venv .venv # venv on a specific version
py -V:3.12 -m venv .venv is the payoff: an explicit version, no long paths, no PATH edits. In
scripts, use pymanager instead of py — the docs suggest it, because an older launcher can take
over the py command.
Do not follow the PATH tip in the install output. After py install you are told to add
%LocalAppData%\Python\bin. Plenty of write-ups then tell you to also add the per-version folder
like pythoncore-3.12-64. Do not.
Per the official Windows docs, adding bin is optional — it only makes aliases like
python3.14.exe work, and py works fine without it. The per-version folder is that version’s
scripts folder. Putting it on PATH pins bare python to one fixed version, which is the exact
bug this post opened with, just created on purpose.
If py is not resolving, let the manager repair itself rather than editing PATH by hand:
py install --configure -y
5. The pytest version of this bug
pytest adds two twists.
pytest on PATH may belong to a different venv. The pytest command is a small script with
the creating venv’s interpreter written into it. If an old one is earlier on PATH, your tests run
under that Python with that set of packages — while python --version happily reports the right
thing. Check both:
command -v pytest
python -c "import sys; print(sys.executable)"
python -m pytest is not the same as pytest. The pytest docs put it like this: calling via
python “will also add the current directory to sys.path.”
That cuts both ways:
- It guarantees you are using the Python you just checked, so it sidesteps the problem above.
- It can hide a packaging mistake. A test that only imports your package because the current folder
happens to be on
sys.pathwill fail in CI, where the package is installed properly.
So: use python -m pytest while sorting out an environment, and pip install -e . with plain
pytest as the real setup.
Install with python -m pip, not bare pip, so it goes to the Python you just verified:
python -m pip install -r requirements.txt
python -m pip -V # check the path is inside your venv
6. Which version to pin
Support status on 2026-10-04:
| Version | Latest | Supported until |
|---|---|---|
| 3.14 | 3.14.8 | Oct 2030 |
| 3.13 | 3.13.16 | Oct 2029 |
| 3.12 | 3.12.15 | Oct 2028 |
| 3.11 | 3.11.17 | Oct 2027 |
| 3.10 | 3.10.22 | ended 1 Oct 2026 |
| 3.9 | 3.9.25 | ended Oct 2025 |
| 3.8 | 3.8.20 | ended Oct 2024 |
Worth knowing before you spend an afternoon getting 3.10 onto an old machine: 3.10 is already end of life — support ran out on 1 October 2026. If you are picking a version now, rather than matching one a project already uses, pick 3.12 or newer.
Write the decision where tools can read it, not in a folder name:
# pyproject.toml
[project]
requires-python = ">=3.12"
7. The habit that prevents all of it
Never create a venv with a bare python -m venv. Name the version every time:
python3.12 -m venv .venv # Linux and macOS
py -V:3.12 -m venv .venv # Windows
uv venv --python 3.12 .venv # anywhere — downloads it if missing
The difference is what happens when the version is missing. Bare python -m venv quietly gives you
whatever it found. All three above fail straight away and tell you. That is a two-minute problem
instead of a two-hour one.
Two more habits:
- Call it
.venv, notpy_venv310. A version in the folder name is a comment nothing enforces, and it will eventually be wrong..venvis what editors and most tools look for automatically. - Read
pyvenv.cfgwhen anything looks odd. Three lines, and it cannot lie.
Quick reference
| Symptom | Cause | Fix |
|---|---|---|
which python is /usr/bin/python with venv “active” |
Not actually activated | source .venv/bin/activate |
| Right venv, wrong version | Built on the wrong Python | Check pyvenv.cfg, delete, recreate with python3.12 -m venv |
Unable to locate package python3.10 on 20.04 |
Not in the focal archive | Use uv, or pyenv, or upgrade |
ensurepip is not available |
Ubuntu ships venv separately | sudo apt install python3.X-venv |
| Recreated the venv, still wrong | Shell cached the old path | hash -r |
Activate.ps1 cannot be loaded |
PowerShell execution policy | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser |
pip install works, import still fails |
pip belongs to another Python | Use python -m pip; check python -m pip -V |
Passes locally, ModuleNotFoundError in CI |
python -m pytest put cwd on sys.path |
pip install -e ., run plain pytest |
python stuck on one version |
Per-version folder added to PATH |
Remove it; use py -V:3.12 |
Sources
- Python version and EOL schedule
- Python Install Manager for Windows
- venv documentation
- pytest invocation
- uv
Did this match what you hit, or was your venv broken in some new way? Tell me in the comments below — and if you are on Ubuntu 20.04, I would like to know which fix you went with.
Questions or comments?
Ask a question, share your experience, or add a correction below.