diff --git a/hydrolib/core/dflowfm/xyn/models.py b/hydrolib/core/dflowfm/xyn/models.py index 7a4a996cd..84947656c 100644 --- a/hydrolib/core/dflowfm/xyn/models.py +++ b/hydrolib/core/dflowfm/xyn/models.py @@ -38,9 +38,9 @@ def _validate_name(cls, value): if str_is_empty_or_none(value): raise ValueError("Name cannot be empty.") - if "'" in value: + if "'" in value or '"' in value: raise ValueError( - "Name cannot contain single quotes except at the start and end." + "Name cannot contain single or double quotes except at the start and end." ) return value diff --git a/hydrolib/core/dflowfm/xyn/parser.py b/hydrolib/core/dflowfm/xyn/parser.py index b8700c13f..40e45ccae 100644 --- a/hydrolib/core/dflowfm/xyn/parser.py +++ b/hydrolib/core/dflowfm/xyn/parser.py @@ -25,12 +25,20 @@ def parse(filepath: Path) -> Dict: Raises: ValueError: if a line in the file cannot be parsed or if the name contains whitespace while not surrounded with - single quotes. + single or double quotes. """ - def is_surrounded_by_quotes(name: str) -> bool: + def is_surrounded_by_single_quotes(name: str) -> bool: return name.startswith("'") and name.endswith("'") + def is_surrounded_by_double_quotes(name: str) -> bool: + return name.startswith('"') and name.endswith('"') + + def is_surrounded_by_quotes(name: str) -> bool: + return is_surrounded_by_single_quotes( + name + ) or is_surrounded_by_double_quotes(name) + def may_contain_whitespace(name: str) -> bool: return is_surrounded_by_quotes(name) @@ -58,7 +66,7 @@ def contains_whitespace_while_not_allowed(name: str) -> bool: if contains_whitespace_while_not_allowed(n): raise ValueError( - f"Error parsing XYN file '{filepath}', line {linenr+1}. Name `{n}` contains whitespace, so should be enclosed in single quotes." + f"Error parsing XYN file '{filepath}', line {linenr+1}. Name `{n}` contains whitespace, so should be enclosed in quotes." ) if is_surrounded_by_quotes(n): diff --git a/tests/dflowfm/test_xyn.py b/tests/dflowfm/test_xyn.py index b405d3364..cdb77759b 100644 --- a/tests/dflowfm/test_xyn.py +++ b/tests/dflowfm/test_xyn.py @@ -19,7 +19,7 @@ def test_parse_xyn_file(self): file_content = """ *This is a comment and should not be parsed. 1.1 2.2 'ObservationPoint_2D_01' - 3.3 4.4 'ObservationPoint_2D_02' + 3.3 4.4 "ObservationPoint_2D_02" """ expected_result = { @@ -31,22 +31,42 @@ def test_parse_xyn_file(self): with create_temp_file(file_content, "test.xyn") as xyn_file: parsed_contents = XYNParser.parse(xyn_file) - assert expected_result == parsed_contents + assert expected_result == parsed_contents - def test_parse_xyn_file_with_quoted_name_removes_quotes(self): + def test_parse_xyn_file_with_single_quoted_name_removes_quotes_and_keeps_whitespace( + self, + ): file_content = """ - 1.1 2.2 'ObservationPoint_2D_01' + 1.1 2.2 'ObservationPoint 2D 01' """ expected_result = { "points": [ - {"x": "1.1", "y": "2.2", "n": "ObservationPoint_2D_01"}, + {"x": "1.1", "y": "2.2", "n": "ObservationPoint 2D 01"}, + ] + } + + with create_temp_file(file_content, "test.xyn") as xyn_file: + parsed_contents = XYNParser.parse(xyn_file) + assert expected_result == parsed_contents + + def test_parse_xyn_file_with_double_quoted_name_removes_quotes_and_keeps_whitespace( + self, + ): + file_content = """ + 1.1 2.2 "ObservationPoint 2D 01" + """ + + expected_result = { + "points": [ + {"x": "1.1", "y": "2.2", "n": "ObservationPoint 2D 01"}, ] } with create_temp_file(file_content, "test.xyn") as xyn_file: parsed_contents = XYNParser.parse(xyn_file) - assert expected_result == parsed_contents + + assert expected_result == parsed_contents def test_parse_xyn_file_with_too_many_columns_raises_error(self): name = "'ObservationPoint_2D_01' This is too much content" @@ -54,7 +74,7 @@ def test_parse_xyn_file_with_too_many_columns_raises_error(self): with pytest.raises(ValueError) as error: with create_temp_file(file_content, "test.xyn") as xyn_file: - expected_message = f"Error parsing XYN file '{xyn_file}', line 1. Name `{name}` contains whitespace, so should be enclosed in single quotes." + expected_message = f"Error parsing XYN file '{xyn_file}', line 1. Name `{name}` contains whitespace, so should be enclosed in quotes." _ = XYNParser.parse(xyn_file) assert expected_message in str(error.value) @@ -79,7 +99,7 @@ def test_serialize_xyn_point(self): with open(xyn_file) as file: file_content = file.readlines() - assert file_content == expected_file_content + assert file_content == expected_file_content class TestXYNModel: @@ -131,7 +151,8 @@ class TestXYNPoint: @pytest.mark.parametrize( ("name"), [ - pytest.param("'nameWithQuote", id="Name with quote"), + pytest.param("nameWith'SingleQuote", id="Name with single quote"), + pytest.param('nameWith"DoubleQuote', id="Name with double quote"), pytest.param(None, id="None value"), pytest.param("", id="Empty string"), pytest.param(" ", id="Whitespace only"),