summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2018-08-05 12:27:52 +0200
committerAnthon van der Neut <anthon@mnt.org>2018-08-05 12:27:52 +0200
commit06ea5a9886a6abb88b7a81481c6600477088f2c6 (patch)
treec8f72ab953eab349348714b2f5b65ce5356622d6
parent9b15f5f59e52ce2257a579900d8315cd7d46e4c9 (diff)
downloadruamel.yaml-06ea5a9886a6abb88b7a81481c6600477088f2c6.tar.gz
leading empty lines and comments on root level literal scalars not preserved0.15.49
added test, fixed both now you can have --- !some_tag | # with a comment this is a multi- line literal scalar
-rw-r--r--CHANGES6
-rw-r--r--README.rst10
-rw-r--r--__init__.py4
-rw-r--r--_doc/_static/pypi.svg2
-rw-r--r--_test/test_literal.py27
-rw-r--r--constructor.py5
-rw-r--r--emitter.py12
-rw-r--r--representer.py7
-rw-r--r--scalarstring.py2
-rw-r--r--scanner.py11
-rw-r--r--setup.py2
11 files changed, 75 insertions, 13 deletions
diff --git a/CHANGES b/CHANGES
index 3fc9b84..eadd88a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+[0, 15, 49]: 2018-08-05
+ - fix preservation of leading newlines in root level literal style scalar,
+ and preserve comment after literal style indicator (``| # some comment``)
+ Both needed for round-tripping multi-doc streams in
+ `ryd <https://pypi.org/project/ryd/>`__.
+
[0, 15, 48]: 2018-08-03
- housekeeping: ``oitnb`` for formatting, mypy 0.620 upgrade and conformity
diff --git a/README.rst b/README.rst
index 2f91868..8f540c3 100644
--- a/README.rst
+++ b/README.rst
@@ -4,8 +4,8 @@ ruamel.yaml
``ruamel.yaml`` is a YAML 1.2 loader/dumper package for Python.
-:version: 0.15.48
-:updated: 2018-08-03
+:version: 0.15.49
+:updated: 2018-08-05
:documentation: http://yaml.readthedocs.io
:repository: https://bitbucket.org/ruamel/
:pypi: https://pypi.org/project/ruamel.yaml/
@@ -54,6 +54,12 @@ ChangeLog
.. should insert NEXT: at the beginning of line for next key (with empty line)
+0.15.49 (2018-08-05):
+ - fix preservation of leading newlines in root level literal style scalar,
+ and preserve comment after literal style indicator (``| # some comment``)
+ Both needed for round-tripping multi-doc streams in
+ `ryd <https://pypi.org/project/ryd/>`__.
+
0.15.48 (2018-08-03):
- housekeeping: ``oitnb`` for formatting, mypy 0.620 upgrade and conformity
diff --git a/__init__.py b/__init__.py
index c675a8e..2eb42bc 100644
--- a/__init__.py
+++ b/__init__.py
@@ -7,8 +7,8 @@ if False: # MYPY
_package_data = dict(
full_package_name='ruamel.yaml',
- version_info=(0, 15, 48),
- __version__='0.15.48',
+ version_info=(0, 15, 49),
+ __version__='0.15.49',
author='Anthon van der Neut',
author_email='a.van.der.neut@ruamel.eu',
description='ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order', # NOQA
diff --git a/_doc/_static/pypi.svg b/_doc/_static/pypi.svg
index 45c5650..67703ce 100644
--- a/_doc/_static/pypi.svg
+++ b/_doc/_static/pypi.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="86" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="86" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h33v20H0z"/><path fill="#007ec6" d="M33 0h53v20H33z"/><path fill="url(#b)" d="M0 0h86v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="175" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="230">pypi</text><text x="175" y="140" transform="scale(.1)" textLength="230">pypi</text><text x="585" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">0.15.48</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.48</text></g> </svg>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="86" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="86" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h33v20H0z"/><path fill="#007ec6" d="M33 0h53v20H33z"/><path fill="url(#b)" d="M0 0h86v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="175" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="230">pypi</text><text x="175" y="140" transform="scale(.1)" textLength="230">pypi</text><text x="585" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">0.15.49</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.49</text></g> </svg>
diff --git a/_test/test_literal.py b/_test/test_literal.py
index 74dca3d..5847269 100644
--- a/_test/test_literal.py
+++ b/_test/test_literal.py
@@ -240,6 +240,33 @@ class Test_RoundTripLiteral:
d = yaml.load(ys)
yaml.dump(d, compare=ys)
+ def test_rt_top_literal_scalar_no_indent_comment(self):
+ yaml = YAML()
+ yaml.explicit_start = True
+ s = 'testing123'
+ ys = """
+ --- | # a comment
+ {}
+ """.format(
+ s
+ )
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_top_literal_scalar_no_indent_leading_empty_line(self):
+ yaml = YAML()
+ yaml.explicit_start = True
+ s = 'testing123'
+ ys = """
+ --- |
+
+ {}
+ """.format(
+ s
+ )
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
def test_rt_top_literal_scalar_indent(self):
yaml = YAML()
yaml.explicit_start = True
diff --git a/constructor.py b/constructor.py
index 6a26da3..c830580 100644
--- a/constructor.py
+++ b/constructor.py
@@ -979,7 +979,10 @@ class RoundTripConstructor(SafeConstructor):
)
if node.style == '|' and isinstance(node.value, text_type):
- return PreservedScalarString(node.value)
+ pss = PreservedScalarString(node.value)
+ if node.comment and node.comment[1]:
+ pss.comment = node.comment[1][0]
+ return pss
elif bool(self._preserve_quotes) and isinstance(node.value, text_type):
if node.style == "'":
return SingleQuotedScalarString(node.value)
diff --git a/emitter.py b/emitter.py
index 626a69e..a75fa52 100644
--- a/emitter.py
+++ b/emitter.py
@@ -811,7 +811,7 @@ class Emitter(object):
elif self.style == '>':
self.write_folded(self.analysis.scalar)
elif self.style == '|':
- self.write_literal(self.analysis.scalar)
+ self.write_literal(self.analysis.scalar, self.event.comment)
else:
self.write_plain(self.analysis.scalar, split)
self.analysis = None
@@ -1337,7 +1337,7 @@ class Emitter(object):
# type: (Any) -> Any
hints = ""
if text:
- if text[0] in u' \n\x85\u2028\u2029':
+ if not self.root_context and text[0] in u' \n\x85\u2028\u2029':
hints += text_type(self.best_sequence_indent)
if text[-1] not in u'\n\x85\u2028\u2029':
hints += u'-'
@@ -1404,10 +1404,16 @@ class Emitter(object):
spaces = ch == u' '
end += 1
- def write_literal(self, text):
+ def write_literal(self, text, comment=None):
# type: (Any) -> None
hints = self.determine_block_hints(text)
self.write_indicator(u'|' + hints, True)
+ try:
+ comment = comment[1][0]
+ if comment:
+ self.stream.write(comment)
+ except (TypeError, IndexError):
+ pass
if hints[-1:] == u'+':
self.open_ended = True
self.write_line_break()
diff --git a/representer.py b/representer.py
index 7b821fe..07b2535 100644
--- a/representer.py
+++ b/representer.py
@@ -148,7 +148,12 @@ class BaseRepresenter(object):
# type: (Any, Any, Any) -> Any
if style is None:
style = self.default_style
- node = ScalarNode(tag, value, style=style)
+ comment = None
+ if style and style[0] == '|':
+ comment = getattr(value, 'comment', None)
+ if comment:
+ comment = [None, [comment]]
+ node = ScalarNode(tag, value, style=style, comment=comment)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
diff --git a/scalarstring.py b/scalarstring.py
index 4b1a317..ef283d2 100644
--- a/scalarstring.py
+++ b/scalarstring.py
@@ -28,7 +28,7 @@ class ScalarString(text_type):
class PreservedScalarString(ScalarString):
- __slots__ = ()
+ __slots__ = ('comment') # the comment after the | on the first line
style = '|'
diff --git a/scanner.py b/scanner.py
index 92fbf52..dd77a12 100644
--- a/scanner.py
+++ b/scanner.py
@@ -1111,7 +1111,8 @@ class Scanner(object):
# Scan the header.
self.reader.forward()
chomping, increment = self.scan_block_scalar_indicators(start_mark)
- self.scan_block_scalar_ignored_line(start_mark)
+ # block scalar comment e.g. : |+ # comment text
+ block_scalar_comment = self.scan_block_scalar_ignored_line(start_mark)
# Determine the indentation level and go to the first non-empty line.
min_indent = self.indent + 1
@@ -1193,6 +1194,8 @@ class Scanner(object):
# We are done.
token = ScalarToken("".join(chunks), False, start_mark, end_mark, style)
+ if block_scalar_comment is not None:
+ token.add_pre_comments([block_scalar_comment])
if len(trailing) > 0:
# print('trailing 1', trailing) # XXXXX
# Eat whitespaces and comments until we reach the next token.
@@ -1261,10 +1264,15 @@ class Scanner(object):
def scan_block_scalar_ignored_line(self, start_mark):
# type: (Any) -> Any
# See the specification for details.
+ prefix = ''
+ comment = None
while self.reader.peek() == ' ':
+ prefix += self.reader.peek()
self.reader.forward()
if self.reader.peek() == '#':
+ comment = prefix
while self.reader.peek() not in _THE_END:
+ comment += self.reader.peek()
self.reader.forward()
ch = self.reader.peek()
if ch not in _THE_END:
@@ -1275,6 +1283,7 @@ class Scanner(object):
self.reader.get_mark(),
)
self.scan_line_break()
+ return comment
def scan_block_scalar_indentation(self):
# type: () -> Any
diff --git a/setup.py b/setup.py
index 4eb777c..857b5e2 100644
--- a/setup.py
+++ b/setup.py
@@ -662,7 +662,7 @@ class NameSpacePackager(object):
@property
def keywords(self):
- return self.pn(self._pkg_data.get('keywords'))
+ return self.pn(self._pkg_data.get('keywords', []))
@property
def install_requires(self):