diff --git a/src/ert/_c_wrappers/enkf/ensemble_config.py b/src/ert/_c_wrappers/enkf/ensemble_config.py index eb3b8362fa4..7f690c200b3 100644 --- a/src/ert/_c_wrappers/enkf/ensemble_config.py +++ b/src/ert/_c_wrappers/enkf/ensemble_config.py @@ -19,6 +19,7 @@ from ert.config.summary_config import SummaryConfig from ert.config.surface_config import SurfaceConfig from ert.parsing import ConfigValidationError, ConfigWarning, ErrorInfo +from ert.parsing.context_values import ContextList, ContextValue from ert.storage.field_utils.field_utils import Shape, get_shape from ert.validation import rangestring_to_list @@ -233,7 +234,7 @@ def __init__( self.addNode(self.get_field_node(field, grid_file, dims)) @staticmethod - def gen_data_node(gen_data: List[str]) -> Optional[GenDataConfig]: + def gen_data_node(gen_data: ContextList[ContextValue]) -> Optional[GenDataConfig]: options = _option_dict(gen_data, 1) name = gen_data[0] res_file = options.get(ConfigKeys.RESULT_FILE) @@ -246,15 +247,22 @@ def gen_data_node(gen_data: List[str]) -> Optional[GenDataConfig]: report_steps = rangestring_to_list(options.get(ConfigKeys.REPORT_STEPS, "")) if os.path.isabs(res_file) or "%d" not in res_file: - logger.error( - f"The RESULT_FILE:{res_file} setting for {name} is invalid - " - "must have an embedded %d - and be a relative path" + result_file_context = next( + x for x in gen_data if x.startswith("RESULT_FILE:") + ) + raise ConfigValidationError.from_info( + ErrorInfo( + message=f"The RESULT_FILE:{res_file} setting for {name} is invalid - " + "must have an embedded %d - and be a relative path" + ).set_context(result_file_context) ) elif not report_steps: - logger.error( - "The GEN_DATA keywords must have a REPORT_STEPS:xxxx defined" - "Several report steps separated with ',' and ranges with '-'" - "can be listed" + raise ConfigValidationError.from_info( + ErrorInfo( + message="The GEN_DATA keywords must have a REPORT_STEPS:xxxx defined" + "Several report steps separated with ',' and ranges with '-'" + "can be listed" + ).set_context_keyword(gen_data.keyword_token) ) else: gdc = GenDataConfig( diff --git a/src/ert/parsing/error_info.py b/src/ert/parsing/error_info.py index 01ac526d2fe..c4c810f9a62 100644 --- a/src/ert/parsing/error_info.py +++ b/src/ert/parsing/error_info.py @@ -12,7 +12,7 @@ # pylint: disable=too-many-instance-attributes class ErrorInfo: message: str - filename: Optional[str] + filename: Optional[str] = None start_pos: Optional[int] = None line: Optional[int] = None column: Optional[int] = None @@ -70,6 +70,7 @@ def attached_to_context(cls, token, *args, **kwargs): def _attach_to_context(self, token: Optional[FileContextToken]): if token is not None: + self.filename = token.filename self.originates_from = token self.start_pos = token.start_pos self.line = token.line diff --git a/tests/test_config_parsing/test_parser_error_collection.py b/tests/test_config_parsing/test_parser_error_collection.py index 5e30a3dda33..f2c39835acc 100644 --- a/tests/test_config_parsing/test_parser_error_collection.py +++ b/tests/test_config_parsing/test_parser_error_collection.py @@ -1059,3 +1059,41 @@ def test_that_deprecations_are_handled(contents, expected_errors): assert_that_config_leads_to_warning( config_file_contents=contents, expected_error=expected_error ) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_invalid_ensemble_result_file_errors(): + assert_that_config_leads_to_error( + config_file_contents=dedent( + """ +NUM_REALIZATIONS 1 +GEN_DATA RFT_3-1_R_DATA INPUT_FORMAT:ASCII REPORT_STEPS:100 RESULT_FILE:RFT_3-1_R_ + + """ + ), + expected_error=ExpectedErrorInfo( + match="must have an embedded %d", + line=3, + column=61, + end_column=89, + ), + ) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_missing_report_steps_errors(): + assert_that_config_leads_to_error( + config_file_contents=dedent( + """ +NUM_REALIZATIONS 1 +GEN_DATA RFT_3-1_R_DATA INPUT_FORMAT:ASCII RESULT_FILE:RFT_3-1_R%d + + """ + ), + expected_error=ExpectedErrorInfo( + match="REPORT_STEPS", + line=3, + column=1, + end_column=9, + ), + )