Skip to content

Commit 6fb973f

Browse files
committed
Review fixes
1 parent afce2f6 commit 6fb973f

File tree

6 files changed

+101
-49
lines changed

6 files changed

+101
-49
lines changed

easybuild/framework/easyconfig/easyconfig.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -809,37 +809,30 @@ def validate_iterate_opts_lists(self):
809809
Configure/build/install options specified as lists should have same length.
810810
"""
811811

812-
# Disable templating as only the existance is of interest, the actual resolved value is not
813-
prev_enable_templating = self.enable_templating
814-
self.enable_templating = False
815-
816812
# configure/build/install options may be lists, in case of an iterated build
817813
# when lists are used, they should be all of same length
818814
# list of length 1 are treated as if it were strings in EasyBlock
819815
opt_counts = []
820816
for opt in ITERATE_OPTIONS:
821817

822818
# only when builddependencies is a list of lists are we iterating over them
823-
if opt == 'builddependencies' and not all(isinstance(e, list) for e in self[opt]):
819+
if opt == 'builddependencies' and not all(isinstance(e, list) for e in self.get_ref(opt)):
824820
continue
825821

822+
opt_value = self.get(opt, None, resolve=False)
826823
# anticipate changes in available easyconfig parameters (e.g. makeopts -> buildopts?)
827-
if self.get(opt, None) is None:
828-
self.enable_templating = prev_enable_templating
824+
if opt_value is None:
829825
raise EasyBuildError("%s not available in self.cfg (anymore)?!", opt)
830826

831827
# keep track of list, supply first element as first option to handle
832-
if isinstance(self[opt], (list, tuple)):
833-
opt_counts.append((opt, len(self[opt])))
828+
if isinstance(opt_value, (list, tuple)):
829+
opt_counts.append((opt, len(opt_value)))
834830

835831
# make sure that options that specify lists have the same length
836832
list_opt_lengths = [length for (opt, length) in opt_counts if length > 1]
837833
if len(nub(list_opt_lengths)) > 1:
838-
self.enable_templating = prev_enable_templating
839834
raise EasyBuildError("Build option lists for iterated build should have same length: %s", opt_counts)
840835

841-
self.enable_templating = prev_enable_templating
842-
843836
return True
844837

845838
def start_iterating(self):
@@ -1531,12 +1524,13 @@ def __setitem__(self, key, value):
15311524
key, value)
15321525

15331526
@handle_deprecated_or_replaced_easyconfig_parameters
1534-
def get(self, key, default=None):
1527+
def get(self, key, default=None, resolve=True):
15351528
"""
15361529
Gets the value of a key in the config, with 'default' as fallback.
1530+
:param resolve: if False, disables templating via calling get_ref, else resolves template values
15371531
"""
15381532
if key in self:
1539-
return self[key]
1533+
return self[key] if resolve else self.get_ref(key)
15401534
else:
15411535
return default
15421536

easybuild/framework/extension.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from easybuild.tools.build_log import EasyBuildError, raise_nosupport
4242
from easybuild.tools.filetools import change_dir
4343
from easybuild.tools.run import run_cmd
44+
from easybuild.tools.py2vs3 import string_type
4445

4546

4647
def resolve_exts_filter_template(exts_filter, ext):
@@ -51,7 +52,7 @@ def resolve_exts_filter_template(exts_filter, ext):
5152
:return (cmd, input) as a tuple of strings
5253
"""
5354

54-
if isinstance(exts_filter, str) or len(exts_filter) != 2:
55+
if isinstance(exts_filter, string_type) or len(exts_filter) != 2:
5556
raise EasyBuildError('exts_filter should be a list or tuple of ("command","input")')
5657

5758
cmd, cmdinput = exts_filter
@@ -71,11 +72,13 @@ def resolve_exts_filter_template(exts_filter, ext):
7172
}
7273

7374
try:
74-
return (cmd % tmpldict, cmdinput % tmpldict if cmdinput else None)
75+
cmd = cmd % tmpldict
76+
cmdinput = cmdinput % tmpldict if cmdinput else None
7577
except KeyError as err:
7678
msg = "KeyError occurred on completing extension filter template: %s; "
7779
msg += "'name'/'version' keys are no longer supported, should use 'ext_name'/'ext_version' instead"
7880
raise_nosupport(msg % err, '2.0')
81+
return cmd, cmdinput
7982

8083

8184
class Extension(object):

