Skip to content

Commit 2945e38

Browse files
committed
Rename SOURCE_STEP to EXTRACT_STEP
Adjust tests to take that into account and add tests to check that the old name causes a proper error. Add such error handling for the `skipsteps` EC parameter. Split up `test_skip` into more focused sub tests.
1 parent fc7816c commit 2945e38

File tree

6 files changed

+103
-62
lines changed

6 files changed

+103
-62
lines changed

easybuild/framework/easyblock.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@
8585
from easybuild.tools.filetools import get_cwd, get_source_tarball_from_git, is_alt_pypi_url
8686
from easybuild.tools.filetools import is_binary, is_sha256_checksum, mkdir, move_file, move_logs, read_file, remove_dir
8787
from easybuild.tools.filetools import remove_file, remove_lock, verify_checksum, weld_paths, write_file, symlink
88-
from easybuild.tools.hooks import BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EXTENSIONS_STEP, FETCH_STEP, INSTALL_STEP
89-
from easybuild.tools.hooks import MODULE_STEP, MODULE_WRITE, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTITER_STEP
90-
from easybuild.tools.hooks import POSTPROC_STEP, PREPARE_STEP, READY_STEP, SANITYCHECK_STEP, SOURCE_STEP
91-
from easybuild.tools.hooks import SINGLE_EXTENSION, TEST_STEP, TESTCASES_STEP, load_hooks, run_hook
88+
from easybuild.tools.hooks import (
89+
BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EXTENSIONS_STEP, EXTRACT_STEP, FETCH_STEP, INSTALL_STEP, MODULE_STEP,
90+
MODULE_WRITE, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTITER_STEP, POSTPROC_STEP, PREPARE_STEP, READY_STEP,
91+
SANITYCHECK_STEP, SINGLE_EXTENSION, TEST_STEP, TESTCASES_STEP, load_hooks, run_hook,
92+
)
9293
from easybuild.tools.run import RunShellCmdError, raise_run_shell_cmd_error, run_shell_cmd
9394
from easybuild.tools.jenkins import write_to_xml
9495
from easybuild.tools.module_generator import ModuleGeneratorLua, ModuleGeneratorTcl, module_generator, dependencies_for
@@ -4051,7 +4052,7 @@ def install_step_spec(initial):
40514052
# format for step specifications: (step_name, description, list of functions, skippable)
40524053

40534054
# core steps that are part of the iterated loop
4054-
extract_step_spec = (SOURCE_STEP, "unpacking", [lambda x: x.extract_step], True)
4055+
extract_step_spec = (EXTRACT_STEP, "unpacking", [lambda x: x.extract_step], True)
40554056
patch_step_spec = (PATCH_STEP, 'patching', [lambda x: x.patch_step], True)
40564057
prepare_step_spec = (PREPARE_STEP, 'preparing', [lambda x: x.prepare_step], False)
40574058
configure_step_spec = (CONFIGURE_STEP, 'configuring', [lambda x: x.configure_step], True)

easybuild/framework/easyconfig/easyconfig.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
from easybuild.tools.filetools import convert_name, copy_file, create_index, decode_class_name, encode_class_name
7373
from easybuild.tools.filetools import find_backup_name_candidate, find_easyconfigs, load_index
7474
from easybuild.tools.filetools import read_file, write_file
75-
from easybuild.tools.hooks import PARSE, load_hooks, run_hook
75+
from easybuild.tools.hooks import STEP_NAMES, PARSE, load_hooks, run_hook
7676
from easybuild.tools.module_naming_scheme.mns import DEVEL_MODULE_SUFFIX
7777
from easybuild.tools.module_naming_scheme.utilities import avail_module_naming_schemes, det_full_ec_version
7878
from easybuild.tools.module_naming_scheme.utilities import det_hidden_modname, is_valid_module_name
@@ -858,9 +858,21 @@ def validate(self, check_osdeps=True):
858858
self.log.info("Not checking OS dependencies")
859859

860860
self.log.info("Checking skipsteps")
861-
if not isinstance(self._config['skipsteps'][0], (list, tuple,)):
861+
skipsteps = self._config['skipsteps'][0]
862+
if not isinstance(skipsteps, (list, tuple,)):
862863
raise EasyBuildError('Invalid type for skipsteps. Allowed are list or tuple, got %s (%s)',
863-
type(self._config['skipsteps'][0]), self._config['skipsteps'][0])
864+
type(skipsteps), skipsteps)
865+
unknown_step_names = [step for step in skipsteps if step not in STEP_NAMES]
866+
if unknown_step_names:
867+
error_lines = ["Found one or more unknown step names in skipsteps:"]
868+
for step in unknown_step_names:
869+
error_lines.append("* %s" % step)
870+
# try to find close match, may be just a typo in the hook name
871+
close_matches = difflib.get_close_matches(step, STEP_NAMES, 2, 0.8)
872+
if close_matches:
873+
error_lines[-1] += " (did you mean %s?)" % ', or '.join("'%s'" % s for s in close_matches)
874+
raise EasyBuildError('\n'.join(error_lines))
875+
864876

