cheat sheet

Python Installation

Install Python 3 on macOS via Homebrew or pyenv. Explains the system-Python warning, PATH precedence, and verification steps.

Python Installation — macOS

bash
# Install Homebrew first if you haven't
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install Python
brew install python@3.12

Output:

text
==> Downloading https://ghcr.io/v2/homebrew/core/python/3.12/manifests/3.12.3
==> Fetching python@3.12
==> Installing python@3.12
...
==> Summary
🍺  /opt/homebrew/Cellar/python@3.12/3.12.3: 3,217 files, 56.7MB

Homebrew symlinks python3 and pip3 automatically. To also get python and pip (without version suffix) in your PATH:

bash
# Add Homebrew Python to PATH (add to ~/.zshrc or ~/.bash_profile)
export PATH="$(brew --prefix python@3.12)/bin:$PATH"
bash
brew install pyenv

# Add to ~/.zshrc
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
source ~/.zshrc

pyenv install 3.12.3
pyenv global 3.12.3

Output:

text
python-build: use openssl@3 from homebrew
Downloading Python-3.12.3.tar.xz...
Installing Python-3.12.3...
Installed Python-3.12.3 to /Users/alice/.pyenv/versions/3.12.3
bash
pyenv versions

Output:

text
  system
* 3.12.3 (set by /Users/alice/.pyenv/version)

The system Python warning

macOS ships /usr/bin/python3 (a thin stub that prompts you to install Xcode Command Line Tools). It is managed by Apple and should not be used for development.

bash
which python3
/usr/bin/python3          # this is the system stub
/opt/homebrew/bin/python3 # this is Homebrew Python

Never run pip install against the system Python — it may interfere with macOS utilities and you cannot upgrade or remove system packages safely. Always work inside a virtual environment or use a Homebrew/pyenv-managed Python.

Verify

bash
python3 --version
pip3 --version
which python3

Output:

text
Python 3.12.3
pip 24.0 from /opt/homebrew/lib/python3.12/site-packages/pip (python 3.12)
/opt/homebrew/bin/python3

Common pitfalls

PATH order matters — if /usr/bin appears before /opt/homebrew/bin in your $PATH, the system stub wins. Make sure your shell config exports the Homebrew prefix early.

Check PATH order: echo $PATH | tr ':' '\n' | grep -n python

Next steps

bash
python3 -m venv .venv
source .venv/bin/activate

See Virtual Environments for the full guide.

Apple Silicon vs Intel — prefixes that matter

The Homebrew prefix differs between Apple Silicon (M1/M2/M3/M4) and Intel Macs, which trips up shell scripts copied from one machine to another. Always derive the prefix dynamically rather than hard-coding it.

MachineHomebrew prefixPython binary
Apple Silicon/opt/homebrew/opt/homebrew/bin/python3
Intel/usr/local/usr/local/bin/python3
Rosetta 2 Intel-emulating shell on Apple Silicon/usr/local/usr/local/bin/python3
bash
# Always use the dynamic prefix
brew --prefix
export PATH="$(brew --prefix)/bin:$PATH"

# Check what kind of CPU you're running on
sysctl -n machdep.cpu.brand_string
uname -m

# Check if your current shell is x86 or arm64
arch
file $(which python3)

Output:

text
/opt/homebrew
Apple M2 Pro
arm64
arm64
/opt/homebrew/bin/python3: Mach-O 64-bit executable arm64

If you have both /opt/homebrew and /usr/local populated (a leftover from migrating from Intel), which -a python3 will show two interpreters. They are completely separate installations — packages installed in one are invisible to the other.

Method 3 — uv python install

uv downloads a statically-linked CPython tarball (from the python-build-standalone project) and unpacks it under ~/.local/share/uv/python/. It is the fastest possible install — no Xcode Command Line Tools needed, no Homebrew dependency, no recompilation on every minor version bump.

bash
# Install uv itself
curl -LsSf https://astral.sh/uv/install.sh | sh
exec $SHELL    # reload PATH

# Install one or many Pythons
uv python install 3.12
uv python install 3.11 3.13

# List what uv can see (both uv-managed and system-installed)
uv python list

# Pin a project
cd ~/code/my-project
uv python pin 3.12
cat .python-version

Output:

text
Installed Python 3.12.3 in 1.27s
 + cpython-3.12.3-macos-aarch64-none (python3.12)
