diff options
author | Anthon van der Neut <anthon@mnt.org> | 2018-08-14 16:30:15 +0200 |
---|---|---|
committer | Anthon van der Neut <anthon@mnt.org> | 2018-08-14 16:30:15 +0200 |
commit | 0923c2a691f57f9e095f5c4b9a8f06fa78bb46d2 (patch) | |
tree | ba2dc411b705bc0290711a591480a66225634352 | |
parent | 65d95151526615f21aa9fbecb92831e17da697bc (diff) | |
download | ruamel.yaml-0923c2a691f57f9e095f5c4b9a8f06fa78bb46d2.tar.gz |
reimplement CommentedSeq to subclass MutableSequence instead of list
re issue #176
-rw-r--r-- | _test/test_issues.py | 19 | ||||
-rw-r--r-- | comments.py | 87 | ||||
-rw-r--r-- | compat.py | 4 | ||||
-rw-r--r-- | constructor.py | 5 |
4 files changed, 67 insertions, 48 deletions
diff --git a/_test/test_issues.py b/_test/test_issues.py index 7c779d7..9539dc5 100644 --- a/_test/test_issues.py +++ b/_test/test_issues.py @@ -39,8 +39,8 @@ class TestIssues: from ruamel import yaml import re - - + + class SINumber(yaml.YAMLObject): PREFIXES = {'k': 1e3, 'M': 1e6, 'G': 1e9} yaml_loader = yaml.Loader @@ -48,29 +48,29 @@ class TestIssues: yaml_tag = u'!si' yaml_implicit_pattern = re.compile( r'^(?P<value>[0-9]+(?:\.[0-9]+)?)(?P<prefix>[kMG])$') - + @classmethod def from_yaml(cls, loader, node): return cls(node.value) - + @classmethod def to_yaml(cls, dumper, data): return dumper.represent_scalar(cls.yaml_tag, str(data)) - + def __init__(self, *args): m = self.yaml_implicit_pattern.match(args[0]) self.value = float(m.groupdict()['value']) self.prefix = m.groupdict()['prefix'] - + def __str__(self): return str(self.value)+self.prefix - + def __int__(self): return int(self.value*self.PREFIXES[self.prefix]) - + # This fails: yaml.add_implicit_resolver(SINumber.yaml_tag, SINumber.yaml_implicit_pattern) - + ret = yaml.load(""" [1,2,3, !si 10k, 100G] """, Loader=yaml.Loader) @@ -83,7 +83,6 @@ class TestIssues: yaml_str = "[1, 2, 3, !si 10k, 100G]\n" x = round_trip(yaml_str, preserve_quotes=True) # NOQA - def test_issue_102(self): yaml_str = dedent(""" var1: #empty diff --git a/comments.py b/comments.py index c98baf8..2063775 100644 --- a/comments.py +++ b/comments.py @@ -12,7 +12,7 @@ import sys import copy -from ruamel.yaml.compat import ordereddict, PY2, string_types +from ruamel.yaml.compat import ordereddict, PY2, string_types, MutableSequence from ruamel.yaml.scalarstring import ScalarString if PY2: @@ -385,42 +385,73 @@ class CommentedBase(object): raise NotImplementedError -class CommentedSeq(list, CommentedBase): - __slots__ = (Comment.attrib,) +class CommentedSeq(MutableSequence, CommentedBase): + __slots__ = (Comment.attrib, '_lst') - def _yaml_add_comment(self, comment, key=NoComment): - # type: (Any, Optional[Any]) -> None - if key is not NoComment: - self.yaml_key_comment_extend(key, comment) - else: - self.ca.comment = comment + def __init__(self, *args, **kw): + # type: (Any, Any) -> None + self._lst = list(*args, **kw) - def _yaml_add_eol_comment(self, comment, key): + def __getitem__(self, idx): + # type: (Any) -> Any + return self._lst[idx] + + def __setitem__(self, idx, value): # type: (Any, Any) -> None - self._yaml_add_comment(comment, key=key) + # try to preserve the scalarstring type if setting an existing key to a new value + if idx < len(self): + if ( + isinstance(value, string_types) + and not isinstance(value, ScalarString) + and isinstance(self[idx], ScalarString) + ): + value = type(self[idx])(value) + self._lst.__setitem__(idx, value) - def _yaml_get_columnX(self, key): + def __delitem__(self, idx=None): # type: (Any) -> Any - return self.ca.items[key][0].start_mark.column + del self._lst[idx] + self.ca.items.pop(idx, None) # might not be there -> default value + for list_index in sorted(self.ca.items): + if list_index < idx: + continue + self.ca.items[list_index - 1] = self.ca.items.pop(list_index) + + def __len__(self): + # type: () -> int + return len(self._lst) def insert(self, idx, val): # type: (Any, Any) -> None """the comments after the insertion have to move forward""" - list.insert(self, idx, val) + self._lst.insert(idx, val) for list_index in sorted(self.ca.items, reverse=True): if list_index < idx: break self.ca.items[list_index + 1] = self.ca.items.pop(list_index) - def pop(self, idx=None): + def extend(self, val): + # type: (Any) -> None + self._lst.extend(val) + + def __eq__(self, other): + # type: (Any) -> bool + return bool(self._lst == other) + + def _yaml_add_comment(self, comment, key=NoComment): + # type: (Any, Optional[Any]) -> None + if key is not NoComment: + self.yaml_key_comment_extend(key, comment) + else: + self.ca.comment = comment + + def _yaml_add_eol_comment(self, comment, key): + # type: (Any, Any) -> None + self._yaml_add_comment(comment, key=key) + + def _yaml_get_columnX(self, key): # type: (Any) -> Any - res = list.pop(self, idx) - self.ca.items.pop(idx, None) # might not be there -> default value - for list_index in sorted(self.ca.items): - if list_index < idx: - continue - self.ca.items[list_index - 1] = self.ca.items.pop(list_index) - return res + return self.ca.items[key][0].start_mark.column def _yaml_get_column(self, key): # type: (Any) -> Any @@ -461,18 +492,6 @@ class CommentedSeq(list, CommentedBase): self.copy_attributes(res, deep=True) return res - def __setitem__(self, idx, value): - # type: (Any, Any) -> None - # try to preserve the scalarstring type if setting an existing key to a new value - if idx < len(self): - if ( - isinstance(value, string_types) - and not isinstance(value, ScalarString) - and isinstance(self[idx], ScalarString) - ): - value = type(self[idx])(value) - list.__setitem__(self, idx, value) - class CommentedKeySeq(tuple, CommentedBase): """This primarily exists to be able to roundtrip keys that are sequences""" @@ -91,7 +91,7 @@ if PY3: BytesIO = io.BytesIO # have unlimited precision no_limit_int = int - from collections.abc import Hashable # NOQA + from collections.abc import Hashable, MutableSequence # NOQA else: string_types = basestring # NOQA @@ -110,7 +110,7 @@ else: BytesIO = cStringIO.StringIO # have unlimited precision no_limit_int = long # NOQA not available on Python 3 - from collections import Hashable # NOQA + from collections import Hashable, MutableSequence # NOQA if False: # MYPY # StreamType = Union[BinaryIO, IO[str], IO[unicode], StringIO] diff --git a/constructor.py b/constructor.py index 8bf72bb..9de28d0 100644 --- a/constructor.py +++ b/constructor.py @@ -16,7 +16,8 @@ from ruamel.yaml.error import (MarkedYAMLError, MarkedYAMLFutureWarning, from ruamel.yaml.nodes import * # NOQA from ruamel.yaml.nodes import (SequenceNode, MappingNode, ScalarNode) from ruamel.yaml.compat import (utf8, builtins_module, to_str, PY2, PY3, # NOQA - ordereddict, text_type, nprint, version_tnf, Hashable) + ordereddict, text_type, nprint, version_tnf, Hashable, + MutableSequence) from ruamel.yaml.comments import * # NOQA from ruamel.yaml.comments import (CommentedMap, CommentedOrderedMap, CommentedSet, CommentedKeySeq, CommentedSeq, TaggedScalar) @@ -1311,7 +1312,7 @@ class RoundTripConstructor(SafeConstructor): key = self.construct_object(key_node, deep=True) # lists are not hashable, but tuples are if not isinstance(key, Hashable): - if isinstance(key, list): + if isinstance(key, MutableSequence): key_a = CommentedKeySeq(key) if key_node.flow_style is True: key_a.fa.set_flow_style() |