865877
self.log.info("Checking build option lists")
866878
self.validate_iterate_opts_lists()

easybuild/tools/hooks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
PREPARE_STEP = 'prepare'
5656
READY_STEP = 'ready'
5757
SANITYCHECK_STEP = 'sanitycheck'
58-
SOURCE_STEP = 'source'
58+
EXTRACT_STEP = 'extract'
5959
TEST_STEP = 'test'
6060
TESTCASES_STEP = 'testcases'
6161

@@ -77,7 +77,7 @@
7777
HOOK_SUFF = '_hook'
7878

7979
# list of names for steps in installation procedure (in order of execution)
80-
STEP_NAMES = [FETCH_STEP, READY_STEP, SOURCE_STEP, PATCH_STEP, PREPARE_STEP, CONFIGURE_STEP, BUILD_STEP, TEST_STEP,
80+
STEP_NAMES = [FETCH_STEP, READY_STEP, EXTRACT_STEP, PATCH_STEP, PREPARE_STEP, CONFIGURE_STEP, BUILD_STEP, TEST_STEP,
8181
INSTALL_STEP, EXTENSIONS_STEP, POSTITER_STEP, POSTPROC_STEP, SANITYCHECK_STEP, CLEANUP_STEP, MODULE_STEP,
8282
PERMISSIONS_STEP, PACKAGE_STEP, TESTCASES_STEP]
8383

easybuild/tools/options.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
from easybuild.base import fancylogger # build_log should always stay there, to ensure EasyBuildLog
5252
from easybuild.base.fancylogger import setLogLevel
5353
from easybuild.base.generaloption import GeneralOption
54-
from easybuild.framework.easyblock import MODULE_ONLY_STEPS, SOURCE_STEP, FETCH_STEP, EasyBlock
54+
from easybuild.framework.easyblock import MODULE_ONLY_STEPS, EXTRACT_STEP, FETCH_STEP, EasyBlock
5555
from easybuild.framework.easyconfig import EASYCONFIGS_PKG_SUBDIR
5656
from easybuild.framework.easyconfig.easyconfig import HAVE_AUTOPEP8
5757
from easybuild.framework.easyconfig.format.one import EB_FORMAT_EXTENSION
@@ -292,7 +292,7 @@ def basic_options(self):
292292
'skip': ("Skip existing software (useful for installing additional packages)",
293293
None, 'store_true', False, 'k'),
294294
'stop': ("Stop the installation after certain step",
295-
'choice', 'store_or_None', SOURCE_STEP, 's', all_stops),
295+
'choice', 'store_or_None', EXTRACT_STEP, 's', all_stops),
296296
'strict': ("Set strictness level", 'choice', 'store', WARN, strictness_options),
297297
})
298298

