summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2015-04-15 10:48:20 +0200
committerAnthon van der Neut <anthon@mnt.org>2015-04-15 10:48:20 +0200
commit09c1cd1447ecc089d219ba80a9319736cb2dfc07 (patch)
treed9650bd12bebbcb06618e3700c1d735d3e28c23f
parent505435a7f1bbd919c4d8ec0b25885f212bb61731 (diff)
downloadruamel.yaml-09c1cd1447ecc089d219ba80a9319736cb2dfc07.tar.gz
added add/update of eol comments
-rw-r--r--CHANGES3
-rw-r--r--Makefile13
-rw-r--r--README.rst73
-rw-r--r--py/__init__.py2
-rw-r--r--py/comments.py86
5 files changed, 166 insertions, 11 deletions
diff --git a/CHANGES b/CHANGES
index 0b9b320..19b6387 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,6 @@
-0.7.3: 2015-04
+0.8: 2015-04-15
- bug fix for non-roundtrip save of ordereddict
+- adding/replacing end of line comments on block style mappings/sequences
0.7.2: 2015-03-29
- support for end-of-line comments on flow style sequences and mappings
diff --git a/Makefile b/Makefile
index 35aedc8..c31716f 100644
--- a/Makefile
+++ b/Makefile
@@ -7,3 +7,16 @@ REGEN:=/home/bin/ruamel_util_new util --published --command YAML --skip-hg
include ~/.config/ruamel_util_new/Makefile.inc
clean: clean_common
+
+
+#tstvenv: testvenv testsetup testtest
+#
+#testvenv:
+# virtualenv -p /opt/python/2.7/bin/python testvenv
+#
+#testsetup:
+# testvenv/bin/pip install -e .
+# testvenv/bin/pip install pytest
+#
+#testtest:
+# testvenv/bin/py.test
diff --git a/README.rst b/README.rst
index 2d34fe7..73a200c 100644
--- a/README.rst
+++ b/README.rst
@@ -8,7 +8,7 @@ which supports YAML1.1
Major differences with PyYAML 3.11:
-- intergrated Python 2 and 3 sources, running on Python 2.6, 2.7 (CPython,
+- intergrated Python 2 and 3 sources, running on Python 2.6, 2.7 (CPython,
PyPy), 3.3 and 3.4.
- round trip mode that **includes comments** (block mode, key ordering kept)
- support for simple lists as mapping keys by transformation to tuples
@@ -29,6 +29,8 @@ Major differences with PyYAML 3.11:
parent container, like comments).
- RoundTrip preservation of flow style sequences ( 'a: b, c, d') (based
on request and test by Anthony Sottile)
+- adding/replacing of comments on block style sequences and mappings
+ with smart column positioning
Round trip including comments
=============================
@@ -37,12 +39,69 @@ The major motivation for this fork is the round-trip capability for
comments. The integration of the sources was just an initial step to
make this easier.
+adding/replacing comments
+-------------------------
+
+Starting with version 0.8, you can add/replace comments on block style
+collections (mappings/sequences resuting in Python dict/list). The basic
+for for this is::
+
+ from __future__ import print_function
+
+ import ruamel.yaml
+
+ inp = """\
+ abc:
+ - a # comment 1
+ xyz:
+ a: 1 # comment 2
+ b: 2
+ c: 3
+ d: 4
+ e: 5
+ f: 6 # comment 3
+ """
+
+ data = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
+ data['abc'].append('b')
+ data['abc'].yaml_add_eol_comment('comment 4', 1) # takes column of comment 1
+ data['xyz'].yaml_add_eol_comment('comment 5', 'c') # takes column of comment 2
+ data['xyz'].yaml_add_eol_comment('comment 6', 'e') # takes column of comment 3
+ data['xyz'].yaml_add_eol_comment('comment 7', 'd', column=20)
+
+ print(ruamel.yaml.dump(data, Dumper=ruamel.yaml.RoundTripDumper), end='')
+
+.. example code add_comment.py
+
+Resulting in::
+
+ abc:
+ - a # comment 1
+ - b # comment 4
+ xyz:
+ a: 1 # comment 2
+ b: 2
+ c: 3 # comment 5
+ d: 4 # comment 7
+ e: 5 # comment 6
+ f: 6 # comment 3
+
+
+.. example output add_comment.py
+
+
+If the comment doesn't start with '#', this will be added. The key is is
+the element index for list, the actual key for dictionaries. As can be seen
+from the example, the column to choose for a comment is derived
+from the previous, next or preceding comment column (picking the first one
+found).
+
Config file formats
--------------------
+===================
-There are only a few configuration file formats that are human
-readable and editable: JSON, INI/ConfigParser, YAML (XML is to verbose
-to be readable).
+There are only a few configuration file formats that are easily
+readable, and editable: JSON, INI/ConfigParser, YAML (XML is to cluttered
+to be called easily readable).
Unfortunately `JSON <http://www.json.org/>`_ doesn't support comments,
and although there are some solutions with pre-processed filtering of
@@ -67,7 +126,7 @@ configuration files that are human readable and editable while at
the same time interpretable and modifiable by a program.
Extending
----------
+=========
There are normally 6 files involved when extending the roundtrip
capabilities: the reader, parser, composer and constructor to go from YAML to
@@ -101,7 +160,7 @@ and generating YAML::
code = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
code['name']['given'] = 'Bob'
- print(ruamel.yaml.dump(code, Dumper= ruamel.yaml.RoundTripDumper), end='')
+ print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')
.. example code small.py
diff --git a/py/__init__.py b/py/__init__.py
index 9e1c057..2eb14d2 100644
--- a/py/__init__.py
+++ b/py/__init__.py
@@ -21,7 +21,7 @@ def _convert_version(tup):
return ret_val
-version_info = (0, 7, 3)
+version_info = (0, 8)
__version__ = _convert_version(version_info)
del _convert_version
diff --git a/py/comments.py b/py/comments.py
index c26ad1d..d39fd87 100644
--- a/py/comments.py
+++ b/py/comments.py
@@ -79,7 +79,7 @@ class Format(object):
def set_block_style(self):
self._flow_style = False
- def flow_style(self, default):
+ def flow_style(self, default=None):
"""if default (the flow_style) is None, the flow style tacked on to
the object explicitly will be taken. If that is None as well the
default flow style rules the format down the line, or the type
@@ -127,6 +127,27 @@ class CommentedBase(object):
return getattr(self, Format.attrib)
+ def yaml_add_eol_comment(self, comment, key=NoComment, column=None):
+ """
+ there is a problem as eol comments should start with ' #'
+ (but at the beginning of the line the space doesn't have to be before
+ the #. The column index is for the # mark
+ """
+ from .tokens import CommentToken
+ from .error import Mark
+ if column is None:
+ column = self._yaml_get_column(key)
+ if comment[0] != '#':
+ comment = '# ' + comment
+ if column is None:
+ if comment[0] == '#':
+ comment = ' ' + comment
+ column = 0
+ start_mark = Mark(None, None, None, column, None, None)
+ ct = [CommentToken(comment, start_mark, None), None]
+ self._yaml_add_eol_comment(ct, key=key)
+
+
class CommentedSeq(list, CommentedBase):
__slots__ = [Comment.attrib, ]
@@ -136,6 +157,32 @@ class CommentedSeq(list, CommentedBase):
else:
self.ca.comment = comment
+ def _yaml_add_eol_comment(self, comment, key):
+ self._yaml_add_comment(comment, key=key)
+
+ def _yaml_get_columnX(self, key):
+ return self.ca.items[key][0].start_mark.column
+
+ def _yaml_get_column(self, key):
+ column = None
+ sel_idx = None
+ pre, post = key-1, key+1
+ if pre in self.ca.items:
+ sel_idx = pre
+ elif post in self.ca.items:
+ sel_idx = post
+ else:
+ # self.ca.items is not ordered
+ for row_idx, k1 in enumerate(self):
+ if row_idx >= key:
+ break
+ if row_idx not in self.ca.items:
+ continue
+ sel_idx = row_idx
+ if sel_idx is not None:
+ column = self._yaml_get_columnX(sel_idx)
+ return column
+
class CommentedMap(ordereddict, CommentedBase):
__slots__ = [Comment.attrib, ]
@@ -144,11 +191,46 @@ class CommentedMap(ordereddict, CommentedBase):
"""values is set to key to indicate a value attachment of comment"""
if key is not NoComment:
self.yaml_key_comment_extend(key, comment)
- elif value is not NoComment:
+ return
+ if value is not NoComment:
self.yaml_value_comment_extend(value, comment)
else:
self.ca.comment = comment
+ def _yaml_add_eol_comment(self, comment, key):
+ """add on the value line, with value specified by the key"""
+ self._yaml_add_comment(comment, value=key)
+
+ def _yaml_get_columnX(self, key):
+ return self.ca.items[key][2].start_mark.column
+
+ def _yaml_get_column(self, key):
+ column = None
+ sel_idx = None
+ pre, post, last = None, None, None
+ for x in self:
+ if pre is not None and x != key:
+ post = x
+ break
+ if x == key:
+ pre = last
+ last = x
+ if pre in self.ca.items:
+ sel_idx = pre
+ elif post in self.ca.items:
+ sel_idx = post
+ else:
+ # self.ca.items is not ordered
+ for row_idx, k1 in enumerate(self):
+ if k1 >= key:
+ break
+ if k1 not in self.ca.items:
+ continue
+ sel_idx = k1
+ if sel_idx is not None:
+ column = self._yaml_get_columnX(sel_idx)
+ return column
+
def update(self, vals):
try:
ordereddict.update(self, vals)