QGIS Python Version Compatibility Guide

QGIS ships with a strictly version-locked Python interpreter. The bundled Python minor version changes with each major release, and you cannot safely swap it for a system Python, conda environment, or standalone installer without breaking compiled qgis.core and qgis.gui bindings. Always match external packages to the exact minor version QGIS provides.

QGIS Release RangeBundled PythonCompatibility Notes
3.16–3.223.9.xLegacy LTR. Requires pip install --ignore-installed.
3.24–3.303.9.x → 3.10.xTransition builds. macOS Homebrew may ship mismatched wheels.
3.32–3.383.10.xStable baseline. C-extensions must target the 3.10 ABI.
3.40+3.12.xRequires updated C-extensions. PyQt6 migration begins.

Rule of thumb: Never assume a Python wheel built for your OS system Python will work inside QGIS. Verify the ABI tag (cp39, cp310, cp312) matches the QGIS runtime before installing.

Why Version Locking Matters

The QGIS API Architecture relies on SIP-generated Python bindings compiled against a specific Python ABI and Qt/PyQt version. When Python’s minor version changes, the C-extensions in qgis._core become binary-incompatible. Mixing external Python installations with QGIS plugins typically triggers ImportError: DLL load failed or ModuleNotFoundError: qgis.

Verify Your Active Environment

Run this in the QGIS Python Console (Plugins > Python Console) to confirm version alignment:

import sys
import qgis.core

print(f"QGIS Python: {sys.version_info.major}.{sys.version_info.minor}")
print(f"QGIS Core Path: {qgis.core.__file__}")

if sys.version_info < (3, 9):
 raise RuntimeError("Python < 3.9 is incompatible with QGIS 3.16+")

Safe Dependency Installation

Follow the PyQGIS Fundamentals & Environment Setup workflow to treat QGIS as a self-contained runtime. Add third-party libraries without breaking bindings:

  1. Use the bundled pip: QGIS 3.22+ includes pip in the console.
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"])
  1. Windows: Always run external scripts via python-qgis.bat (located in the QGIS install folder). This launcher sets PYTHONHOME, PYTHONPATH, and QT_PLUGIN_PATH automatically.
  2. Linux/macOS: Invoke qgis --code script.py or source the official qgis.sh environment script. Never manually export PYTHONPATH to system directories.

Recover from ABI Mismatches

If you encounter ImportError: cannot import name 'QgsApplication', DLL load failed, or silent crashes after installing packages:

  1. Clear corrupted packages: Delete manually copied folders in QGIS’s user profile:
  • Windows: %APPDATA%\QGIS\QGIS3\profiles\default\python\
  • Linux/macOS: ~/.local/share/QGIS/QGIS3/profiles/default/python/
  1. Force reinstall via QGIS pip: Open the Python Console and run:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "--force-reinstall", "package_name"])
  1. Use OSGeo4W for geospatial wheels: For GDAL, Fiona, or Shapely, install via the OSGeo4W installer. It compiles binaries against the exact Python version and MSVC runtime QGIS expects, preventing C-extension segfaults.
  2. Avoid venv for QGIS plugins: Copying qgis and PyQt5 into a virtual environment only works temporarily and breaks on updates. Reserve external environments for pure data processing (pandas, rasterio) that runs outside the QGIS process.

Deployment Checklist

  • ✅ Match your plugin’s metadata.txt qgis_minimum_version to the Python version you tested.
  • ✅ Never hardcode sys.path. Use os.environ["PYTHONPATH"] only when spawning external processes.
  • ✅ Audit C-extensions before upgrading QGIS. Python 3.10+ drops legacy wheel formats and changes str/bytes handling.
  • ✅ Keep qgis.core, qgis.gui, and qgis.analysis imports strictly inside the bundled runtime.