diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | README.rst | 10 | ||||
-rw-r--r-- | __init__.py | 4 | ||||
-rw-r--r-- | _doc/_static/pypi.svg | 2 | ||||
-rw-r--r-- | _test/test_literal.py | 27 | ||||
-rw-r--r-- | constructor.py | 5 | ||||
-rw-r--r-- | emitter.py | 12 | ||||
-rw-r--r-- | representer.py | 7 | ||||
-rw-r--r-- | scalarstring.py | 2 | ||||
-rw-r--r-- | scanner.py | 11 | ||||
-rw-r--r-- | setup.py | 2 |
11 files changed, 75 insertions, 13 deletions
@@ -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 @@ -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) @@ -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 = '|' @@ -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 @@ -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): |