Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of custom delimiter for paths in module generator #4687

Merged
merged 16 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1469,21 +1469,29 @@ def make_module_extra(self, altroot=None, altversion=None):
for (key, value) in self.cfg['modextravars'].items():
lines.append(self.module_generator.set_environment(key, value))

for (key, value) in self.cfg['modextrapaths'].items():
if isinstance(value, str):
value = [value]
elif not isinstance(value, (tuple, list)):
raise EasyBuildError("modextrapaths dict value %s (type: %s) is not a list or tuple",
value, type(value))
lines.append(self.module_generator.prepend_paths(key, value, allow_abs=self.cfg['allow_prepend_abs_path']))

for (key, value) in self.cfg['modextrapaths_append'].items():
if isinstance(value, str):
value = [value]
elif not isinstance(value, (tuple, list)):
raise EasyBuildError("modextrapaths_append dict value %s (type: %s) is not a list or tuple",
value, type(value))
lines.append(self.module_generator.append_paths(key, value, allow_abs=self.cfg['allow_append_abs_path']))
for name, prepend in [('modextrapaths', True), ('modextrapaths_append', False)]:
allow_abs = self.cfg['allow_prepend_abs_path'] if prepend else self.cfg['allow_append_abs_path']

for (key, value) in self.cfg[name].items():
if not isinstance(value, (tuple, list, dict, str)):
raise EasyBuildError(f'{name} dict value "{value}" (type {type(value)}) is not a '
'list, dict or str')
elif isinstance(value, dict):
if 'paths' not in value or 'delimiter' not in value:
raise EasyBuildError(f'{name} dict value "{value}" must contain "paths" and "delimiter"')

paths = value['paths']
delim = value['delimiter']
if not isinstance(paths, (list, str)):
raise EasyBuildError('modextrapaths dict value "{value}" paths must be list or str')
if not isinstance(delim, str):
raise EasyBuildError('modextrapaths dict value "{value}" delimiter must be a str')
lines.append(self.module_generator.update_paths(key, paths, prepend=prepend, delim=delim,
allow_abs=allow_abs))
else:
if isinstance(value, str):
value = [value]
lines.append(self.module_generator.update_paths(key, value, prepend=prepend, allow_abs=allow_abs))

Micket marked this conversation as resolved.
Show resolved Hide resolved
# add lines to update $PYTHONPATH or $EBPYTHONPREFIXES
lines.extend(self.make_module_pythonpath())
Expand Down
36 changes: 25 additions & 11 deletions easybuild/tools/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,33 +235,37 @@ def _filter_paths(self, key, paths):
filtered_paths = None
return filtered_paths

def append_paths(self, key, paths, allow_abs=False, expand_relpaths=True):
def append_paths(self, key, paths, allow_abs=False, expand_relpaths=True, delim=':'):
"""
Generate append-path statements for the given list of paths.

:param key: environment variable to append paths to
:param paths: list of paths to append
:param allow_abs: allow providing of absolute paths
:param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir)
:param delim: delimiter used between paths
"""
paths = self._filter_paths(key, paths)
if paths is None:
return ''
return self.update_paths(key, paths, prepend=False, allow_abs=allow_abs, expand_relpaths=expand_relpaths)
return self.update_paths(key, paths, prepend=False, allow_abs=allow_abs, expand_relpaths=expand_relpaths,
delim=delim)

def prepend_paths(self, key, paths, allow_abs=False, expand_relpaths=True):
def prepend_paths(self, key, paths, allow_abs=False, expand_relpaths=True, delim=':'):
"""
Generate prepend-path statements for the given list of paths.

:param key: environment variable to append paths to
:param paths: list of paths to append
:param allow_abs: allow providing of absolute paths
:param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir)
:param delim: delimiter used between paths
"""
paths = self._filter_paths(key, paths)
if paths is None:
return ''
return self.update_paths(key, paths, prepend=True, allow_abs=allow_abs, expand_relpaths=expand_relpaths)
return self.update_paths(key, paths, prepend=True, allow_abs=allow_abs, expand_relpaths=expand_relpaths,
delim=delim)