easybuild/tools/build_log.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def raise_nosupport(msg, ver):
9595
nosupport_msg = "NO LONGER SUPPORTED since v%s: %s; see %s for more information"
9696
raise_easybuilderror(nosupport_msg, ver, msg, DEPRECATED_DOC_URL)
9797

98+
9899
class EasyBuildLog(fancylogger.FancyLogger):
99100
"""
100101
The EasyBuild logger, with its own error and exception functions.

easybuild/tools/modules.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,17 @@ def __init__(self, mod_paths=None, testing=False):
180180
if mod_paths is not None:
181181
self.set_mod_paths(mod_paths)
182182

183-
cmd_path = which(self.cmd, log_ok=False, log_error=False)
184-
# only use command path in environment variable if command in not available in $PATH
185-
if which(self.cmd) is None:
186-
if env_cmd_path is not None:
183+
if env_cmd_path:
184+
cmd_path = which(self.cmd, log_ok=False, log_error=False)
185+
# only use command path in environment variable if command in not available in $PATH
186+
if cmd_path is None:
187187
self.log.debug("Set %s command via environment variable %s: %s",
188188
self.NAME, self.COMMAND_ENVIRONMENT, self.cmd)
189189
self.cmd = env_cmd_path
190-
191-
# check whether paths obtained via $PATH and $LMOD_CMD are different
192-
elif env_cmd_path and cmd_path != env_cmd_path:
193-
self.log.debug("Different paths found for %s command '%s' via which/$PATH and $%s: %s vs %s",
194-
self.NAME, self.COMMAND, self.COMMAND_ENVIRONMENT, cmd_path, env_cmd_path)
190+
# check whether paths obtained via $PATH and $LMOD_CMD are different
191+
elif cmd_path != env_cmd_path:
192+
self.log.debug("Different paths found for %s command '%s' via which/$PATH and $%s: %s vs %s",
193+
self.NAME, self.COMMAND, self.COMMAND_ENVIRONMENT, cmd_path, env_cmd_path)
195194

196195
# make sure the module command was found
197196
if self.cmd is None:
@@ -676,7 +675,7 @@ def modulefile_path(self, mod_name, strip_ext=False):
676675
:param strip_ext: strip (.lua) extension from module fileame (if present)"""
677676
# (possible relative) path is always followed by a ':', and may be prepended by whitespace
678677
# this works for both environment modules and Lmod
679-
modpath_re = re.compile('^\s*(?P<modpath>[^/\n]*/[^\s]+):$', re.M)
678+
modpath_re = re.compile(r'^\s*(?P<modpath>[^/\n]*/[^\s]+):$', re.M)
680679
modpath = self.get_value_from_modulefile(mod_name, modpath_re)
681680

682681
if strip_ext and modpath.endswith('.lua'):
@@ -941,7 +940,7 @@ def file_join(res):
941940
"""Helper function to compose joined path."""
942941
return os.path.join(*[x.strip('"') for x in res.groups()])
943942

944-
res = re.sub('\[\s+file\s+join\s+(.*)\s+(.*)\s+\]', file_join, res)
943+
res = re.sub(r'\[\s+file\s+join\s+(.*)\s+(.*)\s+\]', file_join, res)
945944

946945
# also interpret all $env(...) parts
947946
res = re.sub(r'\$env\((?P<key>[^)]*)\)', lambda res: os.getenv(res.group('key'), ''), res)
@@ -1160,7 +1159,7 @@ def run_module(self, *args, **kwargs):
11601159
# this is required for the DEISA variant of modulecmd.tcl which is commonly used
11611160
def tweak_stdout(txt):
11621161
"""Tweak stdout before it's exec'ed as Python code."""
1163-
modulescript_regex = "^exec\s+[\"'](?P<modulescript>/tmp/modulescript_[0-9_]+)[\"']$"
1162+
modulescript_regex = r"^exec\s+[\"'](?P<modulescript>/tmp/modulescript_[0-9_]+)[\"']$"
11641163
return re.sub(modulescript_regex, r"execfile('\1')", txt)
11651164

11661165
tweak_stdout_fn = None
@@ -1368,7 +1367,7 @@ def module_wrapper_exists(self, mod_name):
13681367

13691368
# first consider .modulerc.lua with Lmod 7.8 (or newer)
13701369
if StrictVersion(self.version) >= StrictVersion('7.8'):
1371-
mod_wrapper_regex_template = '^module_version\("(?P<wrapped_mod>.*)", "%s"\)$'
1370+
mod_wrapper_regex_template = r'^module_version\("(?P<wrapped_mod>.*)", "%s"\)$'
13721371
res = super(Lmod, self).module_wrapper_exists(mod_name, modulerc_fn='.modulerc.lua',
13731372
mod_wrapper_regex_template=mod_wrapper_regex_template)
13741373

