Repository: easybuilders/easybuild-framework

Issue:
EB crashes with `KeyError` when `dirs` key is not present in `sanity_check_paths`
Here's an example easyconfig:

```python
easyblock = "PythonPackage"

name = "setuptools"
version = "41.0.1"

homepage = "https://pypi.org/project/setuptools"
description = """Easily download, build, install, upgrade, and uninstall Python packages"""

toolchain = {'name': 'dummy', 'version': 'dummy'}

source_urls = [PYPI_SOURCE]
sources = [SOURCE_ZIP]

download_dep_fail = True
use_pip = False

req_py_majver = 3

sanity_check_paths = {
    'files': ["bin/easy_install"],
#    'dirs': [],
}

moduleclass = 'devel'
```

The error I am getting is the following:

```
== temporary log file in case of crash /run/user/23483/easybuild/tmp/eb-X5HDBx/easybuild-DhQvBj.log
== resolving dependencies ...
== processing EasyBuild easyconfig /users/karakasv/Devel/easybuild-easyconfigs/easybuild/easyconfigs/s/setuptools/setuptools-41.0.1.eb
ERROR: Traceback (most recent call last):
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/main.py", line 112, in build_and_install_software
    (ec_res['success'], app_log, err) = build_and_install_one(ec, init_env)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyblock.py", line 3045, in build_and_install_one
    reprod_dir = reproduce_build(app, reprod_dir_root)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyblock.py", line 3189, in reproduce_build
    app.cfg.dump(reprod_spec)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyconfig/easyconfig.py", line 990, in dump
    ectxt = self.parser.dump(self, default_values, templ_const, templ_val)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyconfig/parser.py", line 230, in dump
    return self._formatter.dump(ecfg, default_values, templ_const, templ_val)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyconfig/format/one.py", line 294, in dump
    params, _ = self._find_defined_params(ecfg, [[k] for k in LAST_PARAMS], default_values, templ_const, templ_val)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyconfig/format/one.py", line 261, in _find_defined_params
    eclines.extend(self._find_param_with_comments(key, valstr, templ_const, templ_val))
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyconfig/format/one.py", line 213, in _find_param_with_comments
    val = self._reformat_line(key, val, outer=True)
  File "/apps/common/UES/easybuild/software/EasyBuild/3.9.1/lib/python2.7/site-packages/easybuild_framework-3.9.1-py2.7.egg/easybuild/framework/easyconfig/format/one.py", line 161, in _reformat_line
    item_val = param_val[item_key]
KeyError: 'dirs'
```

Since `dirs` is required, it'd be more useful if a more human-readable error was printed out.


Discussion:

Resolving patch:
diff --git a/easybuild/framework/easyconfig/format/one.py b/easybuild/framework/easyconfig/format/one.py
--- a/easybuild/framework/easyconfig/format/one.py
+++ b/easybuild/framework/easyconfig/format/one.py
@@ -161,7 +161,11 @@ def _reformat_line(self, param_name, param_val, outer=False, addlen=0):
                 if isinstance(param_val, dict):
                     ordered_item_keys = REFORMAT_ORDERED_ITEM_KEYS.get(param_name, sorted(param_val.keys()))
                     for item_key in ordered_item_keys:
-                        item_val = param_val[item_key]
+                        if item_key in param_val:
+                            item_val = param_val[item_key]
+                        else:
+                            raise EasyBuildError("Missing mandatory key '%s' in %s.", item_key, param_name)
+
                         comment = self._get_item_comments(param_name, item_val).get(str(item_val), '')
                         key_pref = quote_py_str(item_key) + ': '
                         addlen = addlen + len(INDENT_4SPACES) + len(key_pref) + len(comment)


Test patch: