diff options
author | Massimiliano Culpo <massimiliano.culpo@gmail.com> | 2024-03-11 20:48:21 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-11 20:48:21 +0100 |
commit | ce75adada6cd3564d42d37bef19eb35dbf368e2a (patch) | |
tree | 9fd9cb47b736a319ef3ebb18955d3537499afc0c /lib | |
parent | 24d37df1a25f0f6f61e2f12db661de6b67db75a0 (diff) | |
download | spack-ce75adada6cd3564d42d37bef19eb35dbf368e2a.tar.gz spack-ce75adada6cd3564d42d37bef19eb35dbf368e2a.tar.bz2 spack-ce75adada6cd3564d42d37bef19eb35dbf368e2a.tar.xz spack-ce75adada6cd3564d42d37bef19eb35dbf368e2a.zip |
Fix callbacks accumulation when using mixins with builders (#43100)
fixes #43097
Before this PR the behavior of mixins used together with
builders was to mask completely the callbacks defined from
the class coming later in the MRO.
Here we fix the behavior by accumulating all callbacks,
and de-duplicating them later.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/builder.py | 25 | ||||
-rw-r--r-- | lib/spack/spack/test/builder.py | 17 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/list.py | 5 |
3 files changed, 33 insertions, 14 deletions
diff --git a/lib/spack/spack/builder.py b/lib/spack/spack/builder.py index 4119062ab6..00734d5533 100644 --- a/lib/spack/spack/builder.py +++ b/lib/spack/spack/builder.py @@ -9,6 +9,8 @@ import functools import inspect from typing import List, Optional, Tuple +from llnl.util import lang + import spack.build_environment #: Builder classes, as registered by the "builder" decorator @@ -231,24 +233,27 @@ class PhaseCallbacksMeta(type): for temporary_stage in (_RUN_BEFORE, _RUN_AFTER): staged_callbacks = temporary_stage.callbacks - # We don't have callbacks in this class, move on - if not staged_callbacks: + # Here we have an adapter from an old-style package. This means there is no + # hierarchy of builders, and every callback that had to be combined between + # *Package and *Builder has been combined already by _PackageAdapterMeta + if name == "Adapter": continue - # If we are here we have callbacks. To get a complete list, get first what - # was attached to parent classes, then prepend what we have registered here. + # If we are here we have callbacks. To get a complete list, we accumulate all the + # callbacks from base classes, we deduplicate them, then prepend what we have + # registered here. # # The order should be: # 1. Callbacks are registered in order within the same class # 2. Callbacks defined in derived classes precede those defined in base # classes + callbacks_from_base = [] for base in bases: - callbacks_from_base = getattr(base, temporary_stage.attribute_name, None) - if callbacks_from_base: - break - else: - callbacks_from_base = [] - + current_callbacks = getattr(base, temporary_stage.attribute_name, None) + if not current_callbacks: + continue + callbacks_from_base.extend(current_callbacks) + callbacks_from_base = list(lang.dedupe(callbacks_from_base)) # Set the callbacks in this class and flush the temporary stage attr_dict[temporary_stage.attribute_name] = staged_callbacks[:] + callbacks_from_base del temporary_stage.callbacks[:] diff --git a/lib/spack/spack/test/builder.py b/lib/spack/spack/test/builder.py index 9a99e6ee08..4bd128c3bf 100644 --- a/lib/spack/spack/test/builder.py +++ b/lib/spack/spack/test/builder.py @@ -164,3 +164,20 @@ def test_install_time_test_callback(tmpdir, config, mock_packages, mock_stage): with open(s.package.tester.test_log_file, "r") as f: results = f.read().replace("\n", " ") assert "PyTestCallback test" in results + + +@pytest.mark.regression("43097") +@pytest.mark.usefixtures("builder_test_repository", "config") +def test_mixins_with_builders(working_env): + """Tests that run_after and run_before callbacks are accumulated correctly, + when mixins are used with builders. + """ + s = spack.spec.Spec("builder-and-mixins").concretized() + builder = spack.builder.create(s.package) + + # Check that callbacks added by the mixin are in the list + assert any(fn.__name__ == "before_install" for _, fn in builder.run_before_callbacks) + assert any(fn.__name__ == "after_install" for _, fn in builder.run_after_callbacks) + + # Check that callback from the GenericBuilder are in the list too + assert any(fn.__name__ == "sanity_check_prefix" for _, fn in builder.run_after_callbacks) diff --git a/lib/spack/spack/test/cmd/list.py b/lib/spack/spack/test/cmd/list.py index a46e690cd2..4a92504673 100644 --- a/lib/spack/spack/test/cmd/list.py +++ b/lib/spack/spack/test/cmd/list.py @@ -144,12 +144,9 @@ def test_list_repos(): os.path.join(spack.paths.repos_path, "builder.test"), ): total_pkgs = len(list().strip().split()) - mock_pkgs = len(list("-r", "builtin.mock").strip().split()) builder_pkgs = len(list("-r", "builder.test").strip().split()) + both_repos = len(list("-r", "builtin.mock", "-r", "builder.test").strip().split()) - assert builder_pkgs == 8 assert total_pkgs > mock_pkgs > builder_pkgs - - both_repos = len(list("-r", "builtin.mock", "-r", "builder.test").strip().split()) assert both_repos == total_pkgs |