diff options
author | Anthon van der Neut <anthon@mnt.org> | 2018-12-28 17:14:28 +0100 |
---|---|---|
committer | Anthon van der Neut <anthon@mnt.org> | 2018-12-28 17:14:28 +0100 |
commit | 3ac28c509c0759a881ebfd06d300507e8db2c9d2 (patch) | |
tree | 1e12c26ddc454f101496c91729b60deeaa5c47a9 | |
parent | 96cf5fdbbfec1902fcaeeb3ba6c75ccf92f1f2f7 (diff) | |
download | ruamel.yaml-3ac28c509c0759a881ebfd06d300507e8db2c9d2.tar.gz |
preserve anchors on scalars, on tagged objects0.15.82
fixes issue #63
fixes issue #266
*When this change indeed resolves your problem, please **Close** this issue*.
*(You can do so using the WorkFlow pull-down (close to the top right of this page))*
-rw-r--r-- | CHANGES | 7 | ||||
-rw-r--r-- | README.rst | 19 | ||||
-rw-r--r-- | __init__.py | 4 | ||||
-rw-r--r-- | _doc/_static/pypi.svg | 2 | ||||
-rw-r--r-- | comments.py | 6 | ||||
-rw-r--r-- | constructor.py | 22 | ||||
-rw-r--r-- | emitter.py | 9 | ||||
-rw-r--r-- | representer.py | 23 | ||||
-rw-r--r-- | scalarbool.py | 51 |
9 files changed, 119 insertions, 24 deletions
@@ -1,3 +1,10 @@ +[0, 15, 82]: 2018-12-28 + - anchors and aliases on scalar int, float, string and bool are now preserved. Anchors + do not need a referring alias for these (reported by + `Alex Harvey <https://bitbucket.org/alexharv074/>`__) + - anchors no longer lost on tagged objects when roundtripping (reported by `Zaloo + <https://bitbucket.org/zaloo/>`__) + [0, 15, 81]: 2018-12-06 - fix issue saving methods of metaclass derived classes (reported and fix provided by `Douglas Raillard <https://bitbucket.org/DouglasRaillard/>`__) @@ -4,8 +4,8 @@ ruamel.yaml ``ruamel.yaml`` is a YAML 1.2 loader/dumper package for Python. -:version: 0.15.81 -:updated: 2018-12-06 +:version: 0.15.82 +:updated: 2018-12-28 :documentation: http://yaml.readthedocs.io :repository: https://bitbucket.org/ruamel/ :pypi: https://pypi.org/project/ruamel.yaml/ @@ -54,13 +54,20 @@ ChangeLog .. should insert NEXT: at the beginning of line for next key (with empty line) +0.15.82 (2018-12-28): + - anchors and aliases on scalar int, float, string and bool are now preserved. Anchors + do not need a referring alias for these (reported by + `Alex Harvey <https://bitbucket.org/alexharv074/>`__) + - anchors no longer lost on tagged objects when roundtripping (reported by `Zaloo + <https://bitbucket.org/zaloo/>`__) + 0.15.81 (2018-12-06): - - fix issue dumping methods of metaclass derived classes (reported and fix provided - by `Douglas Raillard <https://bitbucket.org/DouglasRaillard/>`__) + - fix issue dumping methods of metaclass derived classes (reported and fix provided + by `Douglas Raillard <https://bitbucket.org/DouglasRaillard/>`__) 0.15.80 (2018-11-26): - - fix issue emitting BEL character when round-tripping invalid folded input - (reported by Isaac on `StackOverflow <https://stackoverflow.com/a/53471217/1307905>`__) + - fix issue emitting BEL character when round-tripping invalid folded input + (reported by Isaac on `StackOverflow <https://stackoverflow.com/a/53471217/1307905>`__) 0.15.79 (2018-11-21): - fix issue with anchors nested deeper than alias (reported by gaFF on diff --git a/__init__.py b/__init__.py index c0c019a..1ef43d8 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, 82, 'dev'), - __version__='0.15.82.dev', + version_info=(0, 15, 82), + __version__='0.15.82', 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 62a44e9..a98af9d 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.81</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.81</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.82</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.82</text></g> </svg> diff --git a/comments.py b/comments.py index b76f909..1460a56 100644 --- a/comments.py +++ b/comments.py @@ -17,9 +17,9 @@ from ruamel.yaml.scalarstring import ScalarString from ruamel.yaml.anchor import Anchor if PY2: - from collections import MutableSet, Sized, Set, MutableMapping, Mapping + from collections import MutableSet, Sized, Set, Mapping else: - from collections.abc import MutableSet, Sized, Set, MutableMapping, Mapping + from collections.abc import MutableSet, Sized, Set, Mapping if False: # MYPY from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA @@ -1052,7 +1052,7 @@ class CommentedOrderedMap(CommentedMap): __slots__ = (Comment.attrib,) -class CommentedSet(MutableSet, CommentedBase): # type: ignore # NOQA +class CommentedSet(MutableSet, CommentedBase): # type: ignore # NOQA __slots__ = Comment.attrib, 'odict' def __init__(self, values=None): diff --git a/constructor.py b/constructor.py index 61ac4e9..65d6829 100644 --- a/constructor.py +++ b/constructor.py @@ -27,6 +27,7 @@ from ruamel.yaml.scalarstring import (SingleQuotedScalarString, DoubleQuotedScal PlainScalarString, ScalarString,) from ruamel.yaml.scalarint import ScalarInt, BinaryInt, OctalInt, HexInt, HexCapsInt from ruamel.yaml.scalarfloat import ScalarFloat +from ruamel.yaml.scalarbool import ScalarBoolean from ruamel.yaml.timestamp import TimeStamp from ruamel.yaml.util import RegExp @@ -1163,9 +1164,7 @@ class RoundTripConstructor(SafeConstructor): if underscore is not None: # cannot have a leading underscore underscore[2] = len(value_su) > 1 and value_su[-1] == '_' - return ScalarInt( - sign * int(value_s), width=len(value_s), underscore=underscore - ) + return ScalarInt(sign * int(value_s), width=len(value_s), underscore=underscore) elif underscore: # cannot have a leading underscore underscore[2] = len(value_su) > 1 and value_su[-1] == '_' @@ -1173,9 +1172,7 @@ class RoundTripConstructor(SafeConstructor): sign * int(value_s), width=None, underscore=underscore, anchor=node.anchor ) elif node.anchor: - return ScalarInt( - sign * int(value_s), width=None, anchor=node.anchor - ) + return ScalarInt(sign * int(value_s), width=None, anchor=node.anchor) else: return sign * int(value_s) @@ -1623,6 +1620,8 @@ class RoundTripConstructor(SafeConstructor): data.fa.set_block_style() data.yaml_set_tag(node.tag) yield data + if node.anchor: + data.yaml_set_anchor(node.anchor) self.construct_mapping(node, data) return elif isinstance(node, ScalarNode): @@ -1631,6 +1630,8 @@ class RoundTripConstructor(SafeConstructor): data2.style = node.style data2.yaml_set_tag(node.tag) yield data2 + if node.anchor: + data2.yaml_set_anchor(node.anchor, always_dump=True) return elif isinstance(node, SequenceNode): data3 = CommentedSeq() @@ -1641,6 +1642,8 @@ class RoundTripConstructor(SafeConstructor): data3.fa.set_block_style() data3.yaml_set_tag(node.tag) yield data3 + if node.anchor: + data3.yaml_set_anchor(node.anchor) data3.extend(self.construct_sequence(node)) return except: # NOQA @@ -1713,6 +1716,13 @@ class RoundTripConstructor(SafeConstructor): data._yaml['t'] = True return data + def construct_yaml_bool(self, node): + # type: (Any) -> Any + b = SafeConstructor.construct_yaml_bool(self, node) + if node.anchor: + return ScalarBoolean(b, anchor=node.anchor) + return b + RoundTripConstructor.add_constructor( u'tag:yaml.org,2002:null', RoundTripConstructor.construct_yaml_null @@ -402,8 +402,12 @@ class Emitter(object): if isinstance(self.event, AliasEvent): self.expect_alias() elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): - if self.process_anchor(u'&') and isinstance(self.event, ScalarEvent): - self.no_newline = True + if ( + self.process_anchor(u'&') + and isinstance(self.event, ScalarEvent) + and self.sequence_context + ): + self.sequence_context = False self.process_tag() if isinstance(self.event, ScalarEvent): # nprint('@', self.indention, self.no_newline, self.column) @@ -848,6 +852,7 @@ class Emitter(object): # if self.analysis.multiline and split \ # and (not self.style or self.style in '\'\"'): # self.write_indent() + # nprint('xx', self.sequence_context, self.flow_level) if self.sequence_context and not self.flow_level: self.write_indent() if self.style == '"': diff --git a/representer.py b/representer.py index 923b2a2..ced41a0 100644 --- a/representer.py +++ b/representer.py @@ -16,6 +16,7 @@ from ruamel.yaml.scalarstring import ( ) from ruamel.yaml.scalarint import ScalarInt, BinaryInt, OctalInt, HexInt, HexCapsInt from ruamel.yaml.scalarfloat import ScalarFloat +from ruamel.yaml.scalarbool import ScalarBoolean from ruamel.yaml.timestamp import TimeStamp import datetime @@ -289,8 +290,8 @@ class SafeRepresenter(BaseRepresenter): # type: (Any) -> Any return self.represent_scalar(u'tag:yaml.org,2002:str', data) - def represent_bool(self, data): - # type: (Any) -> Any + def represent_bool(self, data, anchor=None): + # type: (Any, Optional[Any]) -> Any try: value = self.dumper.boolean_representation[bool(data)] except AttributeError: @@ -298,7 +299,7 @@ class SafeRepresenter(BaseRepresenter): value = u'true' else: value = u'false' - return self.represent_scalar(u'tag:yaml.org,2002:bool', value) + return self.represent_scalar(u'tag:yaml.org,2002:bool', value, anchor=anchor) def represent_int(self, data): # type: (Any) -> Any @@ -1200,7 +1201,19 @@ class RoundTripRepresenter(SafeRepresenter): tag = data.tag.value except AttributeError: tag = None - return self.represent_scalar(tag, data.value, style=data.style) + try: + anchor = data.yaml_anchor() + except AttributeError: + anchor = None + return self.represent_scalar(tag, data.value, style=data.style, anchor=anchor) + + def represent_scalar_bool(self, data): + # type: (Any) -> Any + try: + anchor = data.yaml_anchor() + except AttributeError: + anchor = None + return SafeRepresenter.represent_bool(self, data, anchor=anchor) RoundTripRepresenter.add_representer(type(None), RoundTripRepresenter.represent_none) @@ -1237,6 +1250,8 @@ RoundTripRepresenter.add_representer(HexCapsInt, RoundTripRepresenter.represent_ RoundTripRepresenter.add_representer(ScalarFloat, RoundTripRepresenter.represent_scalar_float) +RoundTripRepresenter.add_representer(ScalarBoolean, RoundTripRepresenter.represent_scalar_bool) + RoundTripRepresenter.add_representer(CommentedSeq, RoundTripRepresenter.represent_list) RoundTripRepresenter.add_representer(CommentedMap, RoundTripRepresenter.represent_dict) diff --git a/scalarbool.py b/scalarbool.py new file mode 100644 index 0000000..fc8f8c2 --- /dev/null +++ b/scalarbool.py @@ -0,0 +1,51 @@ +# coding: utf-8 + +from __future__ import print_function, absolute_import, division, unicode_literals + +""" +You cannot subclass bool, and this is necessary for round-tripping anchored +bool values (and also if you want to preserve the original way of writing) + +bool.__bases__ is type 'int', so that is what is used as the basis for ScalarBoolean as well. + +You can use these in an if statement, but not when testing equivalence +""" + +from ruamel.yaml.anchor import Anchor + +if False: # MYPY + from typing import Text, Any, Dict, List # NOQA + +__all__ = ['ScalarBoolean'] + +# no need for no_limit_int -> int + + +class ScalarBoolean(int): + def __new__(cls, *args, **kw): + # type: (Any, Any, Any) -> Any + anchor = kw.pop('anchor', None) # type: ignore + b = int.__new__(cls, *args, **kw) # type: ignore + if anchor is not None: + b.yaml_set_anchor(anchor, always_dump=True) + return b + + @property + def anchor(self): + # type: () -> Any + if not hasattr(self, Anchor.attrib): + setattr(self, Anchor.attrib, Anchor()) + return getattr(self, Anchor.attrib) + + def yaml_anchor(self, any=False): + # type: (bool) -> Any + if not hasattr(self, Anchor.attrib): + return None + if any or self.anchor.always_dump: + return self.anchor + return None + + def yaml_set_anchor(self, value, always_dump=False): + # type: (Any, bool) -> None + self.anchor.value = value + self.anchor.always_dump = always_dump |