test/framework/build_log.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
from unittest import TextTestRunner
3737

3838
from easybuild.base.fancylogger import getLogger, logToFile, setLogFormat
39-
from easybuild.tools.build_log import LOGGING_FORMAT, EasyBuildError, EasyBuildLog, dry_run_msg, dry_run_warning
40-
from easybuild.tools.build_log import init_logging, print_error, print_msg, print_warning, stop_logging, time_str_since
39+
from easybuild.tools.build_log import (
40+
LOGGING_FORMAT, EasyBuildError, EasyBuildLog, dry_run_msg, dry_run_warning, init_logging, print_error, print_msg,
41+
print_warning, stop_logging, time_str_since, raise_nosupport)
4142
from easybuild.tools.filetools import read_file, write_file
4243

4344

@@ -146,9 +147,10 @@ def test_easybuildlog(self):
146147
logtxt_regex = re.compile(r'^%s' % expected_logtxt, re.M)
147148
self.assertTrue(logtxt_regex.search(logtxt), "Pattern '%s' found in %s" % (logtxt_regex.pattern, logtxt))
148149

149-
self.assertErrorRegex(EasyBuildError, "DEPRECATED \(since .*: kaput", log.deprecated, "kaput", older_ver)
150-
self.assertErrorRegex(EasyBuildError, "DEPRECATED \(since .*: 2>1", log.deprecated, "2>1", '2.0', '1.0')
151-
self.assertErrorRegex(EasyBuildError, "DEPRECATED \(since .*: 2>1", log.deprecated, "2>1", '2.0', max_ver='1.0')
150+
self.assertErrorRegex(EasyBuildError, r"DEPRECATED \(since .*: kaput", log.deprecated, "kaput", older_ver)
151+
self.assertErrorRegex(EasyBuildError, r"DEPRECATED \(since .*: 2>1", log.deprecated, "2>1", '2.0', '1.0')
152+
self.assertErrorRegex(EasyBuildError, r"DEPRECATED \(since .*: 2>1", log.deprecated, "2>1", '2.0',
153+
max_ver='1.0')
152154

153155
# wipe log so we can reuse it
154156
write_file(tmplog, '')
@@ -422,6 +424,10 @@ def test_init_logging(self):
422424

423425
stop_logging(logfile, logtostdout=True)
424426

427+
def test_raise_nosupport(self):
428+
self.assertRaisesRegexp(EasyBuildError, 'NO LONGER SUPPORTED since v42: foobar;',
429+
raise_nosupport, 'foobar', 42)
430+
425431

426432
def suite():
427433
""" returns all the testcases in this module """

test/framework/easyconfig.py

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from easybuild.framework.easyconfig.tools import categorize_files_by_type, check_sha256_checksums, dep_graph
5959
from easybuild.framework.easyconfig.tools import find_related_easyconfigs, get_paths_for, parse_easyconfigs
6060
from easybuild.framework.easyconfig.tweak import obtain_ec_for, tweak_one
61+
from easybuild.framework.extension import resolve_exts_filter_template
6162
from easybuild.toolchains.system import SystemToolchain
6263
from easybuild.tools.build_log import EasyBuildError
6364
from easybuild.tools.config import module_classes
@@ -198,7 +199,7 @@ def test_validation(self):
198199
# introduce "TypeError: format requires mapping" issue"
199200
self.contents = self.contents.replace("syntax_error'", "foo = '%(name)s %s' % version")
200201
self.prep()
201-
error_pattern = "Parsing easyconfig file failed: format requires a mapping \(line 8\)"
202+
error_pattern = r"Parsing easyconfig file failed: format requires a mapping \(line 8\)"
202203
self.assertErrorRegex(EasyBuildError, error_pattern, EasyConfig, self.eb_file)
203204

204205
def test_system_toolchain_constant(self):
@@ -854,7 +855,7 @@ def test_obtain_easyconfig(self):
854855
# hidden dependencies must be included in list of dependencies
855856
res = obtain_ec_for(specs, [self.test_prefix], None)
856857
self.assertEqual(res[0], True)
857-
error_pattern = "Hidden deps with visible module names .* not in list of \(build\)dependencies: .*"
858+
error_pattern = r"Hidden deps with visible module names .* not in list of \(build\)dependencies: .*"
858859
self.assertErrorRegex(EasyBuildError, error_pattern, EasyConfig, res[1])
859860
remove_file(res[1])
860861