test/framework/hooks.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"""
3030
import os
3131
import sys
32+
import textwrap
3233
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered, init_config
3334
from unittest import TextTestRunner
3435

@@ -280,22 +281,24 @@ def test_verify_hooks(self):
280281
self.assertEqual(verify_hooks(hooks), None)
281282

282283
test_broken_hooks_pymod = os.path.join(self.test_prefix, 'test_broken_hooks.py')
283-
test_hooks_txt = '\n'.join([
284-
'',
285-
'def there_is_no_such_hook():',
286-
' pass',
287-
'def stat_hook(self):',
288-
' pass',
289-
'def post_source_hook(self):',
290-
' pass',
291-
'def install_hook(self):',
292-
' pass',
293-
])
284+
test_hooks_txt = textwrap.dedent("""
285+
def there_is_no_such_hook():
286+
pass
287+
def stat_hook(self):
288+
pass
289+
def post_source_hook(self):
290+
pass
291+
def post_extract_hook(self):
292+
pass
293+
def install_hook(self):
294+
pass
295+
""")
294296

295297
write_file(test_broken_hooks_pymod, test_hooks_txt)
296298

297299
error_msg_pattern = r"Found one or more unknown hooks:\n"
298300
error_msg_pattern += r"\* install_hook \(did you mean 'pre_install_hook', or 'post_install_hook'\?\)\n"
301+
error_msg_pattern += r"\* post_source_hook \(did you mean .*'\?\)\n"
299302
error_msg_pattern += r"\* stat_hook \(did you mean 'start_hook'\?\)\n"
300303
error_msg_pattern += r"\* there_is_no_such_hook\n\n"
301304
error_msg_pattern += r"Run 'eb --avail-hooks' to get an overview of known hooks"

test/framework/options.py

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ def test_force(self):
296296

297297
def test_skip(self):
298298
"""Test skipping installation of module (--skip, -k)."""
299-
300299
# use toy-0.0.eb easyconfig file that comes with the tests
301300
topdir = os.path.abspath(os.path.dirname(__file__))
302301
toy_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
@@ -335,13 +334,11 @@ def test_skip(self):
335334
with self.mocked_stdout_stderr():
336335
outtxt = self.eb_main(args, do_build=True, verbose=True)
337336

338-
found_msg = "Module toy/1.2.3.4.5.6.7.8.9 found."
339-
found = re.search(found_msg, outtxt)
340-
self.assertTrue(not found, "Module found message not there with --skip for non-existing modules: %s" % outtxt)
337+
self.assertNotIn("Module toy/1.2.3.4.5.6.7.8.9 found.", outtxt,
338+
"Module found message should not be there with --skip for non-existing modules")
341339

342-
not_found_msg = "No module toy/1.2.3.4.5.6.7.8.9 found. Not skipping anything."
343-
not_found = re.search(not_found_msg, outtxt)
344-
self.assertTrue(not_found, "Module not found message there with --skip for non-existing modules: %s" % outtxt)
340+
self.assertIn("No module toy/1.2.3.4.5.6.7.8.9 found. Not skipping anything.", outtxt,
341+
"Module not found message should be there with --skip for non-existing modules")
345342

346343
toy_mod_glob = os.path.join(self.test_installpath, 'modules', 'all', 'toy', '*')
347344
for toy_mod in glob.glob(toy_mod_glob):
@@ -360,29 +357,26 @@ def test_skip(self):
360357
'--force',
361358
]
362359
error_pattern = "Sanity check failed: no file found at 'bin/nosuchfile'"
363-
with self.mocked_stdout_stderr():
364-
self.assertErrorRegex(EasyBuildError, error_pattern, self.eb_main, args, do_build=True, raise_error=True)
360+
self.assertErrorRegex(EasyBuildError, error_pattern, self.mocked_main, args, do_build=True, raise_error=True)
365361

366-
# check use of skipsteps to skip sanity check
367-
test_ec_txt += "\nskipsteps = ['sanitycheck']\n"
368-
write_file(test_ec, test_ec_txt)
369-
with self.mocked_stdout_stderr():
370-
self.eb_main(args, do_build=True, raise_error=True)
362+
def test_module_only_param(self):
363+
"""check use of module_only parameter"""
364+
topdir = os.path.abspath(os.path.dirname(__file__))
365+
toy_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
371366

372-
self.assertEqual(len(glob.glob(toy_mod_glob)), 1)
367+
test_ec = os.path.join(self.test_prefix, 'test.eb')
368+
test_ec_txt = read_file(toy_ec)
369+
test_ec_txt += "\nmodule_only=True\n"
370+
test_ec_txt += "\nskipsteps = ['sanitycheck']\n" # Software does not exist, so sanity check would fail
371+
write_file(test_ec, test_ec_txt)
373372

374-
# check use of module_only parameter
375-
remove_dir(os.path.join(self.test_installpath, 'modules', 'all', 'toy'))
376-
remove_dir(os.path.join(self.test_installpath, 'software', 'toy', '0.0'))
377373
args = [
378374
test_ec,
379375
'--rebuild',
380376
]
381-
test_ec_txt += "\nmodule_only = True\n"
382-
write_file(test_ec, test_ec_txt)
383-
with self.mocked_stdout_stderr():
384-
self.eb_main(args, do_build=True, raise_error=True)
377+
self.mocked_main(args, do_build=True, raise_error=True)
385378

379+
toy_mod_glob = os.path.join(self.test_installpath, 'modules', 'all', 'toy', '*')
386380
self.assertEqual(len(glob.glob(toy_mod_glob)), 1)
387381

388382
# check that no software was installed
@@ -391,6 +385,38 @@ def test_skip(self):
391385
easybuild_dir = os.path.join(installdir, 'easybuild')
392386
self.assertEqual(installdir_glob, [easybuild_dir])
393387

388+
def test_skipsteps(self):
389+
"""Test skipping of steps using skipsteps."""
390+
# use toy-0.0.eb easyconfig file that comes with the tests
391+
topdir = os.path.abspath(os.path.dirname(__file__))
392+
toy_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
393+
394+
# make sure that sanity check is *NOT* skipped
395+
test_ec = os.path.join(self.test_prefix, 'test.eb')
396+
test_ec_txt = read_file(toy_ec)
397+
regex = re.compile(r"sanity_check_paths = \{(.|\n)*\}", re.M)
398+
test_ec_txt = regex.sub("sanity_check_paths = {'files': ['bin/nosuchfile'], 'dirs': []}", test_ec_txt)
399+
write_file(test_ec, test_ec_txt)
400+
args = [
401+
test_ec,
402+
'--rebuild',
403+
]
404+
error_pattern = "Sanity check failed: no file found at 'bin/nosuchfile'"
405+
self.assertErrorRegex(EasyBuildError, error_pattern, self.mocked_main, args, do_build=True, raise_error=True)
406+
407+
# Verify a wrong step name is caught
408+
test_ec_txt += "\nskipsteps = ['wrong-step-name']\n"
409+
write_file(test_ec, test_ec_txt)
410+
self.assertErrorRegex(EasyBuildError, 'wrong-step-name', self.eb_main, args, do_build=True, raise_error=True)
411+
test_ec_txt += "\nskipsteps = ['source']\n" # Especially the old name -> Replaced by extract
412+
write_file(test_ec, test_ec_txt)
413+
self.assertErrorRegex(EasyBuildError, 'source', self.eb_main, args, do_build=True, raise_error=True)
414+
415+
# check use of skipsteps to skip sanity check
416+
test_ec_txt += "\nskipsteps = ['sanitycheck']\n"
417+
write_file(test_ec, test_ec_txt)
418+
self.mocked_main(args, do_build=True, raise_error=True)
419+
394420
def test_skip_test_step(self):
395421
"""Test skipping testing the build (--skip-test-step)."""
396422

@@ -717,8 +743,8 @@ def test_avail_hooks(self):
717743
" post_fetch_hook",
718744
" pre_ready_hook",
719745
" post_ready_hook",
720-
" pre_source_hook",
721-
" post_source_hook",
746+
" pre_extract_hook",
747+
" post_extract_hook",
722748
" pre_patch_hook",
723749
" post_patch_hook",
724750
" pre_prepare_hook",
@@ -1236,12 +1262,7 @@ def mocked_main(self, args, **kwargs):
12361262
if not kwargs:
12371263
kwargs = {'raise_error': True}
12381264

1239-
self.mock_stderr(True)
1240-
self.mock_stdout(True)
1241-
self.eb_main(args, **kwargs)
1242-
stderr, stdout = self.get_stderr(), self.get_stdout()
1243-
self.mock_stderr(False)
1244-
self.mock_stdout(False)
1265+
stdout, stderr = self._run_mock_eb(args, **kwargs)
12451266
self.assertEqual(stderr, '')
12461267
return stdout.strip()
12471268

@@ -4422,14 +4443,14 @@ def _assert_regexs(self, regexs, txt, assert_true=True):
44224443
self.assertFalse(regex.search(txt), "Pattern '%s' NOT found in: %s" % (regex.pattern, txt))
44234444

44244445
def _run_mock_eb(self, args, strip=False, **kwargs):
4425-
"""Helper function to mock easybuild runs"""
4426-
self.mock_stdout(True)
4427-
self.mock_stderr(True)
4428-
self.eb_main(args, **kwargs)
4429-
stdout_txt = self.get_stdout()
4430-
stderr_txt = self.get_stderr()
4431-
self.mock_stdout(False)
4432-
self.mock_stderr(False)
4446+
"""Helper function to mock easybuild runs
4447+
4448+
Return (stdout, stderr) optionally stripped of whitespace at start/end
4449+
"""
4450+
with self.mocked_stdout_stderr() as (stdout, stderr):
4451+
self.eb_main(args, **kwargs)
4452+
stdout_txt = stdout.getvalue()
4453+
stderr_txt = stderr.getvalue()
44334454
if strip:
44344455
stdout_txt = stdout_txt.strip()
44354456
stderr_txt = stderr_txt.strip()
@@ -5437,6 +5458,10 @@ def test_stop(self):
54375458
regex = re.compile(r"COMPLETED: Installation STOPPED successfully \(took .* secs?\)", re.M)
54385459
self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt))
54395460

5461+
args = ['toy-0.0.eb', '--force', '--stop=source']
5462+
_, stderr = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, strip=True)
5463+
self.assertIn("option --stop: invalid choice", stderr)
5464+
54405465
def test_fetch(self):
54415466
options = EasyBuildOptions(go_args=['--fetch'])
54425467

@@ -6446,7 +6471,7 @@ def test_enforce_checksums(self):
64466471

64476472
args = [
64486473
test_ec,
6449-
'--stop=source',
6474+
'--stop=fetch',
64506475
'--enforce-checksums',
64516476
]
64526477

0 commit comments

Comments
 (0)