Skip to content

Commit 6e77604

Browse files
Merge pull request #3099 from Micket/consistent_module_paths
Fix inconsistent module path usage that leads to repeated reloading in HMNS
2 parents d037ed6 + f4dc3a0 commit 6e77604

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

easybuild/tools/modules.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ def set_mod_paths(self, mod_paths=None):
342342
# make sure we don't have the same path twice, using nub
343343
if mod_paths is None:
344344
# no paths specified, so grab list of (existing) module paths from $MODULEPATH
345-
self.mod_paths = [p for p in nub(curr_module_paths()) if os.path.exists(p)]
345+
self.mod_paths = nub(curr_module_paths())
346346
else:
347347
for mod_path in nub(mod_paths):
348348
self.prepend_module_path(mod_path, set_mod_paths=False)
@@ -1475,8 +1475,8 @@ def curr_module_paths():
14751475
"""
14761476
Return a list of current module paths.
14771477
"""
1478-
# avoid empty entries, which don't make any sense
1479-
return [p for p in os.environ.get('MODULEPATH', '').split(':') if p]
1478+
# avoid empty or nonexistent paths, which don't make any sense
1479+
return [p for p in os.environ.get('MODULEPATH', '').split(':') if p and os.path.exists(p)]
14801480

14811481

14821482
def mk_module_path(paths):

test/framework/modules.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,106 @@ def test_load(self):
282282
self.assertEqual(os.environ.get('EBROOTGCC'), None)
283283
self.assertFalse(loaded_modules[-1] == 'GCC/6.4.0-2.28')
284284

285+
def test_curr_module_paths(self):
286+
"""Test for curr_module_paths function."""
287+
288+
# first, create a couple of (empty) directories to use as entries in $MODULEPATH
289+
test1 = os.path.join(self.test_prefix, 'test1')
290+
mkdir(test1)
291+
test2 = os.path.join(self.test_prefix, 'test2')
292+
mkdir(test2)
293+
test3 = os.path.join(self.test_prefix, 'test3')
294+
mkdir(test3)
295+
296+
os.environ['MODULEPATH'] = ''
297+
self.assertEqual(curr_module_paths(), [])
298+
299+
os.environ['MODULEPATH'] = '%s:%s:%s' % (test1, test2, test3)
300+
self.assertEqual(curr_module_paths(), [test1, test2, test3])
301+
302+
# empty entries and non-existing directories are filtered out
303+
os.environ['MODULEPATH'] = '/doesnotexist:%s::%s:' % (test2, test1)
304+
self.assertEqual(curr_module_paths(), [test2, test1])
305+
306+
def test_check_module_path(self):
307+
"""Test ModulesTool.check_module_path() method"""
308+
309+
# first, create a couple of (empty) directories to use as entries in $MODULEPATH
310+
test1 = os.path.join(self.test_prefix, 'test1')
311+
mkdir(test1)
312+
test2 = os.path.join(self.test_prefix, 'test2')
313+
mkdir(test2)
314+
test3 = os.path.join(self.test_prefix, 'test3')
315+
mkdir(test3)
316+
317+
os.environ['MODULEPATH'] = test1
318+
319+
modtool = modules_tool()
320+
321+
# directory where modules are installed based on current configuration is automatically added in front
322+
mod_install_dir = os.path.join(self.test_installpath, 'modules', 'all')
323+
self.assertEqual(modtool.mod_paths, [mod_install_dir, test1])
324+
325+
# if mod_paths is reset, it can be restored using check_module_path
326+
modtool.mod_paths = None
327+
modtool.check_module_path()
328+
self.assertEqual(modtool.mod_paths, [mod_install_dir, test1])
329+
330+
# no harm done with multiple subsequent calls
331+
modtool.check_module_path()
332+
self.assertEqual(modtool.mod_paths, [mod_install_dir, test1])
333+
334+
# if $MODULEPATH is tweaked, mod_paths and $MODULEPATH can be corrected with check_module_path
335+
os.environ['MODULEPATH'] = test2
336+
modtool.check_module_path()
337+
self.assertEqual(modtool.mod_paths, [mod_install_dir, test1, test2])
338+
self.assertEqual(os.environ['MODULEPATH'], mod_install_dir + ':' + test1 + ':' + test2)
339+
340+
# check behaviour if non-existing directories are included in $MODULEPATH
341+
os.environ['MODULEPATH'] = '%s:/does/not/exist:%s' % (test3, test2)
342+
modtool.check_module_path()
343+
# non-existing dir is filtered from mod_paths, but stays in $MODULEPATH
344+
self.assertEqual(modtool.mod_paths, [mod_install_dir, test1, test3, test2])
345+
self.assertEqual(os.environ['MODULEPATH'], ':'.join([mod_install_dir, test1, test3, '/does/not/exist', test2]))
346+
347+
def test_check_module_path_hmns(self):
348+
"""Test behaviour of check_module_path with HierarchicalMNS."""
349+
350+
# to verify that https://github.com/easybuilders/easybuild-framework/issues/3084 is fixed
351+
# (see also https://github.com/easybuilders/easybuild-framework/issues/2226);
352+
# this bug can be triggered by having at least one non-existing directory in $MODULEPATH,
353+
# and using HierarchicalMNS
354+
355+
os.environ['EASYBUILD_MODULE_NAMING_SCHEME'] = 'HierarchicalMNS'
356+
init_config()
357+
358+
top_mod_dir = os.path.join(self.test_installpath, 'modules', 'all')
359+
core_mod_dir = os.path.join(top_mod_dir, 'Core')
360+
mkdir(core_mod_dir, parents=True)
361+
362+
doesnotexist = os.path.join(self.test_prefix, 'doesnotexist')
363+
self.assertFalse(os.path.exists(doesnotexist))
364+
365+
os.environ['MODULEPATH'] = '%s:%s' % (core_mod_dir, doesnotexist)
366+
modtool = modules_tool()
367+
368+
self.assertEqual(modtool.mod_paths, [os.path.dirname(core_mod_dir), core_mod_dir])
369+
self.assertEqual(os.environ['MODULEPATH'], '%s:%s:%s' % (top_mod_dir, core_mod_dir, doesnotexist))
370+
371+
# hack prepend_module_path to make sure it's not called again if check_module_path is called again;
372+
# prepend_module_path is fairly expensive, so should be avoided,
373+
# see https://github.com/easybuilders/easybuild-framework/issues/3084
374+
def broken_prepend_module_path(*args, **kwargs):
375+
raise EasyBuildError("broken prepend_module_path")
376+
377+
modtool.prepend_module_path = broken_prepend_module_path
378+
379+
# if this doesn't trigger a raised error from the hacked prepend_module_path, the bug is fixed
380+
modtool.check_module_path()
381+
382+
self.assertEqual(modtool.mod_paths, [os.path.dirname(core_mod_dir), core_mod_dir])
383+
self.assertEqual(os.environ['MODULEPATH'], '%s:%s:%s' % (top_mod_dir, core_mod_dir, doesnotexist))
384+
285385
def test_prepend_module_path(self):
286386
"""Test prepend_module_path method."""
287387
test_path = tempfile.mkdtemp(prefix=self.test_prefix)

0 commit comments

Comments
 (0)