def _modulerc_check_module_version(self, module_version):
"""
Expand Down Expand Up @@ -552,15 +556,16 @@ def unload_module(self, mod_name):
"""
raise NotImplementedError

def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True):
def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True, delim=':'):
"""
Generate prepend-path or append-path statements for the given list of paths.

:param key: environment variable to prepend/append paths to
:param paths: list of paths to prepend
:param paths: list of paths to prepend/append
:param prepend: whether to prepend (True) or append (False) paths
:param allow_abs: allow providing of absolute paths
:param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir)
:param delim: delimiter used between paths
"""
raise NotImplementedError

Expand Down Expand Up @@ -970,15 +975,16 @@ def msg_on_unload(self, msg):
print_cmd = "puts stderr %s" % quote_str(msg, tcl=True)
return '\n'.join(['', self.conditional_statement("module-info mode unload", print_cmd, indent=False)])

def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True):
def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True, delim=':'):
"""
Generate prepend-path or append-path statements for the given list of paths.

:param key: environment variable to prepend/append paths to
:param paths: list of paths to prepend
:param paths: list of paths to prepend/append
:param prepend: whether to prepend (True) or append (False) paths
:param allow_abs: allow providing of absolute paths
:param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir)
:param delim: delimiter used between paths
"""
if prepend:
update_type = 'prepend'
Expand Down Expand Up @@ -1010,7 +1016,10 @@ def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpath
else:
abspaths.append(path)

statements = ['%s-path\t%s\t\t%s\n' % (update_type, key, p) for p in abspaths]
if delim != ':':
statements = ['%s-path -d "%s"\t%s\t\t%s\n' % (update_type, delim, key, p) for p in abspaths]
else:
statements = ['%s-path\t%s\t\t%s\n' % (update_type, key, p) for p in abspaths]
lexming marked this conversation as resolved.
Show resolved Hide resolved
return ''.join(statements)

def set_alias(self, key, value):
Expand Down Expand Up @@ -1161,6 +1170,7 @@ class ModuleGeneratorLua(ModuleGenerator):

PATH_JOIN_TEMPLATE = 'pathJoin(root, "%s")'
UPDATE_PATH_TEMPLATE = '%s_path("%s", %s)'
UPDATE_PATH_TEMPLATE_DELIM = '%s_path("%s", %s, "%s")'

START_STR = '[==['
END_STR = ']==]'
Expand Down Expand Up @@ -1422,7 +1432,7 @@ def modulerc(self, module_version=None, filepath=None, modulerc_txt=None):
return super(ModuleGeneratorLua, self).modulerc(module_version=module_version, filepath=filepath,
modulerc_txt=modulerc_txt)

def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True):
def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True, delim=':'):
"""
Generate prepend_path or append_path statements for the given list of paths

Expand All @@ -1431,6 +1441,7 @@ def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpath
:param prepend: whether to prepend (True) or append (False) paths
:param allow_abs: allow providing of absolute paths
:param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir)
:param delim: delimiter used between paths
"""
if prepend:
update_type = 'prepend'
Expand Down Expand Up @@ -1463,7 +1474,10 @@ def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpath
else:
abspaths.append('root')

statements = [self.UPDATE_PATH_TEMPLATE % (update_type, key, p) for p in abspaths]
if delim != ':':
statements = [self.UPDATE_PATH_TEMPLATE_DELIM % (update_type, key, p, delim) for p in abspaths]
else:
statements = [self.UPDATE_PATH_TEMPLATE % (update_type, key, p) for p in abspaths]
statements.append('')
return '\n'.join(statements)