@@ -1559,7 +1560,6 @@ def test_quote_str(self):
15591560
'foo': '"foo"',
15601561
'foo\'bar': '"foo\'bar"',
15611562
'foo\'bar"baz': '"""foo\'bar"baz"""',
1562-
"foo'bar\"baz": '"""foo\'bar"baz"""',
15631563
"foo\nbar": '"foo\nbar"',
15641564
'foo bar': '"foo bar"'
15651565
}
@@ -2499,9 +2499,9 @@ def test_copy_easyconfigs(self):
24992499
# verify whether copied easyconfig gets cleaned up (stripping out 'Built with' comment + build stats)
25002500
txt = read_file(copied_toy_ec)
25012501
regexs = [
2502-
"# Built with EasyBuild",
2503-
"# Build statistics",
2504-
"buildstats\s*=",
2502+
r"# Built with EasyBuild",
2503+
r"# Build statistics",
2504+
r"buildstats\s*=",
25052505
]
25062506
for regex in regexs:
25072507
regex = re.compile(regex, re.M)
@@ -2708,8 +2708,8 @@ def test_hidden_toolchain(self):
27082708
'--dry-run',
27092709
]
27102710
outtxt = self.eb_main(args, raise_error=True)
2711-
self.assertTrue(re.search('module: GCC/\.4\.9\.2', outtxt))
2712-
self.assertTrue(re.search('module: gzip/1\.6-GCC-4\.9\.2', outtxt))
2711+
self.assertTrue(re.search(r'module: GCC/\.4\.9\.2', outtxt))
2712+
self.assertTrue(re.search(r'module: gzip/1\.6-GCC-4\.9\.2', outtxt))
27132713

27142714
def test_categorize_files_by_type(self):
27152715
"""Test categorize_files_by_type"""
@@ -2853,8 +2853,8 @@ def test_verify_easyconfig_filename(self):
28532853
# incorrect spec
28542854
specs['versionsuffix'] = ''
28552855
error_pattern = "filename '%s' does not match with expected filename 'toy-0.0-gompi-2018a.eb' " % toy_ec_name
2856-
error_pattern += "\(specs: name: 'toy'; version: '0.0'; versionsuffix: ''; "
2857-
error_pattern += "toolchain name, version: 'gompi', '2018a'\)"
2856+
error_pattern += r"\(specs: name: 'toy'; version: '0.0'; versionsuffix: ''; "
2857+
error_pattern += r"toolchain name, version: 'gompi', '2018a'\)"
28582858
self.assertErrorRegex(EasyBuildError, error_pattern, verify_easyconfig_filename, toy_ec, specs)
28592859
specs['versionsuffix'] = '-test'
28602860

@@ -2863,8 +2863,8 @@ def test_verify_easyconfig_filename(self):
28632863
toy_ec = os.path.join(self.test_prefix, 'toy.eb')
28642864
write_file(toy_ec, toy_txt)
28652865
error_pattern = "filename 'toy.eb' does not match with expected filename 'toy-0.0-gompi-2018a-test.eb' "
2866-
error_pattern += "\(specs: name: 'toy'; version: '0.0'; versionsuffix: '-test'; "
2867-
error_pattern += "toolchain name, version: 'gompi', '2018a'\)"
2866+
error_pattern += r"\(specs: name: 'toy'; version: '0.0'; versionsuffix: '-test'; "
2867+
error_pattern += r"toolchain name, version: 'gompi', '2018a'\)"
28682868
self.assertErrorRegex(EasyBuildError, error_pattern, verify_easyconfig_filename, toy_ec, specs)
28692869

28702870
# incorrect file contents
@@ -2998,7 +2998,7 @@ def test_check_sha256_checksums(self):
29982998
toy_ec = os.path.join(test_ecs_dir, 't', 'toy', 'toy-0.0.eb')
29992999
toy_ec_txt = read_file(toy_ec)
30003000

3001-
checksums_regex = re.compile('^checksums = \[\[(.|\n)*\]\]', re.M)
3001+
checksums_regex = re.compile(r'^checksums = \[\[(.|\n)*\]\]', re.M)
30023002

