diff --git a/easybuild/base/testing.py b/easybuild/base/testing.py index 71700794d3..81793623af 100644 --- a/easybuild/base/testing.py +++ b/easybuild/base/testing.py @@ -201,6 +201,15 @@ def get_stderr(self): """Return output captured from stderr until now.""" return sys.stderr.getvalue() + @contextmanager + def mocked_stdout(self): + """Context manager to mock stdout""" + self.mock_stdout(True) + try: + yield sys.stdout + finally: + self.mock_stdout(False) + @contextmanager def mocked_stdout_stderr(self, mock_stdout=True, mock_stderr=True): """Context manager to mock stdout and stderr""" diff --git a/easybuild/tools/github.py b/easybuild/tools/github.py index fa6e5519eb..d7e7f0b5c7 100644 --- a/easybuild/tools/github.py +++ b/easybuild/tools/github.py @@ -52,7 +52,7 @@ from easybuild.framework.easyconfig.easyconfig import EASYCONFIGS_ARCHIVE_DIR from easybuild.framework.easyconfig.easyconfig import copy_easyconfigs, copy_patch_files, det_file_info from easybuild.framework.easyconfig.easyconfig import process_easyconfig -from easybuild.framework.easyconfig.parser import EasyConfigParser +from easybuild.framework.easyconfig.parser import ALTERNATIVE_EASYCONFIG_PARAMETERS, EasyConfigParser from easybuild.tools import LooseVersion from easybuild.tools.build_log import EasyBuildError, EasyBuildExit, print_msg, print_warning from easybuild.tools.config import build_option @@ -753,13 +753,6 @@ def commit_request_fn(gh): # download tarball for specific commit repo_commit = download_repo(repo=github_repo, commit=commit, account=github_account) - if github_repo == GITHUB_EASYCONFIGS_REPO: - files_subdir = 'easybuild/easyconfigs/' - elif github_repo == GITHUB_EASYBLOCKS_REPO: - files_subdir = 'easybuild/easyblocks/' - else: - raise EasyBuildError("Unknown repo: %s", github_repo, exit_code=EasyBuildExit.OPTION_ERROR) - # symlink subdirectories of 'easybuild/easy{blocks,configs}' into path that gets added to robot search path mkdir(path, parents=True) dirpath = os.path.join(repo_commit, easybuild_subdir) @@ -768,6 +761,7 @@ def commit_request_fn(gh): # copy specified files to directory where they're expected to be found file_paths = [] + subdir_prefix = easybuild_subdir + os.path.sep for file in files: # if only filename is specified, we need to determine the file path @@ -782,8 +776,8 @@ def commit_request_fn(gh): # strip of leading subdirectory like easybuild/easyconfigs/ or easybuild/easyblocks/ # because that's what expected by robot_find_easyconfig - if file.startswith(files_subdir): - file = file[len(files_subdir):] + if file.startswith(subdir_prefix): + file = file[len(subdir_prefix):] # if file is found, copy it to dedicated directory; # if not, just skip it (may be an easyconfig file in local directory); @@ -1340,7 +1334,7 @@ def get_name(patch): exit_code=EasyBuildExit.VALUE_ERROR) return patch - patches = [get_name(p) for p in ec['patches']] + patches = [get_name(p) for p in itertools.chain(ec['patches'], ec['postinstallpatches'])] with ec.disable_templating(): # take into account both list of extensions (via exts_list) and components (cfr. Bundle easyblock) @@ -1352,7 +1346,12 @@ def get_name(patch): 'version': entry[1], } options = entry[2] - patches.extend(get_name(p) % templates for p in options.get('patches', [])) + patches.extend(get_name(p) % templates for p in + itertools.chain( + options.get('patches', []), + options.get(ALTERNATIVE_EASYCONFIG_PARAMETERS['post_install_patches'], + options.get('post_install_patches', [])) + )) return patch_name in patches diff --git a/test/framework/github.py b/test/framework/github.py index c80e496cf2..0934ef969b 100644 --- a/test/framework/github.py +++ b/test/framework/github.py @@ -120,7 +120,6 @@ def tearDown(self): def test_det_pr_title(self): """Test det_pr_title function""" - # check if patches for extensions are found rawtxt = textwrap.dedent(""" easyblock = 'ConfigureMake' name = '%s' @@ -995,9 +994,8 @@ def test_github_det_patch_specs(self): file_info['ecs'].append(EasyConfig(None, rawtxt=rawtxt)) error_pattern = "Failed to determine software name to which patch file .*/2.patch relates" - self.mock_stdout(True) - self.assertErrorRegex(EasyBuildError, error_pattern, gh.det_patch_specs, patch_paths, file_info, []) - self.mock_stdout(False) + with self.mocked_stdout(): + self.assertErrorRegex(EasyBuildError, error_pattern, gh.det_patch_specs, patch_paths, file_info, []) rawtxt = textwrap.dedent(""" easyblock = 'ConfigureMake' @@ -1007,12 +1005,11 @@ def test_github_det_patch_specs(self): description = '' toolchain = {"name":"GCC", "version": "4.6.3"} - patches = [('3.patch', 'subdir'), '2.patch'] + postinstallpatches = [('3.patch', 'subdir'), '2.patch'] """) file_info['ecs'].append(EasyConfig(None, rawtxt=rawtxt)) - self.mock_stdout(True) - res = gh.det_patch_specs(patch_paths, file_info, []) - self.mock_stdout(False) + with self.mocked_stdout(): + res = gh.det_patch_specs(patch_paths, file_info, []) self.assertEqual([i[0] for i in res], patch_paths) self.assertEqual([i[1] for i in res], ['A', 'C', 'C']) @@ -1031,20 +1028,24 @@ def test_github_det_patch_specs(self): ('bar', '1.2.3'), ('patched', '4.5.6', { 'patches': [('%(name)s-2.patch', 1), '%(name)s-3.patch'], + 'postinstallpatches': ['%(name)s-4.patch'], }), ] + postinstallpatches = ['%(name)s-5.patch'], """) - patch_paths[1:3] = [os.path.join(self.test_prefix, p) for p in ['patched-2.patch', 'patched-3.patch']] + patch_paths[1:] = [os.path.join(self.test_prefix, p) for p in + ['patched-2.patch', 'patched-3.patch', 'patched-4.patch', 'patched_ext-5.patch']] file_info['ecs'][-1] = EasyConfig(None, rawtxt=rawtxt) - self.mock_stdout(True) - res = gh.det_patch_specs(patch_paths, file_info, []) - self.mock_stdout(False) + with self.mocked_stdout(): + res = gh.det_patch_specs(patch_paths, file_info, []) self.assertEqual([i[0] for i in res], patch_paths) - self.assertEqual([i[1] for i in res], ['A', 'patched_ext', 'patched_ext']) + self.assertEqual([i[1] for i in res], ['A'] + ['patched_ext'] * 4) # check if patches for components are found + # NOTE: Using alternative name and tuple format for post_install_patches, different to above test case + # to verify handling either way works without adding another sub-test rawtxt = textwrap.dedent(""" easyblock = 'PythonBundle' name = 'patched_bundle' @@ -1057,17 +1058,19 @@ def test_github_det_patch_specs(self): ('bar', '1.2.3'), ('patched', '4.5.6', { 'patches': [('%(name)s-2.patch', 1), '%(name)s-3.patch'], + 'post_install_patches': [('%(name)s-4.patch', 1)], }), ] + post_install_patches = [('%(name)s-5.patch', 2)] """) + patch_paths[-1] = 'patched_bundle-5.patch' file_info['ecs'][-1] = EasyConfig(None, rawtxt=rawtxt) - self.mock_stdout(True) - res = gh.det_patch_specs(patch_paths, file_info, []) - self.mock_stdout(False) + with self.mocked_stdout(): + res = gh.det_patch_specs(patch_paths, file_info, []) self.assertEqual([i[0] for i in res], patch_paths) - self.assertEqual([i[1] for i in res], ['A', 'patched_bundle', 'patched_bundle']) + self.assertEqual([i[1] for i in res], ['A'] + ['patched_bundle'] * 4) def test_github_restclient(self): """Test use of RestClient."""