diff options
author | Anthon van der Neut <anthon@mnt.org> | 2016-11-13 11:54:19 +0100 |
---|---|---|
committer | Anthon van der Neut <anthon@mnt.org> | 2016-11-13 11:54:19 +0100 |
commit | 51dfde5d2dc2d568478387104ba0c6398ffa4b60 (patch) | |
tree | 58f064c690f27d65c5855c242ad1222b157b73b4 | |
parent | b96e72c24f8d3a9e2902ba70c653664aad18ec7b (diff) | |
download | ruamel.yaml-51dfde5d2dc2d568478387104ba0c6398ffa4b60.tar.gz |
fix for issue #72, dot allowed in anchor
-rw-r--r-- | README.rst | 16 | ||||
-rw-r--r-- | _test/test_anchor.py | 34 | ||||
-rw-r--r-- | compat.py | 20 | ||||
-rw-r--r-- | emitter.py | 9 | ||||
-rw-r--r-- | nodes.py | 2 | ||||
-rw-r--r-- | representer.py | 2 | ||||
-rw-r--r-- | scanner.py | 13 |
7 files changed, 81 insertions, 15 deletions
@@ -18,8 +18,18 @@ ChangeLog :: + 0.12.15 (2016-09-21): + - allow dot char (and many others) in anchor name + Fix issue 72 (reported by Shalon Wood) + - Slightly smarter behaviour dumping strings when no style is + specified. Single string scalars that start with single quotes + or have newlines now are dumped double quoted: "'abc\nklm'" instead of + '''abc + + klm''' + 0.12.14 (2016-09-21): - - preserve round-trip sequences that are mapping keys + - preserve round-trip sequences that are mapping keys (prompted by stackoverflow question 39595807 from Nowox) 0.12.13 (2016-09-15): @@ -27,7 +37,7 @@ ChangeLog keys incorrect (reported by Tal Liron) 0.12.11 (2016-09-06): - - Fix issue 58 endless loop in scanning tokens (reported by + - Fix issue 58 endless loop in scanning tokens (reported by Christopher Lambert) 0.12.10 (2016-09-05): @@ -45,7 +55,7 @@ ChangeLog - fixing issue 46 empty lines between top-level keys were gobbled (but not between sequence elements, nor between keys in netsted mappings (reported by Alex Harvey) - + 0.12.5 (2016-08-20): - fixing issue 45 preserving datetime formatting (submitted by altuin) Several formatting parameters are preserved with some normalisation: diff --git a/_test/test_anchor.py b/_test/test_anchor.py index 593bf8a..05c8693 100644 --- a/_test/test_anchor.py +++ b/_test/test_anchor.py @@ -17,8 +17,8 @@ def load(s): return round_trip_load(dedent(s)) -def compare(data, s): - assert round_trip_dump(data) == dedent(s) +def compare(d, s): + assert round_trip_dump(d) == dedent(s) class TestAnchorsAliases: @@ -323,3 +323,33 @@ class TestMergeKeysValues: if PY3: ref -= 1 assert len(x) == ref + + +class TestFullCharSetAnchors: + def test_master_of_orion(self): + # https://bitbucket.org/ruamel/yaml/issues/72/not-allowed-in-anchor-names + # submitted by Shalon Wood + yaml_str = ''' + - collection: &Backend.Civilizations.RacialPerk + items: + - key: perk_population_growth_modifier + - *Backend.Civilizations.RacialPerk + ''' + data = load(yaml_str) # NOQA + + def test_roundtrip_00(self): + yaml_str = ''' + - &dotted.words.here + a: 1 + b: 2 + - *dotted.words.here + ''' + data = round_trip(yaml_str) # NOQA + + def test_roundtrip_01(self): + yaml_str = ''' + - &dotted.words.here[a, b] + - *dotted.words.here + ''' + data = load(yaml_str) # NOQA + compare(data, yaml_str.replace('[', ' [')) # an extra space is inserted @@ -120,3 +120,23 @@ def dbg(val=None): def nprint(*args, **kw): if dbg: print(*args, **kw) + +# char checkers following production rules + + +def check_namespace_char(ch): + if u'\x21' <= ch <= u'\x7E': # ! to ~ + return True + if u'\xA0' <= ch <= u'\xD7FF': + return True + if (u'\xE000' <= ch <= u'\xFFFD') and ch != u'\xFEFF': # excl. byte order mark + return True + if u'\x10000' <= ch <= u'\x10FFFF': + return True + return False + + +def check_anchorname_char(ch): + if ch in u',[]{}': + return False + return check_namespace_char(ch) @@ -12,7 +12,8 @@ from __future__ import print_function from ruamel.yaml.error import YAMLError from ruamel.yaml.events import * # NOQA -from ruamel.yaml.compat import utf8, text_type, PY2, nprint, dbg, DBG_EVENT +from ruamel.yaml.compat import utf8, text_type, PY2, nprint, dbg, DBG_EVENT, \ + check_anchorname_char __all__ = ['Emitter', 'EmitterError'] @@ -586,6 +587,9 @@ class Emitter(object): if (not self.flow_level and not self.simple_key_context and self.analysis.allow_block): return self.event.style + if not self.event.style and self.analysis.allow_double_quoted: + if "'" in self.event.value or '\n' in self.event.value: + return '"' if not self.event.style or self.event.style == '\'': if (self.analysis.allow_single_quoted and not (self.simple_key_context and self.analysis.multiline)): @@ -703,8 +707,7 @@ class Emitter(object): if not anchor: raise EmitterError("anchor must not be empty") for ch in anchor: - if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or - u'a' <= ch <= u'z' or ch in u'-_'): + if not check_anchorname_char(ch): raise EmitterError("invalid character %r in the anchor: %r" % (utf8(ch), utf8(anchor))) return anchor @@ -60,7 +60,7 @@ class ScalarNode(Node): " -> double quoted ' -> single quoted | -> literal style - > -> + > -> folding style """ id = 'scalar' diff --git a/representer.py b/representer.py index 77a3f59..56890b3 100644 --- a/representer.py +++ b/representer.py @@ -97,7 +97,7 @@ class BaseRepresenter(object): """ David Fraser: Extract a method to represent keys in mappings, so that a subclass can choose not to quote them (for example) - used in repesent_mapping + used in represent_mapping https://bitbucket.org/davidfraser/pyyaml/commits/d81df6eb95f20cac4a79eed95ae553b5c6f77b8c """ return self.represent_data(data) @@ -32,7 +32,7 @@ from __future__ import print_function # from ruamel.yaml.error import MarkedYAMLError from ruamel.yaml.tokens import * # NOQA -from ruamel.yaml.compat import utf8, unichr, PY3 +from ruamel.yaml.compat import utf8, unichr, PY3, check_anchorname_char __all__ = ['Scanner', 'RoundTripScanner', 'ScannerError'] @@ -937,8 +937,9 @@ class Scanner(object): self.forward() length = 0 ch = self.peek(length) - while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ - or ch in u'-_': + # while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + # or ch in u'-_': + while check_anchorname_char(ch): length += 1 ch = self.peek(length) if not length: @@ -948,8 +949,10 @@ class Scanner(object): % utf8(ch), self.get_mark()) value = self.prefix(length) self.forward(length) - ch = self.peek() - if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`': + # ch1 = ch + # ch = self.peek() # no need to peek, ch is already set + # assert ch1 == ch + if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,[]{}%@`': raise ScannerError( "while scanning an %s" % name, start_mark, "expected alphabetic or numeric character, but found %r" |