30033003
# wipe out specified checksums, to make check fail
30043004
test_ec = os.path.join(self.test_prefix, 'toy-0.0-fail.eb')
@@ -3013,7 +3013,7 @@ def test_check_sha256_checksums(self):
30133013
self.assertTrue(res[0].startswith('Checksums missing for one or more sources/patches in toy-0.0-fail.eb'))
30143014

30153015
# test use of whitelist regex patterns: check passes because easyconfig is whitelisted by filename
3016-
for regex in ['toy-.*', '.*-0\.0-fail\.eb']:
3016+
for regex in [r'toy-.*', r'.*-0\.0-fail\.eb']:
30173017
res = check_sha256_checksums(ecs, whitelist=[regex])
30183018
self.assertFalse(res)
30193019

@@ -3033,7 +3033,7 @@ def test_check_sha256_checksums(self):
30333033
# re-test with right checksum in place
30343034
toy_sha256 = '44332000aa33b99ad1e00cbd1a7da769220d74647060a10e807b916d73ea27bc'
30353035
test_ec_txt = checksums_regex.sub('checksums = ["%s"]' % toy_sha256, toy_ec_txt)
3036-
test_ec_txt = re.sub('patches = \[(.|\n)*\]', '', test_ec_txt)
3036+
test_ec_txt = re.sub(r'patches = \[(.|\n)*\]', '', test_ec_txt)
30373037

30383038
test_ec = os.path.join(self.test_prefix, 'toy-0.0-ok.eb')
30393039
write_file(test_ec, test_ec_txt)
@@ -3557,6 +3557,55 @@ def test_unexpected_version_keys_caught(self):
35573557

35583558
self.assertRaises(EasyBuildError, EasyConfig, test_ec)
35593559

3560+
def test_resolve_exts_filter_template(self):
3561+
class TestExtension(object):
3562+
def __init__(self, values):
3563+
self.name = values['name']
3564+
self.version = values.get('version')
3565+
self.src = values.get('src')
3566+
self.options = values.get('options', {})
3567+
3568+
error_msg = 'exts_filter should be a list or tuple'
3569+
self.assertRaisesRegexp(EasyBuildError, error_msg, resolve_exts_filter_template,
3570+
'[ 1 == 1 ]', {})
3571+
self.assertRaisesRegexp(EasyBuildError, error_msg, resolve_exts_filter_template,
3572+
['[ 1 == 1 ]'], {})
3573+
self.assertRaisesRegexp(EasyBuildError, error_msg, resolve_exts_filter_template,
3574+
['[ 1 == 1 ]', 'true', 'false'], {})
3575+
3576+
test_cases = [
3577+
# Minimal case: just name
3578+
(['%(ext_name)s', None],
3579+
{'name': 'foo'},
3580+
('foo', None),
3581+
),
3582+
# Minimal case with input
3583+
(['%(ext_name)s', '>%(ext_name)s'],
3584+
{'name': 'foo'},
3585+
('foo', '>foo'),
3586+
),
3587+
# All values
3588+
(['%(ext_name)s-%(ext_version)s-%(src)s', '>%(ext_name)s-%(ext_version)s-%(src)s'],
3589+
{'name': 'foo', 'version': 42, 'src': 'bar.tgz'},
3590+
('foo-42-bar.tgz', '>foo-42-bar.tgz'),
3591+
),
3592+
# options dict is accepted
3593+
(['%(ext_name)s-%(ext_version)s-%(src)s', '>%(ext_name)s-%(ext_version)s-%(src)s'],
3594+
{'name': 'foo', 'version': 42, 'src': 'bar.tgz', 'options': {'dummy': 'value'}},
3595+
('foo-42-bar.tgz', '>foo-42-bar.tgz'),
3596+
),
3597+
# modulename overwrites name
3598+
(['%(ext_name)s-%(ext_version)s-%(src)s', '>%(ext_name)s-%(ext_version)s-%(src)s'],
3599+
{'name': 'foo', 'version': 42, 'src': 'bar.tgz', 'options': {'modulename': 'baz'}},
3600+
('baz-42-bar.tgz', '>baz-42-bar.tgz'),
3601+
),
3602+
]
3603+
for exts_filter, ext, expected_value in test_cases:
3604+
value = resolve_exts_filter_template(exts_filter, ext)
3605+
self.assertEqual(value, expected_value)
3606+
value = resolve_exts_filter_template(exts_filter, TestExtension(ext))
3607+
self.assertEqual(value, expected_value)
3608+
35603609

35613610
def suite():
35623611
""" returns all the testcases in this module """

0 commit comments

Comments
 (0)