skip to content

Python Installation — macOS

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

12 min read 39 snippets deep dive

Python Installation — macOS#

# 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:

==> 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:

# Add Homebrew Python to PATH (add to ~/.zshrc or ~/.bash_profile)
export PATH="$(brew --prefix python@3.12)/bin:$PATH"
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:

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
pyenv versions

Output:

  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.

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

[!WARNING] 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#

python3 --version
pip3 --version
which python3

Output:

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#

[!WARNING] 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.

[!TIP] Check PATH order: echo $PATH | tr ':' '\n' | grep -n python

Next steps#

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
# 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:

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

[!WARNING] 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.

# 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:

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

[!TIP] 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.

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:

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.

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:

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

[!TIP] 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).

# 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:

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

[!NOTE] 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
# 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:

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.

# 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:

Selecting 'python312' for 'python3' succeeded
Python 3.12.3

[!NOTE] 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.

# 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:

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

[!TIP] 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.

# 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:

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

[!WARNING] 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.

# 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:

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

[!TIP] 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.

# 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:

/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)#

[!WARNING] 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.

[!WARNING] 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.

[!WARNING] 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.

[!TIP] 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#

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#

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#

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#

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#

# 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#

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.