summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2018-09-21 20:39:48 +0200
committerAnthon van der Neut <anthon@mnt.org>2018-09-21 20:39:48 +0200
commit76b0f89d8fcbf88616f62c01551731cc043f86a3 (patch)
tree249e6d08fa6f8be2d18cba96640244fb47a69a4e
parent7303884f29383c9c0f71547a6e3d41997aac5cb1 (diff)
downloadruamel.yaml-76b0f89d8fcbf88616f62c01551731cc043f86a3.tar.gz
fix #233 fix #231 reverting to subclass dict resp list for CommentedMap/Seq0.15.70
had to reimplement round-trippable mappings with merge-keys to get both JSON dumping and casting using dict(**some_commented_map) to work. (JSON dump of merge maps not tested) *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--CHANGES8
-rw-r--r--README.rst12
-rw-r--r--__init__.py4
-rw-r--r--_doc/_static/pypi.svg2
-rw-r--r--_test/test_issues.py21
-rw-r--r--comments.py50
6 files changed, 61 insertions, 36 deletions
diff --git a/CHANGES b/CHANGES
index 6b8208e..2b64da8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,11 @@
+[0, 15, 70]: 2018-09-21
+ - reverted CommentedMap and CommentedSeq to subclass ordereddict resp. list,
+ reimplemented merge maps so that both ``dict(**commented_map_instance)`` and JSON
+ dumping works. This also allows checking with ``isinstance()`` on ``dict`` resp. ``list``.
+ (Proposed by `Stuart Berg <https://bitbucket.org/stuarteberg/>`__, with feedback
+ from `blhsing <https://stackoverflow.com/users/6890912/blhsing>`__ on
+ `StackOverflow <https://stackoverflow.com/q/52314186/1307905>`__)
+
[0, 15, 69]: 2018-09-20
- fix issue with dump_all gobbling end-of-document comments on parsing
(reported by `Pierre B. <https://bitbucket.org/octplane/>`__)
diff --git a/README.rst b/README.rst
index 607fd5e..04f6783 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.69
-:updated: 2018-09-20
+:version: 0.15.70
+:updated: 2018-09-21
:documentation: http://yaml.readthedocs.io
:repository: https://bitbucket.org/ruamel/
:pypi: https://pypi.org/project/ruamel.yaml/
@@ -54,6 +54,14 @@ ChangeLog
.. should insert NEXT: at the beginning of line for next key (with empty line)
+0.15.70 (2018-09-21):
+ - reverted CommentedMap and CommentedSeq to subclass ordereddict resp. list,
+ reimplemented merge maps so that both ``dict(**commented_map_instance)`` and JSON
+ dumping works. This also allows checking with ``isinstance()`` on ``dict`` resp. ``list``.
+ (Proposed by `Stuart Berg <https://bitbucket.org/stuarteberg/>`__, with feedback
+ from `blhsing <https://stackoverflow.com/users/6890912/blhsing>`__ on
+ `StackOverflow <https://stackoverflow.com/q/52314186/1307905>`__)
+
0.15.69 (2018-09-20):
- fix issue with dump_all gobbling end-of-document comments on parsing
(reported by `Pierre B. <https://bitbucket.org/octplane/>`__)
diff --git a/__init__.py b/__init__.py
index 16bfa0c..6ffb2d4 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, 69),
- __version__='0.15.69',
+ version_info=(0, 15, 70),
+ __version__='0.15.70',
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 c73ced3..8b244c0 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.69</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.69</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.70</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.70</text></g> </svg>
diff --git a/_test/test_issues.py b/_test/test_issues.py
index 2651cff..80be4f4 100644
--- a/_test/test_issues.py
+++ b/_test/test_issues.py
@@ -187,13 +187,12 @@ class TestIssues:
assert res == yaml_str.replace(' b ', ' B ').replace(' d\n', ' D\n')
def test_issue_176_test_slicing(self):
- from ruamel.yaml.comments import CommentedSeq
+ from ruamel.yaml.compat import PY2
mss = round_trip_load('[0, 1, 2, 3, 4]')
assert len(mss) == 5
assert mss[2:2] == []
assert mss[2:4] == [2, 3]
- assert isinstance(mss[2:4], CommentedSeq)
assert mss[1::2] == [1, 3]
# slice assignment
@@ -219,10 +218,18 @@ class TestIssues:
m[1::2] = [42, 43]
assert m == [0, 42, 2, 43, 4]
m = mss[:]
- with pytest.raises(TypeError, match='too many'):
- m[1::2] = [42, 43, 44]
- with pytest.raises(TypeError, match='not enough'):
- m[1::2] = [42]
+ if PY2:
+ with pytest.raises(ValueError, match='attempt to assign'):
+ m[1::2] = [42, 43, 44]
+ else:
+ with pytest.raises(TypeError, match='too many'):
+ m[1::2] = [42, 43, 44]
+ if PY2:
+ with pytest.raises(ValueError, match='attempt to assign'):
+ m[1::2] = [42]
+ else:
+ with pytest.raises(TypeError, match='not enough'):
+ m[1::2] = [42]
m = mss[:]
m += [5]
m[1::2] = [42, 43, 44]
@@ -408,7 +415,6 @@ class TestIssues:
with pytest.raises(ruamel.yaml.parser.ParserError):
yaml.safe_load('{]')
- # @pytest.mark.xfail(strict=True, reason='not a dict subclass', raises=TypeError)
def test_issue_233(self):
from ruamel.yaml import YAML
import json
@@ -417,7 +423,6 @@ class TestIssues:
data = yaml.load('{}')
json_str = json.dumps(data) # NOQA
- @pytest.mark.xfail(strict=True, reason='not a list subclass', raises=TypeError)
def test_issue_233a(self):
from ruamel.yaml import YAML
import json
diff --git a/comments.py b/comments.py
index 9730f0a..b54f45a 100644
--- a/comments.py
+++ b/comments.py
@@ -385,16 +385,16 @@ class CommentedBase(object):
raise NotImplementedError
-class CommentedSeq(MutableSliceableSequence, CommentedBase):
+class CommentedSeq(MutableSliceableSequence, list, CommentedBase):
__slots__ = (Comment.attrib, '_lst')
def __init__(self, *args, **kw):
# type: (Any, Any) -> None
- self._lst = list(*args, **kw)
+ list.__init__(self, *args, **kw)
def __getsingleitem__(self, idx):
# type: (Any) -> Any
- return self._lst[idx]
+ return list.__getitem__(self, idx)
def __setsingleitem__(self, idx, value):
# type: (Any, Any) -> None
@@ -406,11 +406,11 @@ class CommentedSeq(MutableSliceableSequence, CommentedBase):
and isinstance(self[idx], ScalarString)
):
value = type(self[idx])(value)
- self._lst.__setitem__(idx, value)
+ list.__setitem__(self, idx, value)
def __delsingleitem__(self, idx=None):
# type: (Any) -> Any
- del self._lst[idx]
+ list.__delitem__(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:
@@ -419,12 +419,12 @@ class CommentedSeq(MutableSliceableSequence, CommentedBase):
def __len__(self):
# type: () -> int
- return len(self._lst)
+ return list.__len__(self)
def insert(self, idx, val):
# type: (Any, Any) -> None
"""the comments after the insertion have to move forward"""
- self._lst.insert(idx, val)
+ list.insert(self, idx, val)
for list_index in sorted(self.ca.items, reverse=True):
if list_index < idx:
break
@@ -432,11 +432,11 @@ class CommentedSeq(MutableSliceableSequence, CommentedBase):
def extend(self, val):
# type: (Any) -> None
- self._lst.extend(val)
+ list.extend(self, val)
def __eq__(self, other):
# type: (Any) -> bool
- return bool(self._lst == other)
+ return list.__eq__(self, other)
def _yaml_add_comment(self, comment, key=NoComment):
# type: (Any, Optional[Any]) -> None
@@ -494,16 +494,18 @@ class CommentedSeq(MutableSliceableSequence, CommentedBase):
def __add__(self, other):
# type: (Any) -> Any
- return self._lst + other
+ return list.__add__(self, other)
- def sort(self, key=None, reverse=False):
+ def sort(self, key=None, reverse=False): # type: ignore
# type: (Any, bool) -> None
if key is None:
- tmp_lst = sorted(zip(self._lst, range(len(self._lst))), reverse=reverse)
- self._lst = [x[0] for x in tmp_lst]
+ tmp_lst = sorted(zip(self, range(len(self))), reverse=reverse)
+ list.__init__(self, [x[0] for x in tmp_lst])
else:
- tmp_lst = sorted(zip(map(key, self._lst), range(len(self._lst))), reverse=reverse)
- self._lst = [self._lst[x[1]] for x in tmp_lst]
+ tmp_lst = sorted(
+ zip(map(key, list.__iter__(self)), range(len(self))), reverse=reverse
+ )
+ list.__init__(self, [list.__getitem__(self, x[1]) for x in tmp_lst])
itm = self.ca.items
self.ca._items = {}
for idx, x in enumerate(tmp_lst):
@@ -513,7 +515,7 @@ class CommentedSeq(MutableSliceableSequence, CommentedBase):
def __repr__(self):
# type: () -> Any
- return self._lst.__repr__()
+ return list.__repr__(self)
class CommentedKeySeq(tuple, CommentedBase):
@@ -638,13 +640,13 @@ class CommentedMapValuesView(CommentedMapView):
class CommentedMap(MutableMapping, ordereddict, CommentedBase):
- __slots__ = (Comment.attrib, '_ok', '_ref') # own keys
+ __slots__ = (Comment.attrib, '_ok', '_ref')
def __init__(self, *args, **kw):
# type: (Any, Any) -> None
- ordereddict.__init__(self, *args, **kw)
- self._ok = set()
- self._ref = []
+ ordereddict.__init__(self, *args, **kw) # type: ignore
+ self._ok = set() # type: MutableSet[Any] # own keys
+ self._ref = [] # type: List[CommentedMap]
def _yaml_add_comment(self, comment, key=NoComment, value=NoComment):
# type: (Any, Optional[Any], Optional[Any]) -> None
@@ -707,7 +709,7 @@ class CommentedMap(MutableMapping, ordereddict, CommentedBase):
# type: (Any) -> None
try:
ordereddict.update(self, vals)
- self._ok.update(vals.keys())
+ self._ok.update(vals.keys()) # type: ignore
except TypeError:
# probably a dict that is used
for x in vals:
@@ -780,7 +782,7 @@ class CommentedMap(MutableMapping, ordereddict, CommentedBase):
def __contains__(self, key):
# type: (Any) -> bool
- return ordereddict.__contains__(self, key)
+ return bool(ordereddict.__contains__(self, key))
def get(self, key, default=None):
# type: (Any, Any) -> Any
@@ -831,7 +833,7 @@ class CommentedMap(MutableMapping, ordereddict, CommentedBase):
def __len__(self):
# type: () -> int
- return ordereddict.__len__(self)
+ return ordereddict.__len__(self) # type: ignore
def __eq__(self, other):
# type: (Any) -> bool
@@ -922,6 +924,7 @@ class CommentedMap(MutableMapping, ordereddict, CommentedBase):
return x
def add_referent(self, cm):
+ # type: (Any) -> None
if cm not in self._ref:
self._ref.append(cm)
@@ -936,6 +939,7 @@ class CommentedMap(MutableMapping, ordereddict, CommentedBase):
self.merge.extend(value)
def update_key_value(self, key):
+ # type: (Any) -> None
if key in self._ok:
return
for v in self.merge: