allow release notes sections to be single strings

Release notes entries may now be made up of single strings. This
simplifies formatting for smaller notes, and eliminates a class of
errors associated with escaping reStructuredText inside YAML lists.

Change-Id: I7f2fb2d2fd16f49e7ee061582df7bcdd4116f215
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2017-06-05 15:33:16 -04:00
parent 942bbd8427
commit bc3d1241dd
4 changed files with 22 additions and 6 deletions

View File

@ -26,3 +26,6 @@ other:
This example is also rendered This example is also rendered
correctly on multiple lines correctly on multiple lines
as a pre-formatted block. as a pre-formatted block.
features:
This note is a simple string, and does not retain its
formatting when it is rendered in HTML.

View File

@ -0,0 +1,5 @@
---
features:
Release notes entries may now be made up of single strings. This
simplifies formatting for smaller notes, and eliminates a class of
errors associated with escaping reStructuredText inside YAML lists.

View File

@ -101,6 +101,8 @@ class Loader(object):
body = self._scanner.get_file_at_commit(filename, sha) body = self._scanner.get_file_at_commit(filename, sha)
content = yaml.safe_load(body) content = yaml.safe_load(body)
cleaned_content = {}
for section_name, section_content in content.items(): for section_name, section_content in content.items():
if section_name == 'prelude': if section_name == 'prelude':
if not isinstance(section_content, six.string_types): if not isinstance(section_content, six.string_types):
@ -111,10 +113,15 @@ class Loader(object):
filename, filename,
) )
else: else:
if not isinstance(section_content, list): if isinstance(section_content, six.string_types):
# A single string is OK, but wrap it with a list
# so the rest of the code can treat the data model
# consistently.
section_content = [section_content]
elif not isinstance(section_content, list):
LOG.warning( LOG.warning(
('The %s section of %s ' ('The %s section of %s '
'does not parse as a list of strings. ' 'does not parse as a string or list of strings. '
'Is the YAML input escaped properly?') % ( 'Is the YAML input escaped properly?') % (
section_name, filename), section_name, filename),
) )
@ -128,5 +135,6 @@ class Loader(object):
) % (item, section_name, ) % (item, section_name,
filename, type(item)), filename, type(item)),
) )
cleaned_content[section_name] = section_content
return content return cleaned_content

View File

@ -67,7 +67,7 @@ class TestValidate(base.TestCase):
ldr.parse_note_file('note1', None) ldr.parse_note_file('note1', None)
self.assertIn('prelude', self.logger.output) self.assertIn('prelude', self.logger.output)
def test_non_prelude_single_string(self): def test_non_prelude_single_string_converted_to_list(self):
note_bodies = yaml.safe_load(textwrap.dedent(''' note_bodies = yaml.safe_load(textwrap.dedent('''
issues: | issues: |
This is a single string. This is a single string.
@ -75,8 +75,8 @@ class TestValidate(base.TestCase):
print(type(note_bodies['issues'])) print(type(note_bodies['issues']))
self.assertIsInstance(note_bodies['issues'], six.string_types) self.assertIsInstance(note_bodies['issues'], six.string_types)
ldr = self._make_loader(note_bodies) ldr = self._make_loader(note_bodies)
ldr.parse_note_file('note1', None) parse_results = ldr.parse_note_file('note1', None)
self.assertIn('list of strings', self.logger.output) self.assertIsInstance(parse_results['issues'], list)
def test_note_with_colon_as_dict(self): def test_note_with_colon_as_dict(self):
note_bodies = yaml.safe_load(textwrap.dedent(''' note_bodies = yaml.safe_load(textwrap.dedent('''