diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index ac4345cc85..13e0f621f2 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -2234,6 +2234,22 @@ def resolve_template(value, tmpl_dict, expect_resolved=True): return value +def _copy_ec_dict(easyconfig): + """Copy an easyconfig dict as (initially) parsed""" + ec = easyconfig.pop('ec') # Don't copy this via deepcopy + try: + new_easyconfig = copy.deepcopy(easyconfig) # Copy the rest of the dict + finally: + easyconfig['ec'] = ec # Put back + new_easyconfig['ec'] = ec.copy() + return new_easyconfig + + +def _copy_ec_dicts(easyconfigs): + """Copy list of easyconfig dicts as (initially) parsed""" + return [_copy_ec_dict(ec) for ec in easyconfigs] + + def process_easyconfig(path, build_specs=None, validate=True, parse_only=False, hidden=None): """ Process easyconfig, returning some information for each block @@ -2253,7 +2269,7 @@ def process_easyconfig(path, build_specs=None, validate=True, parse_only=False, if not build_specs: cache_key = (path, validate, hidden, parse_only) if cache_key in _easyconfigs_cache: - return [e.copy() for e in _easyconfigs_cache[cache_key]] + return _copy_ec_dicts(_easyconfigs_cache[cache_key]) easyconfigs = [] for spec in blocks: @@ -2308,7 +2324,7 @@ def process_easyconfig(path, build_specs=None, validate=True, parse_only=False, easyconfig['dependencies'].append(tc) if cache_key is not None: - _easyconfigs_cache[cache_key] = [e.copy() for e in easyconfigs] + _easyconfigs_cache[cache_key] = _copy_ec_dicts(easyconfigs) return easyconfigs diff --git a/test/framework/easyconfig.py b/test/framework/easyconfig.py index 7a07eab449..8c76831283 100644 --- a/test/framework/easyconfig.py +++ b/test/framework/easyconfig.py @@ -5276,6 +5276,24 @@ def test_easyconfigs_caches(self): self.assertIsInstance(ec2['ec'].toolchain, SystemToolchain) self.assertTrue(os.path.samefile(ec2['ec'].path, toy_ec)) + # Returned easyconfigs are independent as-if there was no caching + ec3 = process_easyconfig(toy_ec)[0] + ec3['ec']['name'] = 'newname' + ec3['ec']['version'] = '99.1234' + ec3['spec'] = 'non-existing.eb' + ec3['dependencies'].append('Dummy') + self.assertEqual(ec3['ec'].name, 'newname') + self.assertEqual(ec3['ec'].version, '99.1234') + self.assertEqual(ec3['spec'], 'non-existing.eb') + self.assertEqual(ec3['dependencies'], ['Dummy']) + # Neither the previously returned nor newly requested ECs are modified by the above + ec2_2 = process_easyconfig(toy_ec)[0] + for orig_ec in (ec2, ec2_2): + self.assertEqual(orig_ec['ec'].name, 'toy') + self.assertEqual(orig_ec['ec'].version, '0.0') + self.assertEqual(orig_ec['spec'], toy_ec) + self.assertEqual(orig_ec['dependencies'], []) + # also check whether easyconfigs cache works with end-to-end test args = [libtoy_ec, '--trace'] self.mock_stdout(True) diff --git a/test/framework/toy_build.py b/test/framework/toy_build.py index f9adda1df9..f42205d304 100644 --- a/test/framework/toy_build.py +++ b/test/framework/toy_build.py @@ -3663,7 +3663,7 @@ def start_hook(): print('start hook triggered') def parse_hook(ec): - print('%s %s' % (ec.name, ec.version)) + print('Parse Hook %s %s' % (ec.name, ec.version)) # print sources value to check that raw untemplated strings are exposed in parse_hook print(ec['sources']) # try appending to postinstallcmd to see whether the modification is actually picked up @@ -3762,7 +3762,10 @@ def post_build_and_install_loop_hook(ecs): # - for devel module file expected_output = textwrap.dedent(f""" start hook triggered - toy 0.0 + Parse Hook toy 0.0 + ['%(name)s-%(version)s.tar.gz'] + echo toy + Parse Hook toy 0.0 ['%(name)s-%(version)s.tar.gz'] echo toy installing 1 easyconfigs: toy/0.0 @@ -3773,10 +3776,10 @@ def post_build_and_install_loop_hook(ecs): ' gcc toy.c -o toy && copy_toy_file toy copy_of_toy' command failed (exit code 127), but I fixed it! in post-install hook for toy v0.0 bin, lib - toy 0.0 + Parse Hook toy 0.0 ['%(name)s-%(version)s.tar.gz'] echo toy - toy 0.0 + Parse Hook toy 0.0 ['%(name)s-%(version)s.tar.gz'] echo toy in module-write hook hook for {mod_name}