3.12.3

uv python install never edits global PATH or shell config — uv run, uv venv, and uv tool run all find the right interpreter via uv's own discovery logic. If you want a python shim on PATH, run uv tool update-shell.

Method 4 — asdf

asdf is a polyglot version manager that handles Python alongside Node, Ruby, Go, Java, and ~600 other languages via a plugin system. Its Python plugin wraps python-build (the build script behind pyenv), so the install experience is the same: source compile, slow first time, fast switches afterwards.

bash
brew install asdf

# Wire asdf into your shell
echo -e "\n. \"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.zshrc
source ~/.zshrc

# Add the Python plugin and install
asdf plugin add python
asdf install python 3.12.3
asdf install python 3.13.0

# Set a global default
asdf global python 3.12.3

# Per-project pin (writes .tool-versions)
cd ~/code/my-project
asdf local python 3.13.0
cat .tool-versions

Output:

text
python 3.12.3 installation successful
python 3.13.0

Method 5 — mise

mise is a Rust-based successor to asdf with the same .tool-versions file format but dramatically faster shell startup (single-digit milliseconds vs ~80 ms for asdf). It also defaults to downloading precompiled Pythons via python-build rather than compiling from source on first use.

bash
brew install mise

# Wire mise into your shell
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
source ~/.zshrc

# Install and use Python
mise use --global python@3.12
mise use python@3.13          # per-directory

# Inspect
mise ls python
mise current python

Output:

text
mise python@3.12.3 ✓ installed
mise ~/code/my-project: writing .tool-versions
python 3.13.0

If you are starting fresh in 2026, mise is the better choice over asdf. Same plugin ecosystem, same config file, much faster.

Method 6 — python.org installer

The official .pkg installer from python.org is universal2 (Apple Silicon + Intel) and installs into /Library/Frameworks/Python.framework/Versions/3.12/. It is unaffected by Homebrew upgrades and remains the most reliable choice when you need a "framework build" — a Python that knows how to draw windows via the macOS GUI APIs (required by Tkinter, IDLE, and some scientific GUI tools).

bash
# Download the latest .pkg
curl -LO https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg

# Verify the signature (optional but recommended)
pkgutil --check-signature python-3.12.3-macos11.pkg

# Install (will prompt for admin password)
sudo installer -pkg python-3.12.3-macos11.pkg -target /

# The installer creates a shim under /usr/local/bin
/usr/local/bin/python3 --version

Output:

text
Status: signed by a developer certificate issued by Apple for distribution
installer: Package name is Python 3.12.3
installer: Installation successful.
Python 3.12.3

The python.org installer drops an "Install Certificates.command" script under /Applications/Python 3.12/. Run it once to populate the certificate store — without it, pip install may fail with SSL: CERTIFICATE_VERIFY_FAILED.

Framework build vs CLT Python vs Homebrew Python

macOS has historically had several Python flavors, each linked differently against system libraries. They are not interchangeable in subtle ways.

