The “System Python” is the interpreter that comes pre-installed with your operating system (typically found at /usr/bin/python3 on macOS and Linux). This interpreter exists primarily to run various system utilities and tools, and should not be used for your personal development work.

Using system Python directly for your projects introduces three main problems:

1. Risk of breaking system utilities

Some system utilities depend on specific versions of Python packages. Installing, upgrading or removing packages in the system environment can make those utilities stop working. To mitigate this, newer OS releases enforce PEP 668 EXTERNALLY-MANAGED marker, which tells tools such as pip to refuse installations outside virtual environments by default. This helps protect packages managed by the operating system or another package manager.

2. Limited control over the Python version

The system interpreter’s version is chosen by its distributor. Your projects may need something different: one might use features introduced in Python 3.12, while another depends on libraries that only support Python 3.10. A single interpreter cannot satisfy both requirements. Installing Python versions separately gives you control over which version each project uses.

3. Shared dependencies across projects

When projects use the same Python environment, they share its installed packages. If Project A requires version 1.0 of a library and Project B requires version 2.0, installing one version can break the other project. A separate virtual environment for each project helps to keeps its dependencies isolated, so that changes to one project’s packages does not affect another.


How to Check Your System Python?

To see if you have accidentally installed third-party packages into your system Python for macOS/Linux, run this command:

/usr/bin/python3 -m pip list

The following are some of the expected packages you might find:

NOTE

Your local list might look slightly different depending on your specific operating system version and the developer tools you have installed.

Expected Packages For macOS

PackagePurpose
pipThe standard package installer for Python. It fetches and installs software packages from the Python Package Index (PyPI).
setuptoolsA core library designed to faciliate packaging Python projects. It gives Python the ability to build, install, and manage the metadata of software packages.
wheelA build-package format for Python. It allows packages to be installed much faster than compiling them from source code.
sixA compatibility library designed to allow developers to write code that runs on both the deprecated Python 2 and the modern Python 3 without modification.
futureSimilar to six, this is another compatibility layer used to bridge the gap between Python 2 and Python 3 syntax in legacy scripts.
macholibA highly specific macOS utility used to read, analyze, and edit Mach-O headers. Mach-O is the native executable file format used by macOS and iOS.
altgraphA graph (network) representation library. Apple uses it behind the scenes, often in conjunction with macholib, to map out the dependency trees of macOS executable files.

Expected Packages For Linux (Debian & Ubuntu-based)

PackagePurpose
apt-pkgPython bindings for the Advanced Package Tool (APT). This allows Python-based OS scripts to interact directly with the system’s software updater to install or remove Debian packages (.deb).
debconfA module that interacts with the Debian configuration management system. When you install a system package and a blue screen pops up asking you a configuration question, debconf is often handling that prompt.
ufwUncomplicated Firewall. The actual command-line tool used to manage the system’s firewall on Ubuntu is written in Python, so its core modules live here.
apparmorPython bindings for AppArmor, a kernel security module that restricts individual programs’ capabilities.
dbus-pythonBindings for D-Bus, a system that allows different software applications to communicate with each other. It is heavily used by Linux desktop environments to pass messages (e.g., telling the OS a USB drive was plugged in).
PyGObjectA wrapper that allows Python to interface with GObject-based libraries, most notably GTK. This is heavily used by GNOME and other desktop environments to draw graphical user interfaces.
distroA lightweight package used to reliably extract detailed information about the specific Linux distribution and version currently running.

The following are common third-party libraries that you should NOT see on your list: requests, numpy, pandas, matplotlib, Flask, Django, FastAPI. Seeing these means someone (or an automated script) accidentally installed packages globally.


What to Use Instead?

To manage your projects independently of system Python, you need two things:

  1. Python version management, so each project can use the version it needs.
  2. Dependency isolation, so installing packages for one project does not affect another.

Historically a common approach is to combine pyenv for version management with venv for isolation. As of the time of writing this, we now have uv which brings both capabilities into one tool, alongside dependency management.

1. Using uv on a project

Whether you are starting from scratch or working on an existing codebase, open a terminal in your project directory and run:

uv init

uv will scan your system and pick the best Python version for the job, writing it down in a local .python-version file.

2. Adding packages

To install dependencies (like requests or pandas), use the add command:

uv add requests

3. Running your code

You can completely skip activating virtual environments using the uv run command:

uv run main.py