Expand Down
50 changes: 35 additions & 15 deletions test/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,7 @@ def test_make_module_step(self):
modextrapaths = {
'PATH': ('xbin', 'pibin'),
'CPATH': 'pi/include',
'TCLLIBPATH': {'paths': 'pi', 'delimiter': ' '},
}
modextrapaths_append = {'APPEND_PATH': 'append_path'}
self.contents = '\n'.join([
Expand Down Expand Up @@ -1333,28 +1334,47 @@ def test_make_module_step(self):
self.assertTrue(regex.search(txt), "Pattern %s found in %s" % (regex.pattern, txt))

for (key, vals) in modextrapaths.items():
if isinstance(vals, str):
vals = [vals]
for val in vals:
if get_module_syntax() == 'Tcl':
regex = re.compile(r'^prepend-path\s+%s\s+\$root/%s$' % (key, val), re.M)
elif get_module_syntax() == 'Lua':
regex = re.compile(r'^prepend_path\("%s", pathJoin\(root, "%s"\)\)$' % (key, val), re.M)
else:
self.fail("Unknown module syntax: %s" % get_module_syntax())
self.assertTrue(regex.search(txt), "Pattern %s found in %s" % (regex.pattern, txt))
# Check for duplicates
num_prepends = len(regex.findall(txt))
self.assertEqual(num_prepends, 1, "Expected exactly 1 %s command in %s" % (regex.pattern, txt))
if isinstance(vals, dict):
delim = vals['delimiter']
paths = vals['paths']
if isinstance(paths, str):
paths = [paths]

for val in paths:
if get_module_syntax() == 'Tcl':
regex = re.compile(fr'^prepend-path\s+-d\s+"{delim}"\s+{key}\s+\$root/{val}$', re.M)
elif get_module_syntax() == 'Lua':
regex = re.compile(fr'^prepend_path\("{key}", pathJoin\(root, "{val}"\), "{delim}"\)$', re.M)
else:
self.fail("Unknown module syntax: %s" % get_module_syntax())
self.assertTrue(regex.search(txt), "Pattern %s found in %s" % (regex.pattern, txt))
# Check for duplicates
num_prepends = len(regex.findall(txt))
self.assertEqual(num_prepends, 1, "Expected exactly 1 %s command in %s" % (regex.pattern, txt))
else:
if isinstance(vals, str):
vals = [vals]

for val in vals:
if get_module_syntax() == 'Tcl':
regex = re.compile(fr'^prepend-path\s+{key}\s+\$root/{val}$', re.M)
elif get_module_syntax() == 'Lua':
regex = re.compile(fr'^prepend_path\("{key}", pathJoin\(root, "{val}"\)\)$', re.M)
else:
self.fail("Unknown module syntax: %s" % get_module_syntax())
self.assertTrue(regex.search(txt), "Pattern %s found in %s" % (regex.pattern, txt))
# Check for duplicates
num_prepends = len(regex.findall(txt))
self.assertEqual(num_prepends, 1, "Expected exactly 1 %s command in %s" % (regex.pattern, txt))

for (key, vals) in modextrapaths_append.items():
if isinstance(vals, str):
vals = [vals]
for val in vals:
if get_module_syntax() == 'Tcl':
regex = re.compile(r'^append-path\s+%s\s+\$root/%s$' % (key, val), re.M)
regex = re.compile(r'^append-path\s+(-d ".")?%s\s+\$root/%s$' % (key, val), re.M)
elif get_module_syntax() == 'Lua':
regex = re.compile(r'^append_path\("%s", pathJoin\(root, "%s"\)\)$' % (key, val), re.M)
regex = re.compile(r'^append_path\("%s", pathJoin\(root, "%s"\)(, ".")?\)$' % (key, val), re.M)
else:
self.fail("Unknown module syntax: %s" % get_module_syntax())
self.assertTrue(regex.search(txt), "Pattern %s found in %s" % (regex.pattern, txt))
Expand Down
Loading