diff --git a/f90nml/parser.py b/f90nml/parser.py index 29caf56..0523e4f 100644 --- a/f90nml/parser.py +++ b/f90nml/parser.py @@ -329,6 +329,9 @@ def _readstream(self, nml_file, nml_patch_in=None): except StopIteration: break + # save whether & or $ is opening this group + opening_token = self.token + # Create the next namelist try: self._update_tokens() @@ -384,6 +387,24 @@ def _readstream(self, nml_file, nml_patch_in=None): # Finalise namelist group if self.token in ('/', '&', '$'): + if self.token in ('&', '$'): + # Peek at the next non-whitespace token without + # consuming it, to distinguish F77-style terminators + # (&end or standalone &, or same with $) from + # an unclosed group. + self.tokens, peek_iter = itertools.tee(self.tokens) + skip = self.comment_tokens + whitespace + next_tok = next( + (t for t in peek_iter if t[0] not in skip), + None + ) + if next_tok and next_tok.lower() not in ('end', '&', '$'): + raise ValueError( + "f90nml: error: Namelist group '{}{}' was not " + "closed before the start of a new group or EOF." + .format(opening_token, g_name) + ) + # Append any remaining patched variables for v_name, v_val in grp_patch.items(): g_vars[v_name] = v_val diff --git a/tests/end_uppercase.nml b/tests/end_uppercase.nml new file mode 100644 index 0000000..572e841 --- /dev/null +++ b/tests/end_uppercase.nml @@ -0,0 +1,7 @@ +&END_TOKEN_UPPERCASE + a = 1 +&END + +&last_grp + a = 1 +& \ No newline at end of file diff --git a/tests/f77.nml b/tests/f77.nml index d795759..af4f9e5 100644 --- a/tests/f77.nml +++ b/tests/f77.nml @@ -1,3 +1,7 @@ +$f77_nml_dollar + x = 123 +$end + &f77_nml x = 123 &end @@ -5,3 +9,7 @@ &next_f77_nml y = 'abc' &end + +&another_f77_nml + z = 99 +& diff --git a/tests/f77_target.nml b/tests/f77_target.nml index b7e90d7..ad946f6 100644 --- a/tests/f77_target.nml +++ b/tests/f77_target.nml @@ -1,3 +1,7 @@ +&f77_nml_dollar + x = 123 +/ + &f77_nml x = 123 / @@ -5,3 +9,7 @@ &next_f77_nml y = 'abc' / + +&another_f77_nml + z = 99 +/ diff --git a/tests/first_grp_no_end.nml b/tests/first_grp_no_end.nml new file mode 100644 index 0000000..55c9adf --- /dev/null +++ b/tests/first_grp_no_end.nml @@ -0,0 +1,6 @@ +&open_group +a = 1 + +&closed_group +b = 2 +/ diff --git a/tests/first_grp_no_end_dollar.nml b/tests/first_grp_no_end_dollar.nml new file mode 100644 index 0000000..32939d0 --- /dev/null +++ b/tests/first_grp_no_end_dollar.nml @@ -0,0 +1,7 @@ +$open_group +a = 1 + + +$closed_group +b = 2 +$end diff --git a/tests/test_f90nml.py b/tests/test_f90nml.py index 96cc991..6835971 100644 --- a/tests/test_f90nml.py +++ b/tests/test_f90nml.py @@ -427,8 +427,10 @@ def setUp(self): } self.f77_nml = { + 'f77_nml_dollar': {'x': 123}, 'f77_nml': {'x': 123}, 'next_f77_nml': {'y': 'abc'}, + 'another_f77_nml': {'z': 99}, } self.dollar_nml = {'dollar_nml': {'v': 1.}} @@ -1595,6 +1597,11 @@ def test_repeat_repeating_logical(self): line2 = out.readline() self.assertEqual(line2.lstrip(), "b = 2*.true., .false.\n") + def test_end_uppercase(self): + test_nml = f90nml.read('end_uppercase.nml') + self.assertEqual(test_nml, {'end_token_uppercase': {'a': 1}, + 'last_grp': {'a': 1}}) + # Failed namelist parsing # NOTE: This is a very weak test, since '& x=1' / will pass def test_grp_token_end(self): @@ -1612,6 +1619,12 @@ def test_string_grp_no_end(self): def test_file_grp_no_end(self): self.assertRaises(ValueError, f90nml.read, 'grp_no_end.nml') + def test_file_first_grp_no_end(self): + self.assertRaises(ValueError, f90nml.read, 'first_grp_no_end.nml') + + def test_file_first_grp_no_end_dollar(self): + self.assertRaises(ValueError, f90nml.read, 'first_grp_no_end_dollar.nml') + if __name__ == '__main__': if os.path.isfile('tmp.nml'):