From 4230e66a4e3f275162634e61d6e44ac4066989d1 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sun, 8 Feb 2026 21:43:05 +0100 Subject: [PATCH] [Python] Assert ABI-compatible Python version on `import ROOT` We should give the user a helpful error message if the ROOT and Python interpreter versions don't match. Otherwise, one will get confusing `undefined symbol` error or other unclear behavior at runtime. This came up sometimes in forum posts or GitHub issues, so the clear error would have helped. --- .../pythonizations/python/CMakeLists.txt | 6 ++++++ .../pythonizations/python/ROOT/__init__.py | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/bindings/pyroot/pythonizations/python/CMakeLists.txt b/bindings/pyroot/pythonizations/python/CMakeLists.txt index 0ea818af2f146..892e26b5bda01 100644 --- a/bindings/pyroot/pythonizations/python/CMakeLists.txt +++ b/bindings/pyroot/pythonizations/python/CMakeLists.txt @@ -172,3 +172,9 @@ set(py_sources ) ROOT_PYTHON_PACKAGE(ROOT SOURCES ${py_sources}) + +# The Python version at build time should be easy to figure out from Python, so +# we can raise an exception if the used Python version is not compatible. +set(python_version_file "${localruntimedir}/ROOT/_python_version.py") +file(WRITE "${python_version_file}" "_root_python_version = \"${Python3_VERSION}\"\n") +install(FILES "${python_version_file}" DESTINATION "${CMAKE_INSTALL_PYTHONDIR}") diff --git a/bindings/pyroot/pythonizations/python/ROOT/__init__.py b/bindings/pyroot/pythonizations/python/ROOT/__init__.py index 0a5ae7bda7736..1cf84f2ffe8c1 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/__init__.py +++ b/bindings/pyroot/pythonizations/python/ROOT/__init__.py @@ -12,6 +12,7 @@ import builtins import os +import platform import sys import types from importlib.abc import Loader, MetaPathFinder @@ -19,6 +20,25 @@ from . import _asan # noqa: F401 # imported for side effects for setup specific to AddressSanitizer environments from ._facade import ROOTFacade +from ._python_version import _root_python_version + +_runtime_version = platform.python_version() + + +def _major_minor(v): + return ".".join(v.split(".")[:2]) + + +# Check for Python ABI compatibility with this ROOT build. This check prevents +# hard crashes and undefined behavior, yielding helpful error messages instead. +if _major_minor(_runtime_version) != _major_minor(_root_python_version): + import textwrap + + message = f""" + ROOT was built for Python {_root_python_version}, but you are running Python {_runtime_version}. + Python major.minor versions must match. Use a matching Python or ROOT build. + """ + raise ImportError(textwrap.dedent(message)) # Prevent cppyy's check for extra header directory os.environ["CPPYY_API_PATH"] = "none"