summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2018-11-09 10:47:11 +0100
committerAnthon van der Neut <anthon@mnt.org>2018-11-09 10:47:11 +0100
commit68ea6b9ef755296c169b5416fe46ac48250e3c62 (patch)
tree8a44637378f20053b3c22ed342bdb2d13805dc97
parent11e092f5746f4437628a4131ea8727db6c4c5692 (diff)
downloadruamel.yaml-68ea6b9ef755296c169b5416fe46ac48250e3c62.tar.gz
fix issue #194 object attributes always sorted0.15.77
Test for this issue in: https://bitbucket.org/ruamel/yaml.data/src/default/object/control_base_representer_mapping_sort.yaml *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--CHANGES7
-rw-r--r--README.rst11
-rw-r--r--__init__.py4
-rw-r--r--_doc/_static/pypi.svg2
-rw-r--r--_test/test_z_data.py40
-rw-r--r--main.py16
-rw-r--r--nodes.py15
-rw-r--r--representer.py13
-rw-r--r--scalarstring.py23
9 files changed, 103 insertions, 28 deletions
diff --git a/CHANGES b/CHANGES
index 2c8e973..528789a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,10 @@
+[0, 15, 77]: 2018-11-09
+ - setting `yaml.sort_base_mapping_type_on_output = False`, will prevent
+ explicit sorting by keys in the base representer of mappings. Roundtrip
+ already did not do this. Usage only makes real sense for Python 3.6+
+ (feature request by `Sebastian Gerber <https://bitbucket.org/spacemanspiff2007/>`__).
+ - implement Python version check in YAML metadata in ``_test/test_z_data.py``
+
[0, 15, 76]: 2018-11-01
- fix issue with empty mapping and sequence loaded as flow-style
(mapping reported by `Min RK <https://bitbucket.org/minrk/>`__, sequence
diff --git a/README.rst b/README.rst
index 09960a4..a9a645b 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.76
-:updated: 2018-11-01
+:version: 0.15.77
+:updated: 2018-11-09
:documentation: http://yaml.readthedocs.io
:repository: https://bitbucket.org/ruamel/
:pypi: https://pypi.org/project/ruamel.yaml/
@@ -54,6 +54,13 @@ ChangeLog
.. should insert NEXT: at the beginning of line for next key (with empty line)
+0.15.77 (2018-11-09):
+ - setting `yaml.sort_base_mapping_type_on_output = False`, will prevent
+ explicit sorting by keys in the base representer of mappings. Roundtrip
+ already did not do this. Usage only makes real sense for Python 3.6+
+ (feature request by `Sebastian Gerber <https://bitbucket.org/spacemanspiff2007/>`__).
+ - implement Python version check in YAML metadata in ``_test/test_z_data.py``
+
0.15.76 (2018-11-01):
- fix issue with empty mapping and sequence loaded as flow-style
(mapping reported by `Min RK <https://bitbucket.org/minrk/>`__, sequence
diff --git a/__init__.py b/__init__.py
index 9b28251..bdc47aa 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, 76),
- __version__='0.15.76',
+ version_info=(0, 15, 77),
+ __version__='0.15.77',
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 ef9b8ff..b48d281 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.76</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.76</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.77</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.15.77</text></g> </svg>
diff --git a/_test/test_z_data.py b/_test/test_z_data.py
index d106a2a..b0464ba 100644
--- a/_test/test_z_data.py
+++ b/_test/test_z_data.py
@@ -2,6 +2,7 @@
from __future__ import print_function, unicode_literals
+import sys
import pytest # NOQA
import warnings # NOQA
@@ -83,7 +84,10 @@ def pytest_generate_tests(metafunc):
paths = sorted(base_path.glob('**/*.yaml'))
idlist = []
for path in paths:
- idlist.append(path.stem)
+ stem = path.stem
+ if stem.startswith('.#'): # skip emacs temporary file
+ continue
+ idlist.append(stem)
test_yaml.append([path])
metafunc.parametrize(['yaml'], test_yaml, ids=idlist, scope='class')
@@ -160,6 +164,9 @@ class TestYAMLData(object):
d = docs[0]
typ = d.get('type')
yaml_version = d.get('yaml_version')
+ if 'python' in d:
+ if not check_python_version(d['python']):
+ pytest.skip("unsupported version")
idx += 1
data = output = confirm = python = None
for doc in docs[idx:]:
@@ -196,3 +203,34 @@ class TestYAMLData(object):
else:
print('\nrun type unknown:', typ)
assert False
+
+
+def check_python_version(match, current=None):
+ """
+ version indication, return True if version matches.
+ match should be something like 3.6+, or [2.7, 3.3] etc. Floats
+ are converted to strings. Single values are made into lists.
+ """
+ if current is None:
+ current = list(sys.version_info[:3])
+ if not isinstance(match, list):
+ match = [match]
+ for m in match:
+ minimal = False
+ if isinstance(m, float):
+ m = str(m)
+ if m.endswith('+'):
+ minimal = True
+ m = m[:-1]
+ # assert m[0].isdigit()
+ # assert m[-1].isdigit()
+ m = [int(x) for x in m.split('.')]
+ current_len = current[:len(m)]
+ # print(m, current, current_len)
+ if minimal:
+ if current_len >= m:
+ return True
+ else:
+ if current_len == m:
+ return True
+ return False
diff --git a/main.py b/main.py
index 35613ef..851ae83 100644
--- a/main.py
+++ b/main.py
@@ -148,6 +148,7 @@ class YAML(object):
self.sequence_dash_offset = 0
self.compact_seq_seq = None
self.compact_seq_map = None
+ self.sort_base_mapping_type_on_output = None # default: sort
self.top_level_colon_align = None
self.prefix_colon = None
@@ -289,15 +290,14 @@ class YAML(object):
# type: () -> Any
attr = '_' + sys._getframe().f_code.co_name
if not hasattr(self, attr):
- setattr(
- self,
- attr,
- self.Representer(
- default_style=self.default_style,
- default_flow_style=self.default_flow_style,
- dumper=self,
- ),
+ repres = self.Representer(
+ default_style=self.default_style,
+ default_flow_style=self.default_flow_style,
+ dumper=self,
)
+ if self.sort_base_mapping_type_on_output is not None:
+ repres.sort_base_mapping_type_on_output = self.sort_base_mapping_type_on_output
+ setattr(self, attr, repres)
return getattr(self, attr)
# separate output resolver?
diff --git a/nodes.py b/nodes.py
index 0a35752..d99e909 100644
--- a/nodes.py
+++ b/nodes.py
@@ -12,14 +12,14 @@ if False: # MYPY
class Node(object):
__slots__ = 'tag', 'value', 'start_mark', 'end_mark', 'comment', 'anchor'
- def __init__(self, tag, value, start_mark, end_mark, comment=None):
- # type: (Any, Any, Any, Any, Any) -> None
+ def __init__(self, tag, value, start_mark, end_mark, comment=None, anchor=None):
+ # type: (Any, Any, Any, Any, Any, Any) -> None
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.comment = comment
- self.anchor = None
+ self.anchor = anchor
def __repr__(self):
# type: () -> str
@@ -78,14 +78,15 @@ class ScalarNode(Node):
__slots__ = ('style',)
id = 'scalar'
- def __init__(self, tag, value, start_mark=None, end_mark=None, style=None, comment=None):
- # type: (Any, Any, Any, Any, Any, Any) -> None
- Node.__init__(self, tag, value, start_mark, end_mark, comment=comment)
+ def __init__(self, tag, value, start_mark=None, end_mark=None, style=None, comment=None,
+ anchor=None):
+ # type: (Any, Any, Any, Any, Any, Any, Any) -> None
+ Node.__init__(self, tag, value, start_mark, end_mark, comment=comment, anchor=anchor)
self.style = style
class CollectionNode(Node):
- __slots__ = 'flow_style', 'anchor'
+ __slots__ = ('flow_style', )
def __init__(
self,
diff --git a/representer.py b/representer.py
index e0ac31d..143b7d5 100644
--- a/representer.py
+++ b/representer.py
@@ -65,6 +65,7 @@ class BaseRepresenter(object):
self.represented_objects = {} # type: Dict[Any, Any]
self.object_keeper = [] # type: List[Any]
self.alias_key = None # type: Optional[int]
+ self.sort_base_mapping_type_on_output = True
@property
def serializer(self):
@@ -209,10 +210,11 @@ class BaseRepresenter(object):
best_style = True
if hasattr(mapping, 'items'):
mapping = list(mapping.items())
- try:
- mapping = sorted(mapping)
- except TypeError:
- pass
+ if self.sort_base_mapping_type_on_output:
+ try:
+ mapping = sorted(mapping)
+ except TypeError:
+ pass
for item_key, item_value in mapping:
node_key = self.represent_key(item_key)
node_value = self.represent_data(item_value)
@@ -986,8 +988,7 @@ class RoundTripRepresenter(SafeRepresenter):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
- if ((item_count != 0) or bool(merge_list)) \
- and self.default_flow_style is not None:
+ if ((item_count != 0) or bool(merge_list)) and self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
diff --git a/scalarstring.py b/scalarstring.py
index 03db72d..c7b1a78 100644
--- a/scalarstring.py
+++ b/scalarstring.py
@@ -3,6 +3,7 @@
from __future__ import print_function, absolute_import, division, unicode_literals
from ruamel.yaml.compat import text_type
+from ruamel.yaml.anchor import Anchor
if False: # MYPY
from typing import Text, Any, Dict, List # NOQA
@@ -20,7 +21,7 @@ __all__ = [
class ScalarString(text_type):
- __slots__ = ()
+ __slots__ = (Anchor.attrib)
def __new__(cls, *args, **kw):
# type: (Any, Any) -> Any
@@ -30,6 +31,26 @@ class ScalarString(text_type):
# type: (Any, Any, int) -> Any
return type(self)((text_type.replace(self, old, new, maxreplace)))
+ @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):
+ # type: () -> Any
+ if not hasattr(self, Anchor.attrib):
+ return None
+ if not self.anchor.always_dump:
+ return None
+ return self.anchor
+
+ def yaml_set_anchor(self, value, always_dump=False):
+ # type: (Any, bool) -> None
+ self.anchor.value = value
+ self.anchor.always_dump = always_dump
+
class LiteralScalarString(ScalarString):
__slots__ = 'comment' # the comment after the | on the first line