From d17feaee8a614c99771633b1738f31e2e24f6403 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Sun, 9 Aug 2026 15:33:44 -0700 Subject: [PATCH 1/5] Add onedir package test for libyaml-linked PyYAML Regression cover for #69907 / PR #69950 (3006.x) / #69949 (3008.x). Spawns the onedir python and asserts yaml.CSafeLoader/CSafeDumper and the _yaml C extension are present, plus salt.utils.yamlloader.SafeLoader resolves to yaml.CSafeLoader. Linux-only; Windows/macOS onedirs already pick libyaml-linked wheels because they do not pass --no-binary=:all: to pip. --- tests/pytests/pkg/integration/test_libyaml.py | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/pytests/pkg/integration/test_libyaml.py diff --git a/tests/pytests/pkg/integration/test_libyaml.py b/tests/pytests/pkg/integration/test_libyaml.py new file mode 100644 index 000000000000..038424a18cbc --- /dev/null +++ b/tests/pytests/pkg/integration/test_libyaml.py @@ -0,0 +1,96 @@ +""" +Verify the onedir bundle ships a libyaml-linked PyYAML. + +Regression cover for #69907 / PR #69950 (3006.x) and #69949 (3008.x): +the Linux onedir build was source-compiling PyYAML under a relenv toolchain +that has no libyaml, so `yaml.CSafeLoader`/`yaml.CSafeDumper` were absent +and every YAML load fell back to the ~10-20x slower pure-Python parser. +""" + +import subprocess +import sys +import textwrap + +import pytest + + +@pytest.fixture +def python_script_bin(install_salt): + return install_salt.binary_paths["python"] + + +@pytest.fixture +def check_libyaml_file(tmp_path): + script_path = tmp_path / "check_libyaml.py" + script_path.write_text( + textwrap.dedent( + """ + import sys + import yaml + + assert hasattr(yaml, "CSafeLoader"), "yaml.CSafeLoader missing" + assert hasattr(yaml, "CSafeDumper"), "yaml.CSafeDumper missing" + assert hasattr(yaml, "CLoader"), "yaml.CLoader missing" + assert hasattr(yaml, "CDumper"), "yaml.CDumper missing" + + import _yaml # noqa: F401 # PyYAML C extension + + loader = yaml.CSafeLoader("key: value\\n") + try: + data = loader.get_single_data() + finally: + loader.dispose() + assert data == {"key": "value"}, data + sys.exit(0) + """ + ) + ) + return script_path + + +@pytest.mark.skipif( + not sys.platform.startswith("linux"), + reason="Only the Linux onedir build passes --no-binary=:all:; " + "Windows/macOS already pick libyaml-linked wheels.", +) +def test_libyaml_bundled_in_onedir(install_salt, python_script_bin, check_libyaml_file): + ret = install_salt.proc.run( + *(python_script_bin + [str(check_libyaml_file)]), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + universal_newlines=True, + ) + assert ret.returncode == 0, ret.stderr + + +@pytest.mark.skipif( + not sys.platform.startswith("linux"), + reason="Only the Linux onedir build passes --no-binary=:all:; " + "Windows/macOS already pick libyaml-linked wheels.", +) +def test_salt_yamlloader_uses_libyaml(install_salt, python_script_bin, tmp_path): + script_path = tmp_path / "check_yamlloader.py" + script_path.write_text( + textwrap.dedent( + """ + import sys + import yaml + import salt.utils.yamlloader + + assert salt.utils.yamlloader.SafeLoader is yaml.CSafeLoader, ( + "salt.utils.yamlloader.SafeLoader fell back to pure-Python " + "SafeLoader (libyaml not linked)" + ) + sys.exit(0) + """ + ) + ) + ret = install_salt.proc.run( + *(python_script_bin + [str(script_path)]), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + universal_newlines=True, + ) + assert ret.returncode == 0, ret.stderr From b3eb171d4e69cff0d218e05577beae60c1944cfb Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Mon, 10 Aug 2026 16:53:00 -0700 Subject: [PATCH 2/5] Fix yamlloader attribute name in libyaml onedir test salt.utils.yamlloader exports BaseLoader (which resolves to yaml.CSafeLoader when libyaml is linked), not SafeLoader. The initial test used the wrong name and hit AttributeError on every Linux runner. Assert against BaseLoader instead. --- tests/pytests/pkg/integration/test_libyaml.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/pytests/pkg/integration/test_libyaml.py b/tests/pytests/pkg/integration/test_libyaml.py index 038424a18cbc..c136c5490ec7 100644 --- a/tests/pytests/pkg/integration/test_libyaml.py +++ b/tests/pytests/pkg/integration/test_libyaml.py @@ -78,9 +78,9 @@ def test_salt_yamlloader_uses_libyaml(install_salt, python_script_bin, tmp_path) import yaml import salt.utils.yamlloader - assert salt.utils.yamlloader.SafeLoader is yaml.CSafeLoader, ( - "salt.utils.yamlloader.SafeLoader fell back to pure-Python " - "SafeLoader (libyaml not linked)" + assert salt.utils.yamlloader.BaseLoader is yaml.CSafeLoader, ( + "salt.utils.yamlloader.BaseLoader fell back to pure-Python " + "yaml.SafeLoader (libyaml not linked)" ) sys.exit(0) """ From 68c6747d76f2833e1369ee0cd7029d4ba8c80113 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 11 Aug 2026 00:40:25 -0700 Subject: [PATCH 3/5] Skip libyaml onedir test on downgrade flavor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pkg-test downgrade matrix installs current salt, then rolls back to the previous release before running pytest. That leaves the pre-#69950 onedir on disk, which correctly lacks libyaml-linked PyYAML — asserting its presence there produces a false positive. Gate both libyaml tests on install_salt.downgrade so the downgrade matrix skips them; install/upgrade flavors continue to exercise the fix. --- tests/pytests/pkg/integration/test_libyaml.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/pytests/pkg/integration/test_libyaml.py b/tests/pytests/pkg/integration/test_libyaml.py index c136c5490ec7..8a0bb4a9a638 100644 --- a/tests/pytests/pkg/integration/test_libyaml.py +++ b/tests/pytests/pkg/integration/test_libyaml.py @@ -54,6 +54,11 @@ def check_libyaml_file(tmp_path): "Windows/macOS already pick libyaml-linked wheels.", ) def test_libyaml_bundled_in_onedir(install_salt, python_script_bin, check_libyaml_file): + if install_salt.downgrade: + pytest.skip( + "Downgrade flavor tests against the pre-#69950 onedir; " + "libyaml is expected to be absent there." + ) ret = install_salt.proc.run( *(python_script_bin + [str(check_libyaml_file)]), stdout=subprocess.PIPE, @@ -70,6 +75,11 @@ def test_libyaml_bundled_in_onedir(install_salt, python_script_bin, check_libyam "Windows/macOS already pick libyaml-linked wheels.", ) def test_salt_yamlloader_uses_libyaml(install_salt, python_script_bin, tmp_path): + if install_salt.downgrade: + pytest.skip( + "Downgrade flavor tests against the pre-#69950 onedir; " + "libyaml is expected to be absent there." + ) script_path = tmp_path / "check_yamlloader.py" script_path.write_text( textwrap.dedent( From a077adc1ae8b0feeace1e858f3dcb5e9e03587b1 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 11 Aug 2026 17:23:30 -0700 Subject: [PATCH 4/5] test_libyaml: version-conditional assertions across install/upgrade/downgrade Post-downgrade pytest re-enters the pkg/integration suite with the previous salt onedir on disk and no --downgrade flag, so the earlier install_salt.downgrade guard didn't trigger and the test still failed. Rewritten to key on install_salt.version: - >= 3006.28 -> assert libyaml present (guards the fix) - < 3006.28 -> assert libyaml absent (documents the pre-fix baseline so a silent regression on the old branch is also caught) Works uniformly across the three pkg-test flavors without skips. --- tests/pytests/pkg/integration/test_libyaml.py | 70 ++++++++++++++----- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/tests/pytests/pkg/integration/test_libyaml.py b/tests/pytests/pkg/integration/test_libyaml.py index 8a0bb4a9a638..d1a8590ffbcd 100644 --- a/tests/pytests/pkg/integration/test_libyaml.py +++ b/tests/pytests/pkg/integration/test_libyaml.py @@ -5,20 +5,40 @@ the Linux onedir build was source-compiling PyYAML under a relenv toolchain that has no libyaml, so `yaml.CSafeLoader`/`yaml.CSafeDumper` were absent and every YAML load fell back to the ~10-20x slower pure-Python parser. + +The test asserts the invariant that matches whatever salt is installed at +run time, so it works uniformly across the install / upgrade / downgrade +package-test flavors: + +- install / post-upgrade: current onedir is on disk, expect libyaml present +- post-downgrade: previous onedir is on disk. If that release predates the + fix, expect libyaml absent (documenting the pre-fix state so a silent + regression on the previous branch is still caught). """ import subprocess import sys import textwrap +import packaging.version import pytest +# First release that ships the libyaml-linked PyYAML wheel on Linux onedir. +# Update if the fix is ever backported earlier. +LIBYAML_FIX_LANDED_IN = packaging.version.Version("3006.28") + @pytest.fixture def python_script_bin(install_salt): return install_salt.binary_paths["python"] +@pytest.fixture +def libyaml_expected(install_salt): + """True if the onedir currently on disk is expected to ship libyaml.""" + return packaging.version.Version(install_salt.version) >= LIBYAML_FIX_LANDED_IN + + @pytest.fixture def check_libyaml_file(tmp_path): script_path = tmp_path / "check_libyaml.py" @@ -53,12 +73,9 @@ def check_libyaml_file(tmp_path): reason="Only the Linux onedir build passes --no-binary=:all:; " "Windows/macOS already pick libyaml-linked wheels.", ) -def test_libyaml_bundled_in_onedir(install_salt, python_script_bin, check_libyaml_file): - if install_salt.downgrade: - pytest.skip( - "Downgrade flavor tests against the pre-#69950 onedir; " - "libyaml is expected to be absent there." - ) +def test_libyaml_matches_installed_version( + install_salt, python_script_bin, check_libyaml_file, libyaml_expected +): ret = install_salt.proc.run( *(python_script_bin + [str(check_libyaml_file)]), stdout=subprocess.PIPE, @@ -66,7 +83,17 @@ def test_libyaml_bundled_in_onedir(install_salt, python_script_bin, check_libyam check=False, universal_newlines=True, ) - assert ret.returncode == 0, ret.stderr + if libyaml_expected: + assert ret.returncode == 0, ( + f"libyaml expected present in salt {install_salt.version} " + f"(>= {LIBYAML_FIX_LANDED_IN}) but the probe failed:\n{ret.stderr}" + ) + else: + assert ret.returncode != 0, ( + f"libyaml unexpectedly present in salt {install_salt.version} " + f"(pre-{LIBYAML_FIX_LANDED_IN}). If the fix was backported " + f"earlier, lower LIBYAML_FIX_LANDED_IN in this test." + ) @pytest.mark.skipif( @@ -74,12 +101,9 @@ def test_libyaml_bundled_in_onedir(install_salt, python_script_bin, check_libyam reason="Only the Linux onedir build passes --no-binary=:all:; " "Windows/macOS already pick libyaml-linked wheels.", ) -def test_salt_yamlloader_uses_libyaml(install_salt, python_script_bin, tmp_path): - if install_salt.downgrade: - pytest.skip( - "Downgrade flavor tests against the pre-#69950 onedir; " - "libyaml is expected to be absent there." - ) +def test_salt_yamlloader_matches_installed_version( + install_salt, python_script_bin, tmp_path, libyaml_expected +): script_path = tmp_path / "check_yamlloader.py" script_path.write_text( textwrap.dedent( @@ -88,11 +112,7 @@ def test_salt_yamlloader_uses_libyaml(install_salt, python_script_bin, tmp_path) import yaml import salt.utils.yamlloader - assert salt.utils.yamlloader.BaseLoader is yaml.CSafeLoader, ( - "salt.utils.yamlloader.BaseLoader fell back to pure-Python " - "yaml.SafeLoader (libyaml not linked)" - ) - sys.exit(0) + sys.exit(0 if salt.utils.yamlloader.BaseLoader is getattr(yaml, "CSafeLoader", None) else 1) """ ) ) @@ -103,4 +123,16 @@ def test_salt_yamlloader_uses_libyaml(install_salt, python_script_bin, tmp_path) check=False, universal_newlines=True, ) - assert ret.returncode == 0, ret.stderr + if libyaml_expected: + assert ret.returncode == 0, ( + f"salt.utils.yamlloader.BaseLoader should be yaml.CSafeLoader in " + f"salt {install_salt.version} (>= {LIBYAML_FIX_LANDED_IN}); " + f"it resolved to the pure-Python loader instead." + ) + else: + assert ret.returncode != 0, ( + f"salt.utils.yamlloader.BaseLoader unexpectedly resolves to " + f"yaml.CSafeLoader in pre-{LIBYAML_FIX_LANDED_IN} " + f"salt {install_salt.version}. If the fix was backported " + f"earlier, lower LIBYAML_FIX_LANDED_IN in this test." + ) From a682bac586c71eefe9d335feb1ccc324c246bb5b Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Wed, 12 Aug 2026 00:37:31 -0700 Subject: [PATCH 5/5] test_libyaml: key on install_salt.use_prev_version, not artifact version The prior design compared install_salt.version >= Version("3006.28") to predict whether libyaml should be present, but dev builds report '3006.27+NNN.gSHA' which packaging.version orders BEFORE '3006.27' let alone '3006.28' (PEP 440 local-version segment). That flipped the expected-libyaml boolean to False on install jobs and turned every Linux install matrix row red. install_salt.use_prev_version is True iff the pytest run is executing against the downgraded-to previous release (set by --use-prev-version in the post-downgrade validation stage). That's the only flavor where the onedir predates PR #69950 and libyaml is legitimately absent. Key the expectation off that flag and drop the version-comparison plumbing. --- tests/pytests/pkg/integration/test_libyaml.py | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/tests/pytests/pkg/integration/test_libyaml.py b/tests/pytests/pkg/integration/test_libyaml.py index d1a8590ffbcd..900d9b39fb71 100644 --- a/tests/pytests/pkg/integration/test_libyaml.py +++ b/tests/pytests/pkg/integration/test_libyaml.py @@ -11,8 +11,8 @@ package-test flavors: - install / post-upgrade: current onedir is on disk, expect libyaml present -- post-downgrade: previous onedir is on disk. If that release predates the - fix, expect libyaml absent (documenting the pre-fix state so a silent +- post-downgrade: previous onedir is on disk. That release predates the + fix, so expect libyaml absent (documenting the pre-fix state so a silent regression on the previous branch is still caught). """ @@ -20,13 +20,8 @@ import sys import textwrap -import packaging.version import pytest -# First release that ships the libyaml-linked PyYAML wheel on Linux onedir. -# Update if the fix is ever backported earlier. -LIBYAML_FIX_LANDED_IN = packaging.version.Version("3006.28") - @pytest.fixture def python_script_bin(install_salt): @@ -35,8 +30,9 @@ def python_script_bin(install_salt): @pytest.fixture def libyaml_expected(install_salt): - """True if the onedir currently on disk is expected to ship libyaml.""" - return packaging.version.Version(install_salt.version) >= LIBYAML_FIX_LANDED_IN + """Current onedir (install/upgrade) ships libyaml; the previous release + (post-downgrade validation) predates PR #69950 and does not.""" + return not install_salt.use_prev_version @pytest.fixture @@ -85,14 +81,14 @@ def test_libyaml_matches_installed_version( ) if libyaml_expected: assert ret.returncode == 0, ( - f"libyaml expected present in salt {install_salt.version} " - f"(>= {LIBYAML_FIX_LANDED_IN}) but the probe failed:\n{ret.stderr}" + f"libyaml expected present in the current onedir but the probe " + f"failed:\n{ret.stderr}" ) else: assert ret.returncode != 0, ( - f"libyaml unexpectedly present in salt {install_salt.version} " - f"(pre-{LIBYAML_FIX_LANDED_IN}). If the fix was backported " - f"earlier, lower LIBYAML_FIX_LANDED_IN in this test." + "libyaml unexpectedly present in the previous-release onedir. " + "If PR #69950 was backported earlier than 3006.28, drop this " + "test's downgrade branch." ) @@ -125,14 +121,14 @@ def test_salt_yamlloader_matches_installed_version( ) if libyaml_expected: assert ret.returncode == 0, ( - f"salt.utils.yamlloader.BaseLoader should be yaml.CSafeLoader in " - f"salt {install_salt.version} (>= {LIBYAML_FIX_LANDED_IN}); " - f"it resolved to the pure-Python loader instead." + "salt.utils.yamlloader.BaseLoader should be yaml.CSafeLoader in " + "the current onedir; it resolved to the pure-Python loader " + "instead." ) else: assert ret.returncode != 0, ( - f"salt.utils.yamlloader.BaseLoader unexpectedly resolves to " - f"yaml.CSafeLoader in pre-{LIBYAML_FIX_LANDED_IN} " - f"salt {install_salt.version}. If the fix was backported " - f"earlier, lower LIBYAML_FIX_LANDED_IN in this test." + "salt.utils.yamlloader.BaseLoader unexpectedly resolves to " + "yaml.CSafeLoader in the previous-release onedir. If PR #69950 " + "was backported earlier than 3006.28, drop this test's downgrade " + "branch." )