diff --git a/easybuild/framework/easyconfig/easyconfig.py b/easybuild/framework/easyconfig/easyconfig.py index 4665a1d1e8..38fd21d360 100644 --- a/easybuild/framework/easyconfig/easyconfig.py +++ b/easybuild/framework/easyconfig/easyconfig.py @@ -2207,6 +2207,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 @@ -2226,7 +2242,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: @@ -2281,7 +2297,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 21a6066557..c790ac384c 100644 --- a/test/framework/easyconfig.py +++ b/test/framework/easyconfig.py @@ -5187,6 +5187,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 d46e755ebc..9aa728ce59 100644 --- a/test/framework/toy_build.py +++ b/test/framework/toy_build.py @@ -3234,7 +3234,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 @@ -3319,7 +3319,10 @@ def post_run_shell_cmd_hook(cmd, *args, **kwargs): # - 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 pre-configure: toy.source: True @@ -3329,10 +3332,10 @@ def post_run_shell_cmd_hook(cmd, *args, **kwargs): in post-install hook for toy v0.0 bin, lib in module-write hook hook for {mod_name} - 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 installing of extension bar is done!