FlavorLinkerTkinter / GUIWhen to use
System Python stub (/usr/bin/python3)Apple CLTYes (via Apple Tk)Never — it's just a stub that triggers a CLT install
Xcode Command Line Tools PythonApple CLTYesApple's internal scripts only
Homebrew python@3.12HomebrewYes (via python-tk@3.12)Day-to-day dev work
python.org framework buildApple frameworksYes (uses bundled Tk)When you need IDLE, Tkinter, or PyObjC against a stable framework
pyenv-built PythonOpenSSL from HomebrewOptional (needs tcl-tk)Multiple-version workflows
uv python-build-standaloneStaticLimited TkinterCI, ephemeral environments
bash
# Find every python3 on the system
ls -l /usr/bin/python3
ls -l $(brew --prefix)/bin/python3
ls -l /Library/Frameworks/Python.framework/Versions/*/bin/python3
ls -l ~/.pyenv/versions/*/bin/python3 2>/dev/null
ls -l ~/.local/share/uv/python/*/bin/python3 2>/dev/null

Output:

text
lrwxr-xr-x  /usr/bin/python3 -> ../../Library/Developer/CommandLineTools/usr/bin/python3
lrwxr-xr-x  /opt/homebrew/bin/python3 -> ../Cellar/python@3.12/3.12.3/bin/python3.12
lrwxr-xr-x  /Library/Frameworks/Python.framework/Versions/3.12/bin/python3 -> python3.12

Method 7 — MacPorts

MacPorts is the older sibling to Homebrew, building everything from source by default into /opt/local/. It still has a loyal user base, particularly among people who need reproducible builds with very explicit dependencies.

bash
# After installing MacPorts itself from https://www.macports.org/install.php
sudo port selfupdate
sudo port install python312 py312-pip py312-virtualenv

# Pick which python3 the `python3` alias points to
sudo port select --set python3 python312
sudo port select --set pip3 pip312

python3 --version

Output:

text
Selecting 'python312' for 'python3' succeeded
Python 3.12.3

MacPorts and Homebrew can coexist — they install into different prefixes (/opt/local vs /opt/homebrew) — but you should not source both into the same shell. The PATH-ordering wars will eventually bite you.

Method 8 — conda / mamba / miniforge

For GPU machine learning, scientific computing, or polyglot data stacks (Python + R + Julia), the conda ecosystem manages native libraries alongside Python. On Apple Silicon, prefer Miniforge — it defaults to the community conda-forge channel (no Anaconda Inc. license risk) and has full native arm64 binaries.

bash
# Install Miniforge for the current architecture
curl -LO "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh -b -p ~/miniforge3

# Initialize for zsh
~/miniforge3/bin/conda init zsh
exec $SHELL

# Create a project environment
mamba create -n ml python=3.12 numpy pandas pytorch -c pytorch
mamba activate ml
python -c "import torch; print(torch.backends.mps.is_available())"

Output:

text
Looking for: ['python=3.12', 'numpy', 'pandas', 'pytorch']
+ python  3.12.3-h99e199e_0
+ numpy   2.0.0
+ pandas  2.2.2
+ pytorch 2.3.0-cpu_py312
done
True

Apple Silicon's mps backend (Metal Performance Shaders) is PyTorch's equivalent of CUDA. torch.backends.mps.is_available() returning True confirms GPU acceleration is wired up.

Multi-version coexistence

It is normal on a working macOS dev box to have Homebrew Python, pyenv Pythons, a uv Python, and the Apple system stub all coexisting. The trick is keeping their PATH precedence stable.

bash
# A typical mixed environment
brew list | grep python
ls ~/.pyenv/versions 2>/dev/null
uv python list

# What does `python3` resolve to right now?
which -a python3

# Make pyenv win for the current shell
eval "$(pyenv init -)"
which python3            # should be a pyenv shim

# Make Homebrew win
export PATH="$(brew --prefix)/bin:$PATH"
which python3            # should be /opt/homebrew/bin/python3

Output:

text
python@3.11
python@3.12
python@3.13
3.11.9
3.12.3
3.13.0
/Users/alice/.pyenv/shims/python3
/Users/alice/.pyenv/shims/python3
/opt/homebrew/bin/python3
/usr/bin/python3
/opt/homebrew/bin/python3

If you eval "$(pyenv init -)" and prepend Homebrew's bin directory, the last one wins. Pick one strategy per shell config and stick with it.

Removing the macOS system Python warning

The first time you type python3 on a new Mac without Xcode Command Line Tools, macOS shows a GUI dialog: "The 'python3' command requires the command line developer tools." Once you install a real Python (Homebrew, pyenv, uv, etc.), the message vanishes — but only if PATH is wired correctly.

bash
# 1. Install Homebrew Python (or any other)
brew install python@3.12

# 2. Confirm it's earlier on PATH than /usr/bin
echo $PATH | tr ':' '\n' | nl | grep -E '(homebrew|usr/bin)'

# 3. Reload the shell
exec $SHELL

# 4. The popup should never appear again
python3 -c "print('hello')"

Output:

text
     1  /opt/homebrew/bin
     2  /usr/local/bin
     3  /usr/bin
hello

If you genuinely don't need Apple's Command Line Tools for anything else (no Xcode, no git, no Homebrew formula compilations), you can leave them uninstalled and use a uv-managed Python — it has no Apple-specific build dependencies.

Verification recipes

After any install, run these to confirm the result.

bash
# Where is python3 resolving from?
which -a python3

# Version
python3 --version

# Is pip wired to the same interpreter?
python3 -m pip --version

# Does the venv module work?
python3 -m venv /tmp/_probe && rm -rf /tmp/_probe && echo 'venv OK'

# Does SSL work?
python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"

# Does Tkinter work? (GUI module that often breaks on macOS)
python3 -c "import tkinter; print(tkinter.TkVersion)"

# Can pip reach PyPI?
python3 -m pip install --dry-run requests

Output:

text
/opt/homebrew/bin/python3
Python 3.12.3
pip 24.0 from /opt/homebrew/lib/python3.12/site-packages/pip (python 3.12)
venv OK
OpenSSL 3.3.0 9 Apr 2024
8.6
Would install requests-2.32.3 certifi-2024.7.4 charset-normalizer-3.3.2 idna-3.7 urllib3-2.2.2

Troubleshooting

SymptomCauseFix
GUI dialog: "command line developer tools required"No real Python on PATHbrew install python@3.12 then restart shell
SSL: CERTIFICATE_VERIFY_FAILEDMissing cert storepython.org: run /Applications/Python 3.12/Install Certificates.command. Homebrew: pip install --upgrade certifi
import tkinter failsTcl/Tk missingbrew install python-tk@3.12 for Homebrew; tcl-tk for pyenv
pip install fails with error: Microsoft Visual C++ (yes, on macOS)Misdiagnosed wheel platformYou're using a Windows wheel by accident — pip cache purge and reinstall
bad CPU type in executableIntel binary on Apple Silicon (or vice-versa)Reinstall the package for the right arch; or use Rosetta with arch -x86_64
Homebrew complains about python3 already linkedTwo python@3.X formulae are installedbrew unlink python@3.11 && brew link --force python@3.12
pyenv refuses to buildMissing build dependenciesbrew install openssl@3 readline sqlite3 xz zlib tcl-tk
Slow first import of Python (~2 s)Gatekeeper checking quarantine on unsigned tarballsxattr -dr com.apple.quarantine /opt/homebrew/Cellar/python@3.12

Common pitfalls (extended)

brew upgrade is destructive — Running brew upgrade without arguments can move you from python@3.12 to python@3.13 if both are installed. Always specify the formula: brew upgrade python@3.12. Or pin: brew pin python@3.12.

Don't sudo brew — Homebrew refuses to run under sudo for a reason. If you see Permission denied, fix ownership: sudo chown -R $(whoami) $(brew --prefix)/Cellar once, never use sudo again.

Tcl/Tk version mismatch — Homebrew's python@3.12 links against tcl-tk@8, but some scientific packages expect tcl-tk@9. If import tkinter works but the window is invisible, run brew info python-tk@3.12 to see which Tk it actually linked against.

Restore a known-good shell config by stashing your ~/.zshrc and recreating it minimally: just eval "$(/opt/homebrew/bin/brew shellenv)" and eval "$(pyenv init -)" (in that order) cover 90% of cases.

Real-world recipes

Fresh Apple Silicon laptop in five commands

bash
xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"
brew install python@3.12 git
python3 -m pip install --user uv

Lock the project to Python 3.12.3 across all team Macs

bash
echo '3.12.3' > .python-version
echo 'python 3.12.3' > .tool-versions

# Anyone with mise / asdf / pyenv / uv on PATH will pick the right version automatically
mise install

Switch a single project between two Python versions

bash
cd ~/code/data-pipeline
pyenv local 3.12.3              # main work
pyenv local 3.13.0              # try the latest

# Or with uv (without writing a global env)
uv venv --python 3.13
uv run pytest

Apple Silicon GPU acceleration with PyTorch

bash
brew install miniforge
conda init zsh && exec $SHELL
mamba create -n ml python=3.12 pytorch torchvision -c pytorch
mamba activate ml
python -c "import torch; print(torch.backends.mps.is_available())"

Uninstalling cleanly

bash
# Homebrew
brew uninstall python@3.12

# pyenv (specific version)
pyenv uninstall 3.12.3

# python.org framework build (manual)
sudo rm -rf /Library/Frameworks/Python.framework/Versions/3.12
sudo rm /usr/local/bin/python3.12 /usr/local/bin/pip3.12

# uv-managed Pythons
uv python uninstall 3.12

# Verify nothing remains
which -a python3
ls /Library/Frameworks/Python.framework/Versions/

Next steps

bash
python3 -m venv .venv
source .venv/bin/activate

See Virtual Environments for the full guide, Homebrew for deeper coverage of the macOS package manager, and pip vs uv for the package-manager comparison.