diff --git a/releasenotes/notes/validate-note-files-1cdbdcde9ae7829b.yaml b/releasenotes/notes/validate-note-files-1cdbdcde9ae7829b.yaml new file mode 100644 index 0000000..587bff3 --- /dev/null +++ b/releasenotes/notes/validate-note-files-1cdbdcde9ae7829b.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Release note file validation is improved. Files missing section information + will now be correctly handled and rejected. diff --git a/reno/loader.py b/reno/loader.py index 4ef6019..3ffd2ec 100644 --- a/reno/loader.py +++ b/reno/loader.py @@ -119,6 +119,17 @@ class Loader(object): cleaned_content = {} + if not isinstance(content, dict): + LOG.warning( + '%s does not appear to be structured as a YAML mapping. ' + 'Did you forget a top-level key?', + filename, + ) + raise ValueError( + f'{filename} does not appear to be structured as a YAML ' + f'mapping. Did you forget a top-level key?' + ) + for section_name, section_content in content.items(): if section_name == self._config.prelude_section_name: if not isinstance(section_content, str): diff --git a/reno/tests/test_loader.py b/reno/tests/test_loader.py index 9c1617f..ec11dec 100644 --- a/reno/tests/test_loader.py +++ b/reno/tests/test_loader.py @@ -87,3 +87,19 @@ class TestValidate(base.TestCase): ldr = self._make_loader(note_bodies) ldr.parse_note_file('note1', None) self.assertIn('dict', self.logger.output) + + def test_invalid_note_with_missing_key(self): + """Test behavior when note is not structured as a mapping. + + This one should be an error since we can't correct the input. + """ + note_bodies = yaml.safe_load(textwrap.dedent(''' + - | + This is an issue but we're missing the top-level 'issues' key. + ''')) + self.assertIsInstance(note_bodies, list) + ldr = self._make_loader(note_bodies) + self.assertRaises(ValueError, ldr.parse_note_file, 'note1', None) + self.assertIn( + 'does not appear to be structured as a YAML mapping', + self.logger.output)