Skip to content

Commit df616c1

Browse files
Merge pull request #3692 from boegel/pure_ec_fix
make get_config_obj return a copy rather than a reference
2 parents 6b3f6f9 + ecaedc4 commit df616c1

File tree

5 files changed

+71
-3
lines changed

5 files changed

+71
-3
lines changed

easybuild/framework/easyconfig/format/one.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
:author: Stijn De Weirdt (Ghent University)
3131
:author: Kenneth Hoste (Ghent University)
3232
"""
33+
import copy
3334
import os
3435
import pprint
3536
import re
@@ -129,7 +130,15 @@ def get_config_dict(self):
129130
if spec_tc_version is not None and not spec_tc_version == tc_version:
130131
raise EasyBuildError('Requested toolchain version %s not available, only %s', spec_tc_version, tc_version)
131132

132-
return cfg
133+
# avoid passing anything by reference, so next time get_config_dict is called
134+
# we can be sure we return a dictionary that correctly reflects the contents of the easyconfig file;
135+
# we can't use copy.deepcopy() directly because in Python 2 copying the (irrelevant) __builtins__ key fails...
136+
cfg_copy = {}
137+
for key in cfg:
138+
if key != '__builtins__':
139+
cfg_copy[key] = copy.deepcopy(cfg[key])
140+
141+
return cfg_copy
133142

134143
def parse(self, txt):
135144
"""

easybuild/framework/easyconfig/format/yeb.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
:author: Caroline De Brouwer (Ghent University)
3030
:author: Kenneth Hoste (Ghent University)
3131
"""
32-
32+
import copy
3333
import os
3434
import platform
3535
from distutils.version import LooseVersion
@@ -91,7 +91,9 @@ def get_config_dict(self):
9191
"""
9292
Return parsed easyconfig as a dictionary, based on specified arguments.
9393
"""
94-
return self.parsed_yeb
94+
# avoid passing anything by reference, so next time get_config_dict is called
95+
# we can be sure we return a dictionary that correctly reflects the contents of the easyconfig file
96+
return copy.deepcopy(self.parsed_yeb)
9597

9698
@only_if_module_is_available('yaml')
9799
def parse(self, txt):

test/framework/easyconfig.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,6 +4401,32 @@ def test_recursive_module_unload(self):
44014401
fail_msg = "Pattern '%s' should not be found in: %s" % (recursive_unload_regex.pattern, modtxt)
44024402
self.assertFalse(recursive_unload_regex.search(modtxt), fail_msg)
44034403

4404+
def test_pure_ec(self):
4405+
"""
4406+
Test whether we can get a 'pure' view on the easyconfig file,
4407+
which correctly reflects what's defined in the easyconfig file.
4408+
"""
4409+
test_ecs_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
4410+
toy_ec = EasyConfig(os.path.join(test_ecs_dir, 't', 'toy', 'toy-0.0.eb'))
4411+
4412+
ec_dict = toy_ec.parser.get_config_dict()
4413+
self.assertEqual(ec_dict.get('version'), '0.0')
4414+
self.assertEqual(ec_dict.get('sources'), ['%(name)s-%(version)s.tar.gz'])
4415+
self.assertEqual(ec_dict.get('exts_default_options'), None)
4416+
self.assertEqual(ec_dict.get('sanity_check_paths'), {'dirs': ['bin'], 'files': [('bin/yot', 'bin/toy')]})
4417+
4418+
# manipulating easyconfig parameter values should not affect the result of parser.get_config_dict()
4419+
with toy_ec.disable_templating():
4420+
toy_ec['version'] = '1.2.3'
4421+
toy_ec['sources'].append('test.tar.gz')
4422+
toy_ec['sanity_check_paths']['files'].append('bin/foobar.exe')
4423+
4424+
ec_dict_bis = toy_ec.parser.get_config_dict()
4425+
self.assertEqual(ec_dict_bis.get('version'), '0.0')
4426+
self.assertEqual(ec_dict_bis.get('sources'), ['%(name)s-%(version)s.tar.gz'])
4427+
self.assertEqual(ec_dict_bis.get('exts_default_options'), None)
4428+
self.assertEqual(ec_dict.get('sanity_check_paths'), {'dirs': ['bin'], 'files': [('bin/yot', 'bin/toy')]})
4429+
44044430

44054431
def suite():
44064432
""" returns all the testcases in this module """

test/framework/easyconfigparser.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ def test_v10(self):
5959
self.assertEqual(ec['name'], 'GCC')
6060
self.assertEqual(ec['version'], '4.6.3')
6161

62+
# changes to this dict should not affect the return value of the next call to get_config_dict
63+
fn = 'test.tar.gz'
64+
ec['sources'].append(fn)
65+
66+
ec_bis = ecp.get_config_dict()
67+
self.assertTrue(fn in ec['sources'])
68+
self.assertFalse(fn in ec_bis['sources'])
69+
6270
def test_v20(self):
6371
"""Test parsing of easyconfig in format v2."""
6472
# hard enable experimental
@@ -81,6 +89,14 @@ def test_v20(self):
8189
self.assertEqual(ec['name'], 'GCC')
8290
self.assertEqual(ec['version'], '4.6.2')
8391

92+
# changes to this dict should not affect the return value of the next call to get_config_dict
93+
fn = 'test.tar.gz'
94+
ec['sources'].append(fn)
95+
96+
ec_bis = ecp.get_config_dict()
97+
self.assertTrue(fn in ec['sources'])
98+
self.assertFalse(fn in ec_bis['sources'])
99+
84100
# restore
85101
easybuild.tools.build_log.EXPERIMENTAL = orig_experimental
86102

test/framework/yeb.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,21 @@ def test_parse_yeb(self):
110110

111111
self.assertEqual(yeb_val, eb_val)
112112

113+
def test_yeb_get_config_obj(self):
114+
"""Test get_config_dict method."""
115+
testdir = os.path.dirname(os.path.abspath(__file__))
116+
test_yeb_easyconfigs = os.path.join(testdir, 'easyconfigs', 'yeb')
117+
ec = EasyConfig(os.path.join(test_yeb_easyconfigs, 'toy-0.0.yeb'))
118+
ecdict = ec.parser.get_config_dict()
119+
120+
# changes to this dict should not affect the return value of the next call to get_config_dict
121+
fn = 'test.tar.gz'
122+
ecdict['sources'].append(fn)
123+
124+
ecdict_bis = ec.parser.get_config_dict()
125+
self.assertTrue(fn in ecdict['sources'])
126+
self.assertFalse(fn in ecdict_bis['sources'])
127+
113128
def test_is_yeb_format(self):
114129
""" Test is_yeb_format function """
115130
testdir = os.path.dirname(os.path.abspath(__file__))

0 commit comments

Comments
 (0)