summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rst16
-rw-r--r--_test/test_anchor.py34
-rw-r--r--compat.py20
-rw-r--r--emitter.py9
-rw-r--r--nodes.py2
-rw-r--r--representer.py2
-rw-r--r--scanner.py13
7 files changed, 81 insertions, 15 deletions
diff --git a/README.rst b/README.rst
index 5418ead..01b6985 100644
--- a/README.rst
+++ b/README.rst
@@ -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
diff --git a/compat.py b/compat.py
index 001fbf9..1cbe923 100644
--- a/compat.py
+++ b/compat.py
@@ -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)
diff --git a/emitter.py b/emitter.py
index 497f173..51ce3ac 100644
--- a/emitter.py
+++ b/emitter.py
@@ -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
diff --git a/nodes.py b/nodes.py
index 03f158f..214284a 100644
--- a/nodes.py
+++ b/nodes.py
@@ -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)
diff --git a/scanner.py b/scanner.py
index fbe190c..2a57c5d 100644
--- a/scanner.py
+++ b/scanner.py
@@ -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"