From e111300b762a556b923f27e54a557ea179b46a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Mon, 19 Oct 2026 16:20:06 +0200 Subject: [PATCH 1/2] fix(tests): load modules with exec_module in test_low_findings Python 3.15 removed SourceFileLoader.load_module(), so the module-level imports in tests/test_low_findings.py raise AttributeError and the whole suite fails to load. selftest.yml floats on python-version '3.x', so CI breaks as soon as setup-python resolves to 3.15. Load gen-sbom, sbom-driver.py and compdb_sbom.py through spec_from_loader + exec_module, the shape test_gen_sbom.py and test_sbom_identity.py already use, and do the same in the /dev/stdout subprocess snippet. Tested on Python 3.9.6, 3.14 and 3.15.0. --- tests/test_low_findings.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/test_low_findings.py b/tests/test_low_findings.py index 2bb4b79..241ea87 100644 --- a/tests/test_low_findings.py +++ b/tests/test_low_findings.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """Regression tests for the low-severity review-findings bundle (gpex.20).""" +import importlib.util import json import os import stat @@ -15,12 +16,19 @@ GEN_SBOM = os.path.join(SHARE, "gen-sbom") VALIDATE = os.path.join(SHARE, "validate_sbom.py") -gs = SourceFileLoader("gen_sbom", GEN_SBOM).load_module() -drv = SourceFileLoader("sbom_driver", os.path.join(SHARE, "sbom-driver.py") - ).load_module() -compdb = SourceFileLoader( - "compdb_sbom", os.path.join(SHARE, "frontends", "compdb_sbom.py") -).load_module() + +def _load(name, path): + loader = SourceFileLoader(name, path) + module = importlib.util.module_from_spec( + importlib.util.spec_from_loader(name, loader)) + loader.exec_module(module) + return module + + +gs = _load("gen_sbom", GEN_SBOM) +drv = _load("sbom_driver", os.path.join(SHARE, "sbom-driver.py")) +compdb = _load("compdb_sbom", + os.path.join(SHARE, "frontends", "compdb_sbom.py")) class TestAtomicWrite(unittest.TestCase): @@ -35,9 +43,11 @@ def test_writes_content_and_leaves_no_temp(self): def test_dev_stdout_redirected_to_file(self): # `--cdx-out /dev/stdout > out.json`: /dev/stdout is a symlink to the # redirected file and must be written through, not replaced. - code = ("from importlib.machinery import SourceFileLoader as L; " - "L('g', %r).load_module().write_json_atomic({'a': 1}, " - "'/dev/stdout')" % GEN_SBOM) + code = ("import importlib.util as u; " + "from importlib.machinery import SourceFileLoader as L; " + "l = L('g', %r); m = u.module_from_spec(" + "u.spec_from_loader('g', l)); l.exec_module(m); " + "m.write_json_atomic({'a': 1}, '/dev/stdout')" % GEN_SBOM) with tempfile.TemporaryDirectory() as d: out = os.path.join(d, "out.json") with open(out, "w") as f: From 1e88ea026d66177ada357e874b9d7bcbe1af427e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Mon, 19 Oct 2026 16:20:27 +0200 Subject: [PATCH 2/2] fix(compdb): pass maxsplit to re.split by keyword macro_name() passed maxsplit to re.split positionally. Python 3.13 deprecated that form and a later release will reject it, which would break the compile_commands.json frontend in every product that vendors it. Passing it by keyword works on every supported Python. Tested with -W error::DeprecationWarning on Python 3.9.6, 3.14 and 3.15.0. --- share/frontends/compdb_sbom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/frontends/compdb_sbom.py b/share/frontends/compdb_sbom.py index 34f427a..804366c 100755 --- a/share/frontends/compdb_sbom.py +++ b/share/frontends/compdb_sbom.py @@ -51,7 +51,7 @@ def entry_tokens(entry): def macro_name(d): """Macro name of a -D/-U operand: the text before the first '=' or '(', so a function-like 'F(a)=a' is keyed as 'F'.""" - return re.split(r'[=(]', d, 1)[0] + return re.split(r'[=(]', d, maxsplit=1)[0] def extract_defines(tokens):