Table of Contents
Open Table of Contents
- Why Python Version Management Matters
- Understanding the Two Layers
- Linux Setup
- macOS Setup
- Windows Setup
- Installing Python Versions with pyenv
- Switching Python Versions
- Switching Python Versions on an Existing Project
- Creating Virtual Environments with venv
- Combining pyenv and venv: Full Workflow Example
- What’s New in Python 3.15
- Useful pyenv Commands Reference
- Useful venv Commands Reference
- Common Pitfalls
- Summary
Why Python Version Management Matters
Modern Python development rarely happens in a vacuum. You might be maintaining a legacy service pinned to Python 3.9 while simultaneously prototyping with the brand-new Python 3.15 feature set. Without proper tooling, juggling these versions turns into a fragile mess of manual symlinks, conflicting system packages, and “it works on my machine” conversations.
Two tools handle this cleanly:
- pyenv — installs and switches between any Python version at the global or per-project level
- venv — creates isolated Python environments per project so packages never bleed across projects
Together they give you a reproducible, portable local setup on Linux, macOS, and Windows.
Understanding the Two Layers
Before diving into installation, it helps to separate what each tool does:
| Tool | Manages | Scope |
|---|---|---|
pyenv | Python interpreter versions | System-wide or per directory |
venv | Package dependencies | Per project |
pyenv answers: “Which Python binary am I using?”
venv answers: “Which packages are installed for this project?”
They complement each other. pyenv selects the interpreter; venv isolates the packages on top of that interpreter.
Linux Setup
1. Install pyenv
The official installer script handles all dependencies:
curl https://pyenv.run | bash
After the script finishes, add the following to your shell configuration file (~/.bashrc, ~/.zshrc, or ~/.profile):
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
Reload your shell:
exec "$SHELL"
2. Install Build Dependencies
pyenv compiles Python from source. Install the required build tools first:
Debian / Ubuntu:
sudo apt update && sudo apt install -y \
build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev \
libncursesw5-dev xz-utils tk-dev \
libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
Fedora / RHEL / CentOS:
sudo dnf install -y \
gcc make openssl-devel bzip2-devel \
libffi-devel readline-devel sqlite-devel \
zlib-devel xz-devel tk-devel
Arch Linux:
sudo pacman -S --needed base-devel openssl zlib xz tk
macOS Setup
1. Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
2. Install pyenv via Homebrew
brew update
brew install pyenv
Add the shell integration to your profile (~/.zshrc for Zsh, which is the default since macOS Catalina):
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
Restart your terminal or run:
source ~/.zshrc
Note for Apple Silicon (M1/M2/M3/M4)
Homebrew installs natively for ARM. pyenv compiles Python for ARM by default, which is correct. If you need an x86 Python for legacy compatibility, use Rosetta 2 and an x86 Homebrew installation — but this is rarely needed for modern Python versions.
Windows Setup
pyenv does not run natively on Windows. The community project pyenv-win provides equivalent functionality.
1. Install pyenv-win via PowerShell
Open PowerShell as a regular user (not Administrator) and run:
Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"
& "./install-pyenv-win.ps1"
If you hit an execution policy error:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Then re-run the install script.
2. Verify Environment Variables
After installation, confirm these variables exist (the installer sets them automatically):
[System.Environment]::GetEnvironmentVariable("PYENV", "User")
[System.Environment]::GetEnvironmentVariable("PYENV_ROOT", "User")
[System.Environment]::GetEnvironmentVariable("PYENV_HOME", "User")
And confirm %PYENV%\bin and %PYENV%\shims are in your PATH:
echo $env:PATH
3. Restart Your Shell
Close and reopen PowerShell (or Windows Terminal) for the PATH changes to take effect.
pyenv --version
Installing Python Versions with pyenv
Once pyenv is installed on any platform, the workflow is identical.
List Available Python Versions
pyenv install --list
The list is long. Filter for a specific release line:
pyenv install --list | grep "3.15"
Install Python 3.15
pyenv install 3.15.0
pyenv downloads the source, compiles it, and places it in ~/.pyenv/versions/3.15.0/. This takes 1–3 minutes depending on your machine.
Install Additional Versions
You can install as many versions as disk space allows:
pyenv install 3.11.12
pyenv install 3.12.10
pyenv install 3.13.3
pyenv install 3.15.0
View Installed Versions
pyenv versions
Output example:
system
3.11.12
3.12.10
3.13.3
* 3.15.0 (set by /home/user/.pyenv/version)
The asterisk marks the currently active version.
Switching Python Versions
Globally (System Default)
Sets the default Python for your entire user session:
pyenv global 3.15.0
python --version
# Python 3.15.0
Per Directory (Project-Scoped)
Creates a .python-version file in the current directory. pyenv automatically activates this version whenever you enter the directory:
cd ~/projects/my-service
pyenv local 3.11.12
python --version
# Python 3.11.12
Check the directory and its .python-version file:
cat .python-version
# 3.11.12
Temporarily (Shell Session Only)
Override for the current terminal session without touching any files:
pyenv shell 3.13.3
python --version
# Python 3.13.3
Switching Python Versions on an Existing Project
This is the most common point of confusion. A virtual environment bakes the Python interpreter path into it at creation time — you cannot change the Python version of an existing venv in place. When you need a different Python version on a project that already has a .venv, follow these steps:
Step-by-step: changing the Python version for an existing project
# 1. Activate the current environment (if not already active)
source .venv/bin/activate # Linux/macOS
.venv\Scripts\Activate.ps1 # Windows PowerShell
# 2. Export all currently installed packages before destroying anything
pip freeze > requirements.txt
# 3. Deactivate the environment
deactivate
# 4. Delete the old virtual environment
rm -rf .venv # Linux/macOS
Remove-Item -Recurse -Force .venv # Windows PowerShell
# 5. Switch to the target Python version with pyenv
pyenv local 3.13.3 # replace with your target version
# 6. Verify the new version is active
python --version
# Python 3.13.3
# 7. Create a fresh virtual environment with the new interpreter
python -m venv .venv
# 8. Activate the new environment
source .venv/bin/activate # Linux/macOS
.venv\Scripts\Activate.ps1 # Windows PowerShell
# 9. Restore your packages
pip install --upgrade pip
pip install -r requirements.txt
# 10. Confirm everything is working
python --version
pip list
Why you can’t skip the delete step
When python -m venv .venv runs, it writes the absolute path of the current Python binary into .venv/bin/python (or .venv/Scripts/python.exe on Windows). That symlink or copy does not update when you run pyenv local later. The only way to change the interpreter is to delete the old .venv and create a new one.
Checking which Python a venv was built with
# Linux/macOS
.venv/bin/python --version
# Windows
.venv\Scripts\python.exe --version
Run this before switching if you need to confirm what version was originally used.
Creating Virtual Environments with venv
venv is part of Python’s standard library since Python 3.3 — no additional installation needed.
Create a Virtual Environment
cd ~/projects/my-project
python -m venv .venv
This creates a .venv/ directory containing a copy of the Python interpreter and an isolated pip.
Convention: Name the virtual environment
.venv— most editors (VS Code, PyCharm) auto-detect this name and activate it automatically.
Activate the Environment
Linux / macOS:
source .venv/bin/activate
Windows (Command Prompt):
.venv\Scripts\activate.bat
Windows (PowerShell):
.venv\Scripts\Activate.ps1
After activation, your prompt shows the environment name:
(.venv) $
Verify You Are Using the Right Python
which python # Linux/macOS
where python # Windows
python --version
Install Packages
pip install requests fastapi uvicorn
Packages install only inside .venv, never touching the system or other projects.
Freeze Dependencies
pip freeze > requirements.txt
Recreate the Environment Elsewhere
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Deactivate
deactivate
Packages persist between activate/deactivate cycles
This is important: deactivating a venv does not delete your installed packages. The packages live on disk inside .venv/lib/pythonX.Y/site-packages/ and are still there the next time you activate. You do not need to reinstall anything just because you deactivated.
# First session
source .venv/bin/activate
pip install requests fastapi
deactivate
# Later — same machine, same .venv directory
source .venv/bin/activate
pip list # requests and fastapi are still there
The only time packages disappear is when you delete and recreate the .venv directory (for example, when switching Python versions as described above). In that case, use requirements.txt to restore them in one command:
pip install -r requirements.txt
Combining pyenv and venv: Full Workflow Example
Here is the complete workflow for starting a new Python 3.15 project:
# 1. Confirm Python 3.15 is installed
pyenv versions
# 2. Create project directory
mkdir ~/projects/python315-demo
cd ~/projects/python315-demo
# 3. Pin Python 3.15 for this directory
pyenv local 3.15.0
# 4. Verify
python --version
# Python 3.15.0
# 5. Create a virtual environment using Python 3.15
python -m venv .venv
# 6. Activate
source .venv/bin/activate # Linux/macOS
# or
.venv\Scripts\Activate.ps1 # Windows PowerShell
# 7. Install dependencies
pip install --upgrade pip
pip install httpx pydantic
# 8. Freeze
pip freeze > requirements.txt
# 9. Work on your project...
# 10. Deactivate when done
deactivate
What’s New in Python 3.15
Python 3.15 (released October 2025) introduces several notable changes worth knowing when setting up a new environment:
utf-8mode is now the default —sys.stdinandsys.stdoutdefault to UTF-8 encoding on all platforms, eliminating a long-standing source of cross-platform encoding bugs.locals()semantics are now defined — mutation oflocals()within optimized scopes is no longer undefined behavior; the behavior is now fully specified.- Improved error messages — the interpreter continues the trend of more precise, actionable
SyntaxErrorandTypeErrormessages introduced in 3.10+. asyncioimprovements — continued refinements to the task and event loop APIs.- Deprecated C API removals — if you maintain C extensions, check the migration guide for removed items.
Always consult the official Python 3.15 release notes before upgrading production workloads.
Useful pyenv Commands Reference
| Command | Description |
|---|---|
pyenv install --list | List all installable versions |
pyenv install 3.15.0 | Install a specific version |
pyenv uninstall 3.11.12 | Remove an installed version |
pyenv versions | List all installed versions |
pyenv version | Show the currently active version |
pyenv global 3.15.0 | Set global default version |
pyenv local 3.12.10 | Set version for current directory |
pyenv shell 3.13.3 | Set version for current shell only |
pyenv which python | Show path to active Python binary |
pyenv rehash | Rebuild shims (run after installing packages with binaries) |
Useful venv Commands Reference
| Command | Description |
|---|---|
python -m venv .venv | Create a virtual environment |
source .venv/bin/activate | Activate (Linux/macOS) |
.venv\Scripts\Activate.ps1 | Activate (Windows PowerShell) |
deactivate | Deactivate the current environment |
pip install <package> | Install a package into the active env |
pip freeze > requirements.txt | Export installed packages |
pip install -r requirements.txt | Install from requirements file |
pip list | List installed packages |
pip show <package> | Show package details |
Common Pitfalls
“python” still points to system Python after installing pyenv
Ensure the pyenv shims directory is at the front of your PATH. Run pyenv doctor to diagnose configuration issues.
Virtual environment was created with the wrong Python version
Delete .venv/ and recreate it after running pyenv local <version>. The Python version baked into a venv cannot be changed in place.
pip installs to system Python instead of venv
Make sure you activated the venv. Your prompt should show (.venv). Run which pip (Linux/macOS) or where pip (Windows) to confirm.
Packages are missing every time I activate the venv
Packages persist inside .venv/ and do not disappear on deactivation. If packages are consistently missing on activate, one of these is happening: (a) you are accidentally activating a different .venv in a parent directory, (b) something is deleting and recreating .venv (e.g., a CI script or an IDE setting), or (c) you ran python -m venv .venv again, which overwrites the directory. Run pip list right after activating and compare to ls .venv/lib/ to confirm the environment’s contents. If you need to restore packages, run pip install -r requirements.txt.
Windows: “cannot be loaded because running scripts is disabled”
Run Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser in PowerShell before activating.
pyenv-win: Python installed but not recognized
Run pyenv rehash after installation to regenerate shims, then open a new terminal window.
Summary
- Install pyenv (or pyenv-win on Windows) to manage multiple Python interpreter versions side by side
- Use
pyenv local <version>to pin a Python version per project directory - Use
python -m venv .venvto create an isolated package environment for each project - Activate the venv before installing packages; freeze with
pip freeze > requirements.txt - Python 3.15 is installable today via
pyenv install 3.15.0and brings UTF-8 defaults and improved error messages
This setup keeps your local machine clean, your projects reproducible, and your Python versions organized regardless of whether you are on Linux, macOS, or Windows.