yaml: use ruamel.yaml instead of pyyaml

- ruamel.yaml allows round-tripping comments from/to files
- ruamel.yaml is single-source, python2/python3 compatible
This commit is contained in:
Todd Gamblin 2018-08-19 18:11:52 -07:00
parent 2e8a820afd
commit 63004e3de1
68 changed files with 5165 additions and 8136 deletions

View file

@ -49,13 +49,6 @@ if sys.version_info[:2] == (2, 6):
sys.path.insert(0, spack_external_libs) sys.path.insert(0, spack_external_libs)
# Handle vendoring of YAML specially, as it has two versions.
if sys.version_info[0] == 2:
spack_yaml_libs = os.path.join(spack_external_libs, "yaml/lib")
else:
spack_yaml_libs = os.path.join(spack_external_libs, "yaml/lib3")
sys.path.insert(0, spack_yaml_libs)
# Once we've set up the system path, run the spack main method # Once we've set up the system path, run the spack main method
import spack.main # noqa import spack.main # noqa
sys.exit(spack.main.main()) sys.exit(spack.main.main())

View file

@ -118,12 +118,17 @@
vendored copy ever needs to be updated again: vendored copy ever needs to be updated again:
https://github.com/spack/spack/pull/6801/commits/ff513c39f2c67ff615de5cbc581dd69a8ec96526 https://github.com/spack/spack/pull/6801/commits/ff513c39f2c67ff615de5cbc581dd69a8ec96526
pyyaml ruamel.yaml
------ ------
* Homepage: https://pypi.python.org/pypi/PyYAML * Homepage: https://yaml.readthedocs.io/
* Usage: Used for config files. * Usage: Used for config files. Ruamel is based on PyYAML but is more
* Version: 3.12 actively maintained and has more features, including round-tripping
comments read from config files.
* Version: 0.11.15 (last version supporting Python 2.6)
* Note: This package has been slightly modified to improve Python 2.6
compatibility -- some ``{}`` format strings were replaced, and the
import for ``OrderedDict`` was tweaked.
six six
--- ---

0
lib/spack/external/ruamel/__init__.py vendored Normal file
View file

View file

@ -0,0 +1,2 @@
import pkg_resources
pkg_resources.declare_namespace(__name__)

21
lib/spack/external/ruamel/yaml/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,38 @@
ruamel.yaml
===========
``ruamel.yaml`` is a YAML 1.2 loader/dumper package for Python.
* `Overview <http://yaml.readthedocs.org/en/latest/overview.html>`_
* `Installing <http://yaml.readthedocs.org/en/latest/install.html>`_
* `Details <http://yaml.readthedocs.org/en/latest/detail.html>`_
* `Examples <http://yaml.readthedocs.org/en/latest/example.html>`_
* `Differences with PyYAML <http://yaml.readthedocs.org/en/latest/pyyaml.html>`_
.. image:: https://readthedocs.org/projects/yaml/badge/?version=stable
:target: https://yaml.readthedocs.org/en/stable
ChangeLog
=========
::
0.11.15 (2016-XX-XX):
- Change to prevent FutureWarning in NumPy, as reported by tgehring
("comparison to None will result in an elementwise object comparison in the future")
0.11.14 (2016-07-06):
- fix preserve_quotes missing on original Loaders (as reported
by Leynos, bitbucket issue 38)
0.11.13 (2016-07-06):
- documentation only, automated linux wheels
0.11.12 (2016-07-06):
- added support for roundtrip of single/double quoted scalars using:
ruamel.yaml.round_trip_load(stream, preserve_quotes=True)
0.11.0 (2016-02-18):
- RoundTripLoader loads 1.2 by default (no sexagesimals, 012 octals nor
yes/no/on/off booleans

View file

@ -0,0 +1,85 @@
# coding: utf-8
from __future__ import print_function
from __future__ import absolute_import
# install_requires of ruamel.base is not really required but the old
# ruamel.base installed __init__.py, and thus a new version should
# be installed at some point
_package_data = dict(
full_package_name="ruamel.yaml",
version_info=(0, 11, 15),
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
entry_points=None,
install_requires=dict(
any=[],
py26=["ruamel.ordereddict"],
py27=["ruamel.ordereddict"]
),
ext_modules=[dict(
name="_ruamel_yaml",
src=["ext/_ruamel_yaml.c", "ext/api.c", "ext/writer.c", "ext/dumper.c",
"ext/loader.c", "ext/reader.c", "ext/scanner.c", "ext/parser.c",
"ext/emitter.c"],
lib=[],
# test='#include "ext/yaml.h"\n\nint main(int argc, char* argv[])\n{\nyaml_parser_t parser;\nparser = parser; /* prevent warning */\nreturn 0;\n}\n' # NOQA
)
],
classifiers=[
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Python :: Implementation :: Jython",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Text Processing :: Markup"
],
windows_wheels=True,
read_the_docs='yaml',
many_linux='libyaml-devel',
)
# < from ruamel.util.new import _convert_version
def _convert_version(tup):
"""create a PEP 386 pseudo-format conformant string from tuple tup"""
ret_val = str(tup[0]) # first is always digit
next_sep = "." # separator for next extension, can be "" or "."
for x in tup[1:]:
if isinstance(x, int):
ret_val += next_sep + str(x)
next_sep = '.'
continue
first_letter = x[0].lower()
next_sep = ''
if first_letter in 'abcr':
ret_val += 'rc' if first_letter == 'r' else first_letter
elif first_letter in 'pd':
ret_val += '.post' if first_letter == 'p' else '.dev'
return ret_val
# <
version_info = _package_data['version_info']
__version__ = _convert_version(version_info)
del _convert_version
try:
from .cyaml import * # NOQA
__with_libyaml__ = True
except (ImportError, ValueError): # for Jython
__with_libyaml__ = False
# body extracted to main.py
try:
from .main import * # NOQA
except ImportError:
from ruamel.yaml.main import * # NOQA

View file

@ -0,0 +1,481 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import print_function
"""
stuff to deal with comments and formatting on dict/list/ordereddict/set
these are not really related, formatting could be factored out as
a separate base
"""
from collections import MutableSet
__all__ = ["CommentedSeq", "CommentedMap", "CommentedOrderedMap",
"CommentedSet", 'comment_attrib', 'merge_attrib']
try:
from .compat import ordereddict
except ImportError:
from ruamel.yaml.compat import ordereddict
comment_attrib = '_yaml_comment'
format_attrib = '_yaml_format'
line_col_attrib = '_yaml_line_col'
anchor_attrib = '_yaml_anchor'
merge_attrib = '_yaml_merge'
tag_attrib = '_yaml_tag'
class Comment(object):
# sys.getsize tested the Comment objects, __slots__ make them bigger
# and adding self.end did not matter
attrib = comment_attrib
def __init__(self):
self.comment = None # [post, [pre]]
# map key (mapping/omap/dict) or index (sequence/list) to a list of
# dict: post_key, pre_key, post_value, pre_value
# list: pre item, post item
self._items = {}
# self._start = [] # should not put these on first item
self._end = [] # end of document comments
def __str__(self):
if self._end:
end = ',\n end=' + str(self._end)
else:
end = ''
return "Comment(comment={0},\n items={1}{2})".format(
self.comment, self._items, end)
@property
def items(self):
return self._items
@property
def end(self):
return self._end
@end.setter
def end(self, value):
self._end = value
@property
def start(self):
return self._start
@start.setter
def start(self, value):
self._start = value
# to distinguish key from None
def NoComment():
pass
class Format(object):
attrib = format_attrib
def __init__(self):
self._flow_style = None
def set_flow_style(self):
self._flow_style = True
def set_block_style(self):
self._flow_style = False
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
of the constituent values (simple -> flow, map/list -> block)"""
if self._flow_style is None:
return default
return self._flow_style
class LineCol(object):
attrib = line_col_attrib
def __init__(self):
self.line = None
self.col = None
self.data = None
def add_kv_line_col(self, key, data):
if self.data is None:
self.data = {}
self.data[key] = data
def key(self, k):
return self._kv(k, 0, 1)
def value(self, k):
return self._kv(k, 2, 3)
def _kv(self, k, x0, x1):
if self.data is None:
return None
data = self.data[k]
return data[x0], data[x1]
def item(self, idx):
if self.data is None:
return None
return self.data[idx][0], self.data[idx][1]
def add_idx_line_col(self, key, data):
if self.data is None:
self.data = {}
self.data[key] = data
class Anchor(object):
attrib = anchor_attrib
def __init__(self):
self.value = None
self.always_dump = False
class Tag(object):
"""store tag information for roundtripping"""
attrib = tag_attrib
def __init__(self):
self.value = None
class CommentedBase(object):
@property
def ca(self):
if not hasattr(self, Comment.attrib):
setattr(self, Comment.attrib, Comment())
return getattr(self, Comment.attrib)
def yaml_end_comment_extend(self, comment, clear=False):
if clear:
self.ca.end = []
self.ca.end.extend(comment)
def yaml_key_comment_extend(self, key, comment, clear=False):
l = self.ca._items.setdefault(key, [None, None, None, None])
if clear or l[1] is None:
if comment[1] is not None:
assert isinstance(comment[1], list)
l[1] = comment[1]
else:
l[1].extend(comment[0])
l[0] = comment[0]
def yaml_value_comment_extend(self, key, comment, clear=False):
l = self.ca._items.setdefault(key, [None, None, None, None])
if clear or l[3] is None:
if comment[1] is not None:
assert isinstance(comment[1], list)
l[3] = comment[1]
else:
l[3].extend(comment[0])
l[2] = comment[0]
def yaml_set_start_comment(self, comment, indent=0):
"""overwrites any preceding comment lines on an object
expects comment to be without `#` and possible have mutlple lines
"""
from .error import Mark
from .tokens import CommentToken
pre_comments = self._yaml_get_pre_comment()
if comment[-1] == '\n':
comment = comment[:-1] # strip final newline if there
start_mark = Mark(None, None, None, indent, None, None)
for com in comment.split('\n'):
pre_comments.append(CommentToken('# ' + com + '\n', start_mark, None))
@property
def fa(self):
"""format attribute
set_flow_style()/set_block_style()"""
if not hasattr(self, Format.attrib):
setattr(self, Format.attrib, Format())
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)
@property
def lc(self):
if not hasattr(self, LineCol.attrib):
setattr(self, LineCol.attrib, LineCol())
return getattr(self, LineCol.attrib)
def _yaml_set_line_col(self, line, col):
self.lc.line = line
self.lc.col = col
def _yaml_set_kv_line_col(self, key, data):
self.lc.add_kv_line_col(key, data)
def _yaml_set_idx_line_col(self, key, data):
self.lc.add_idx_line_col(key, data)
@property
def anchor(self):
if not hasattr(self, Anchor.attrib):
setattr(self, Anchor.attrib, Anchor())
return getattr(self, Anchor.attrib)
def yaml_anchor(self):
if not hasattr(self, Anchor.attrib):
return None
return self.anchor
def yaml_set_anchor(self, value, always_dump=False):
self.anchor.value = value
self.anchor.always_dump = always_dump
@property
def tag(self):
if not hasattr(self, Tag.attrib):
setattr(self, Tag.attrib, Tag())
return getattr(self, Tag.attrib)
def yaml_set_tag(self, value):
self.tag.value = value
class CommentedSeq(list, CommentedBase):
__slots__ = [Comment.attrib, ]
def _yaml_add_comment(self, comment, key=NoComment):
if key is not NoComment:
self.yaml_key_comment_extend(key, comment)
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 insert(self, idx, val):
"""the comments after the insertion have to move forward"""
list.insert(self, idx, val)
for list_index in sorted(self.ca.items, reverse=True):
if list_index < idx:
break
self.ca.items[list_index+1] = self.ca.items.pop(list_index)
def pop(self, idx):
res = list.pop(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:
continue
self.ca.items[list_index-1] = self.ca.items.pop(list_index)
return res
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
def _yaml_get_pre_comment(self):
if self.ca.comment is None:
pre_comments = []
self.ca.comment = [None, pre_comments]
else:
pre_comments = self.ca.comment[1] = []
return pre_comments
class CommentedMap(ordereddict, CommentedBase):
__slots__ = [Comment.attrib, ]
def _yaml_add_comment(self, comment, key=NoComment, value=NoComment):
"""values is set to key to indicate a value attachment of comment"""
if key is not NoComment:
self.yaml_key_comment_extend(key, comment)
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 _yaml_get_pre_comment(self):
if self.ca.comment is None:
pre_comments = []
self.ca.comment = [None, pre_comments]
else:
pre_comments = self.ca.comment[1] = []
return pre_comments
def update(self, vals):
try:
ordereddict.update(self, vals)
except TypeError:
# probably a dict that is used
for x in vals:
self[x] = vals[x]
def insert(self, pos, key, value, comment=None):
"""insert key value into given position
attach comment if provided
"""
ordereddict.insert(self, pos, key, value)
if comment is not None:
self.yaml_add_eol_comment(comment, key=key)
def mlget(self, key, default=None, list_ok=False):
"""multi-level get that expects dicts within dicts"""
if not isinstance(key, list):
return self.get(key, default)
# assume that the key is a list of recursively accessible dicts
def get_one_level(key_list, level, d):
if not list_ok:
assert isinstance(d, dict)
if level >= len(key_list):
if level > len(key_list):
raise IndexError
return d[key_list[level-1]]
return get_one_level(key_list, level+1, d[key_list[level-1]])
try:
return get_one_level(key, 1, self)
except KeyError:
return default
except (TypeError, IndexError):
if not list_ok:
raise
return default
def __getitem__(self, key):
try:
return ordereddict.__getitem__(self, key)
except KeyError:
for merged in getattr(self, merge_attrib, []):
if key in merged[1]:
return merged[1][key]
raise
def get(self, key, default=None):
try:
return self.__getitem__(key)
except:
return default
@property
def merge(self):
if not hasattr(self, merge_attrib):
setattr(self, merge_attrib, [])
return getattr(self, merge_attrib)
def add_yaml_merge(self, value):
self.merge.extend(value)
class CommentedOrderedMap(CommentedMap):
__slots__ = [Comment.attrib, ]
class CommentedSet(MutableSet, CommentedMap):
__slots__ = [Comment.attrib, 'odict']
def __init__(self, values=None):
self.odict = ordereddict()
MutableSet.__init__(self)
if values is not None:
self |= values
def add(self, value):
"""Add an element."""
self.odict[value] = None
def discard(self, value):
"""Remove an element. Do not raise an exception if absent."""
del self.odict[value]
def __contains__(self, x):
return x in self.odict
def __iter__(self):
for x in self.odict:
yield x
def __len__(self):
return len(self.odict)
def __repr__(self):
return 'set({0!r})'.format(self.odict.keys())

120
lib/spack/external/ruamel/yaml/compat.py vendored Normal file
View file

@ -0,0 +1,120 @@
# coding: utf-8
from __future__ import print_function
# partially from package six by Benjamin Peterson
import sys
import os
import types
try:
from ruamel.ordereddict import ordereddict
except:
try:
from collections import OrderedDict # nopyqver
except ImportError:
from ordereddict import OrderedDict
# to get the right name import ... as ordereddict doesn't do that
class ordereddict(OrderedDict):
if not hasattr(OrderedDict, 'insert'):
def insert(self, pos, key, value):
if pos >= len(self):
self[key] = value
return
od = ordereddict()
od.update(self)
for k in od:
del self[k]
for index, old_key in enumerate(od):
if pos == index:
self[key] = value
self[old_key] = od[old_key]
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3:
def utf8(s):
return s
def to_str(s):
return s
def to_unicode(s):
return s
else:
def utf8(s):
return s.encode('utf-8')
def to_str(s):
return str(s)
def to_unicode(s):
return unicode(s)
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
unichr = chr
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
unichr = unichr # to allow importing
import StringIO
StringIO = StringIO.StringIO
import cStringIO
BytesIO = cStringIO.StringIO
if PY3:
builtins_module = 'builtins'
else:
builtins_module = '__builtin__'
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
return meta("NewBase", bases, {})
DBG_TOKEN = 1
DBG_EVENT = 2
DBG_NODE = 4
_debug = None
# used from yaml util when testing
def dbg(val=None):
global _debug
if _debug is None:
# set to true or false
_debug = os.environ.get('YAMLDEBUG')
if _debug is None:
_debug = 0
else:
_debug = int(_debug)
if val is None:
return _debug
return _debug & val
def nprint(*args, **kw):
if dbg:
print(*args, **kw)

View file

@ -1,15 +1,32 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import print_function
try:
from .error import MarkedYAMLError
from .compat import utf8
except (ImportError, ValueError): # for Jython
from ruamel.yaml.error import MarkedYAMLError
from ruamel.yaml.compat import utf8
from ruamel.yaml.events import (
StreamStartEvent, StreamEndEvent, MappingStartEvent, MappingEndEvent,
SequenceStartEvent, SequenceEndEvent, AliasEvent, ScalarEvent,
)
from ruamel.yaml.nodes import (
MappingNode, ScalarNode, SequenceNode,
)
__all__ = ['Composer', 'ComposerError'] __all__ = ['Composer', 'ComposerError']
from error import MarkedYAMLError
from events import *
from nodes import *
class ComposerError(MarkedYAMLError): class ComposerError(MarkedYAMLError):
pass pass
class Composer(object):
class Composer(object):
def __init__(self): def __init__(self):
self.anchors = {} self.anchors = {}
@ -38,9 +55,10 @@ def get_single_node(self):
# Ensure that the stream contains no more documents. # Ensure that the stream contains no more documents.
if not self.check_event(StreamEndEvent): if not self.check_event(StreamEndEvent):
event = self.get_event() event = self.get_event()
raise ComposerError("expected a single document in the stream", raise ComposerError(
document.start_mark, "but found another document", "expected a single document in the stream",
event.start_mark) document.start_mark, "but found another document",
event.start_mark)
# Drop the STREAM-END event. # Drop the STREAM-END event.
self.get_event() self.get_event()
@ -63,18 +81,20 @@ def compose_document(self):
def compose_node(self, parent, index): def compose_node(self, parent, index):
if self.check_event(AliasEvent): if self.check_event(AliasEvent):
event = self.get_event() event = self.get_event()
anchor = event.anchor alias = event.anchor
if anchor not in self.anchors: if alias not in self.anchors:
raise ComposerError(None, None, "found undefined alias %r" raise ComposerError(
% anchor.encode('utf-8'), event.start_mark) None, None, "found undefined alias %r"
return self.anchors[anchor] % utf8(alias), event.start_mark)
return self.anchors[alias]
event = self.peek_event() event = self.peek_event()
anchor = event.anchor anchor = event.anchor
if anchor is not None: if anchor is not None: # have an anchor
if anchor in self.anchors: if anchor in self.anchors:
raise ComposerError("found duplicate anchor %r; first occurence" raise ComposerError(
% anchor.encode('utf-8'), self.anchors[anchor].start_mark, "found duplicate anchor %r; first occurence"
"second occurence", event.start_mark) % utf8(anchor), self.anchors[anchor].start_mark,
"second occurence", event.start_mark)
self.descend_resolver(parent, index) self.descend_resolver(parent, index)
if self.check_event(ScalarEvent): if self.check_event(ScalarEvent):
node = self.compose_scalar_node(anchor) node = self.compose_scalar_node(anchor)
@ -91,7 +111,8 @@ def compose_scalar_node(self, anchor):
if tag is None or tag == u'!': if tag is None or tag == u'!':
tag = self.resolve(ScalarNode, event.value, event.implicit) tag = self.resolve(ScalarNode, event.value, event.implicit)
node = ScalarNode(tag, event.value, node = ScalarNode(tag, event.value,
event.start_mark, event.end_mark, style=event.style) event.start_mark, event.end_mark, style=event.style,
comment=event.comment)
if anchor is not None: if anchor is not None:
self.anchors[anchor] = node self.anchors[anchor] = node
return node return node
@ -102,8 +123,9 @@ def compose_sequence_node(self, anchor):
if tag is None or tag == u'!': if tag is None or tag == u'!':
tag = self.resolve(SequenceNode, None, start_event.implicit) tag = self.resolve(SequenceNode, None, start_event.implicit)
node = SequenceNode(tag, [], node = SequenceNode(tag, [],
start_event.start_mark, None, start_event.start_mark, None,
flow_style=start_event.flow_style) flow_style=start_event.flow_style,
comment=start_event.comment, anchor=anchor)
if anchor is not None: if anchor is not None:
self.anchors[anchor] = node self.anchors[anchor] = node
index = 0 index = 0
@ -111,7 +133,13 @@ def compose_sequence_node(self, anchor):
node.value.append(self.compose_node(node, index)) node.value.append(self.compose_node(node, index))
index += 1 index += 1
end_event = self.get_event() end_event = self.get_event()
if node.flow_style is True and end_event.comment is not None:
if node.comment is not None:
print('Warning: unexpected end_event commment in sequence '
'node {0}'.format(node.flow_style))
node.comment = end_event.comment
node.end_mark = end_event.end_mark node.end_mark = end_event.end_mark
self.check_end_doc_comment(end_event, node)
return node return node
def compose_mapping_node(self, anchor): def compose_mapping_node(self, anchor):
@ -120,20 +148,35 @@ def compose_mapping_node(self, anchor):
if tag is None or tag == u'!': if tag is None or tag == u'!':
tag = self.resolve(MappingNode, None, start_event.implicit) tag = self.resolve(MappingNode, None, start_event.implicit)
node = MappingNode(tag, [], node = MappingNode(tag, [],
start_event.start_mark, None, start_event.start_mark, None,
flow_style=start_event.flow_style) flow_style=start_event.flow_style,
comment=start_event.comment, anchor=anchor)
if anchor is not None: if anchor is not None:
self.anchors[anchor] = node self.anchors[anchor] = node
while not self.check_event(MappingEndEvent): while not self.check_event(MappingEndEvent):
#key_event = self.peek_event() # key_event = self.peek_event()
item_key = self.compose_node(node, None) item_key = self.compose_node(node, None)
#if item_key in node.value: # if item_key in node.value:
# raise ComposerError("while composing a mapping", start_event.start_mark, # raise ComposerError("while composing a mapping",
# "found duplicate key", key_event.start_mark) # start_event.start_mark,
# "found duplicate key", key_event.start_mark)
item_value = self.compose_node(node, item_key) item_value = self.compose_node(node, item_key)
#node.value[item_key] = item_value # node.value[item_key] = item_value
node.value.append((item_key, item_value)) node.value.append((item_key, item_value))
end_event = self.get_event() end_event = self.get_event()
if node.flow_style is True and end_event.comment is not None:
node.comment = end_event.comment
node.end_mark = end_event.end_mark node.end_mark = end_event.end_mark
self.check_end_doc_comment(end_event, node)
return node return node
def check_end_doc_comment(self, end_event, node):
if end_event.comment and end_event.comment[1]:
# pre comments on an end_event, no following to move to
if node.comment is None:
node.comment = [None, None]
assert not isinstance(node, ScalarEvent)
# this is a post comment on a mapping node, add as third element
# in the list
node.comment.append(end_event.comment[1])
end_event.comment[1] = None

View file

@ -0,0 +1,9 @@
# coding: utf-8
import warnings
from ruamel.yaml.util import configobj_walker as new_configobj_walker
def configobj_walker(cfg):
warnings.warn("configobj_walker has move to ruamel.yaml.util, please update your code")
return new_configobj_walker(cfg)

File diff suppressed because it is too large Load diff

102
lib/spack/external/ruamel/yaml/dumper.py vendored Normal file
View file

@ -0,0 +1,102 @@
# coding: utf-8
from __future__ import absolute_import
__all__ = ['BaseDumper', 'SafeDumper', 'Dumper', 'RoundTripDumper']
try:
from .emitter import * # NOQA
from .serializer import * # NOQA
from .representer import * # NOQA
from .resolver import * # NOQA
except (ImportError, ValueError): # for Jython
from ruamel.yaml.emitter import * # NOQA
from ruamel.yaml.serializer import * # NOQA
from ruamel.yaml.representer import * # NOQA
from ruamel.yaml.resolver import * # NOQA
class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
block_seq_indent=block_seq_indent)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
block_seq_indent=block_seq_indent)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class Dumper(Emitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
block_seq_indent=block_seq_indent)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class RoundTripDumper(Emitter, Serializer, RoundTripRepresenter, VersionedResolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
block_seq_indent=block_seq_indent,
top_level_colon_align=top_level_colon_align,
prefix_colon=prefix_colon)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags)
RoundTripRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
VersionedResolver.__init__(self)

View file

@ -1,3 +1,7 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import print_function
# Emitter expects events obeying the following grammar: # Emitter expects events obeying the following grammar:
# stream ::= STREAM-START document* STREAM-END # stream ::= STREAM-START document* STREAM-END
@ -8,17 +12,25 @@
__all__ = ['Emitter', 'EmitterError'] __all__ = ['Emitter', 'EmitterError']
from error import YAMLError try:
from events import * from .error import YAMLError
from .events import * # NOQA
from .compat import utf8, text_type, PY2, nprint, dbg, DBG_EVENT
except (ImportError, ValueError): # for Jython
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
class EmitterError(YAMLError): class EmitterError(YAMLError):
pass pass
class ScalarAnalysis(object): class ScalarAnalysis(object):
def __init__(self, scalar, empty, multiline, def __init__(self, scalar, empty, multiline,
allow_flow_plain, allow_block_plain, allow_flow_plain, allow_block_plain,
allow_single_quoted, allow_double_quoted, allow_single_quoted, allow_double_quoted,
allow_block): allow_block):
self.scalar = scalar self.scalar = scalar
self.empty = empty self.empty = empty
self.multiline = multiline self.multiline = multiline
@ -28,15 +40,18 @@ def __init__(self, scalar, empty, multiline,
self.allow_double_quoted = allow_double_quoted self.allow_double_quoted = allow_double_quoted
self.allow_block = allow_block self.allow_block = allow_block
class Emitter(object):
class Emitter(object):
DEFAULT_TAG_PREFIXES = { DEFAULT_TAG_PREFIXES = {
u'!' : u'!', u'!': u'!',
u'tag:yaml.org,2002:' : u'!!', u'tag:yaml.org,2002:': u'!!',
} }
MAX_SIMPLE_KEY_LENGTH = 128
def __init__(self, stream, canonical=None, indent=None, width=None, def __init__(self, stream, canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None): allow_unicode=None, line_break=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
# The stream should have the methods `write` and possibly `flush`. # The stream should have the methods `write` and possibly `flush`.
self.stream = stream self.stream = stream
@ -75,16 +90,25 @@ def __init__(self, stream, canonical=None, indent=None, width=None,
self.column = 0 self.column = 0
self.whitespace = True self.whitespace = True
self.indention = True self.indention = True
self.no_newline = None # set if directly after `- `
# Whether the document requires an explicit document indicator # Whether the document requires an explicit document indicator
self.open_ended = False self.open_ended = False
# colon handling
self.colon = u':'
self.prefixed_colon = self.colon if prefix_colon is None else prefix_colon + self.colon
# Formatting details. # Formatting details.
self.canonical = canonical self.canonical = canonical
self.allow_unicode = allow_unicode self.allow_unicode = allow_unicode
self.block_seq_indent = block_seq_indent if block_seq_indent else 0
self.top_level_colon_align = top_level_colon_align
self.best_indent = 2 self.best_indent = 2
if indent and 1 < indent < 10: if indent and 1 < indent < 10:
self.best_indent = indent self.best_indent = indent
# if self.best_indent < self.block_seq_indent + 1:
# self.best_indent = self.block_seq_indent + 1
self.best_width = 80 self.best_width = 80
if width and width > self.best_indent*2: if width and width > self.best_indent*2:
self.best_width = width self.best_width = width
@ -109,6 +133,8 @@ def dispose(self):
self.state = None self.state = None
def emit(self, event): def emit(self, event):
if dbg(DBG_EVENT):
nprint(event)
self.events.append(event) self.events.append(event)
while not self.need_more_events(): while not self.need_more_events():
self.event = self.events.pop(0) self.event = self.events.pop(0)
@ -143,7 +169,7 @@ def need_events(self, count):
return False return False
return (len(self.events) < count+1) return (len(self.events) < count+1)
def increase_indent(self, flow=False, indentless=False): def increase_indent(self, flow=False, sequence=None, indentless=False):
self.indents.append(self.indent) self.indents.append(self.indent)
if self.indent is None: if self.indent is None:
if flow: if flow:
@ -152,6 +178,8 @@ def increase_indent(self, flow=False, indentless=False):
self.indent = 0 self.indent = 0
elif not indentless: elif not indentless:
self.indent += self.best_indent self.indent += self.best_indent
# if self.sequence_context and (self.block_seq_indent + 2) > self.best_indent:
# self.indent = self.block_seq_indent + 2
# States. # States.
@ -159,13 +187,19 @@ def increase_indent(self, flow=False, indentless=False):
def expect_stream_start(self): def expect_stream_start(self):
if isinstance(self.event, StreamStartEvent): if isinstance(self.event, StreamStartEvent):
if self.event.encoding and not getattr(self.stream, 'encoding', None): if PY2:
self.encoding = self.event.encoding if self.event.encoding \
and not getattr(self.stream, 'encoding', None):
self.encoding = self.event.encoding
else:
if self.event.encoding \
and not hasattr(self.stream, 'encoding'):
self.encoding = self.event.encoding
self.write_stream_start() self.write_stream_start()
self.state = self.expect_first_document_start self.state = self.expect_first_document_start
else: else:
raise EmitterError("expected StreamStartEvent, but got %s" raise EmitterError("expected StreamStartEvent, but got %s" %
% self.event) self.event)
def expect_nothing(self): def expect_nothing(self):
raise EmitterError("expected nothing, but got %s" % self.event) raise EmitterError("expected nothing, but got %s" % self.event)
@ -185,17 +219,19 @@ def expect_document_start(self, first=False):
self.write_version_directive(version_text) self.write_version_directive(version_text)
self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy()
if self.event.tags: if self.event.tags:
handles = self.event.tags.keys() handles = sorted(self.event.tags.keys())
handles.sort()
for handle in handles: for handle in handles:
prefix = self.event.tags[handle] prefix = self.event.tags[handle]
self.tag_prefixes[prefix] = handle self.tag_prefixes[prefix] = handle
handle_text = self.prepare_tag_handle(handle) handle_text = self.prepare_tag_handle(handle)
prefix_text = self.prepare_tag_prefix(prefix) prefix_text = self.prepare_tag_prefix(prefix)
self.write_tag_directive(handle_text, prefix_text) self.write_tag_directive(handle_text, prefix_text)
implicit = (first and not self.event.explicit and not self.canonical implicit = (first and
and not self.event.version and not self.event.tags not self.event.explicit and
and not self.check_empty_document()) not self.canonical and
not self.event.version and
not self.event.tags and
not self.check_empty_document())
if not implicit: if not implicit:
self.write_indent() self.write_indent()
self.write_indicator(u'---', True) self.write_indicator(u'---', True)
@ -209,8 +245,8 @@ def expect_document_start(self, first=False):
self.write_stream_end() self.write_stream_end()
self.state = self.expect_nothing self.state = self.expect_nothing
else: else:
raise EmitterError("expected DocumentStartEvent, but got %s" raise EmitterError("expected DocumentStartEvent, but got %s" %
% self.event) self.event)
def expect_document_end(self): def expect_document_end(self):
if isinstance(self.event, DocumentEndEvent): if isinstance(self.event, DocumentEndEvent):
@ -221,8 +257,8 @@ def expect_document_end(self):
self.flush_stream() self.flush_stream()
self.state = self.expect_document_start self.state = self.expect_document_start
else: else:
raise EmitterError("expected DocumentEndEvent, but got %s" raise EmitterError("expected DocumentEndEvent, but got %s" %
% self.event) self.event)
def expect_document_root(self): def expect_document_root(self):
self.states.append(self.expect_document_end) self.states.append(self.expect_document_end)
@ -231,9 +267,9 @@ def expect_document_root(self):
# Node handlers. # Node handlers.
def expect_node(self, root=False, sequence=False, mapping=False, def expect_node(self, root=False, sequence=False, mapping=False,
simple_key=False): simple_key=False):
self.root_context = root self.root_context = root
self.sequence_context = sequence self.sequence_context = sequence # not used in PyYAML
self.mapping_context = mapping self.mapping_context = mapping
self.simple_key_context = simple_key self.simple_key_context = simple_key
if isinstance(self.event, AliasEvent): if isinstance(self.event, AliasEvent):
@ -244,13 +280,22 @@ def expect_node(self, root=False, sequence=False, mapping=False,
if isinstance(self.event, ScalarEvent): if isinstance(self.event, ScalarEvent):
self.expect_scalar() self.expect_scalar()
elif isinstance(self.event, SequenceStartEvent): elif isinstance(self.event, SequenceStartEvent):
if self.flow_level or self.canonical or self.event.flow_style \ if self.event.comment:
or self.check_empty_sequence(): self.write_pre_comment(self.event)
if self.event.flow_style is False and self.event.comment:
self.write_post_comment(self.event)
# print('seq event', self.event)
if self.flow_level or self.canonical or self.event.flow_style or \
self.check_empty_sequence():
self.expect_flow_sequence() self.expect_flow_sequence()
else: else:
self.expect_block_sequence() self.expect_block_sequence()
elif isinstance(self.event, MappingStartEvent): elif isinstance(self.event, MappingStartEvent):
if self.flow_level or self.canonical or self.event.flow_style \ if self.event.flow_style is False and self.event.comment:
self.write_post_comment(self.event)
if self.event.comment and self.event.comment[1]:
self.write_pre_comment(self.event)
if self.flow_level or self.canonical or self.event.flow_style \
or self.check_empty_mapping(): or self.check_empty_mapping():
self.expect_flow_mapping() self.expect_flow_mapping()
else: else:
@ -275,7 +320,7 @@ def expect_scalar(self):
def expect_flow_sequence(self): def expect_flow_sequence(self):
self.write_indicator(u'[', True, whitespace=True) self.write_indicator(u'[', True, whitespace=True)
self.flow_level += 1 self.flow_level += 1
self.increase_indent(flow=True) self.increase_indent(flow=True, sequence=True)
self.state = self.expect_first_flow_sequence_item self.state = self.expect_first_flow_sequence_item
def expect_first_flow_sequence_item(self): def expect_first_flow_sequence_item(self):
@ -298,6 +343,9 @@ def expect_flow_sequence_item(self):
self.write_indicator(u',', False) self.write_indicator(u',', False)
self.write_indent() self.write_indent()
self.write_indicator(u']', False) self.write_indicator(u']', False)
if self.event.comment and self.event.comment[0]:
# eol comment on flow sequence
self.write_post_comment(self.event)
self.state = self.states.pop() self.state = self.states.pop()
else: else:
self.write_indicator(u',', False) self.write_indicator(u',', False)
@ -311,7 +359,7 @@ def expect_flow_sequence_item(self):
def expect_flow_mapping(self): def expect_flow_mapping(self):
self.write_indicator(u'{', True, whitespace=True) self.write_indicator(u'{', True, whitespace=True)
self.flow_level += 1 self.flow_level += 1
self.increase_indent(flow=True) self.increase_indent(flow=True, sequence=False)
self.state = self.expect_first_flow_mapping_key self.state = self.expect_first_flow_mapping_key
def expect_first_flow_mapping_key(self): def expect_first_flow_mapping_key(self):
@ -319,6 +367,9 @@ def expect_first_flow_mapping_key(self):
self.indent = self.indents.pop() self.indent = self.indents.pop()
self.flow_level -= 1 self.flow_level -= 1
self.write_indicator(u'}', False) self.write_indicator(u'}', False)
# if self.event.comment and self.event.comment[0]:
# # eol comment on flow sequence
# self.write_post_comment(self.event)
self.state = self.states.pop() self.state = self.states.pop()
else: else:
if self.canonical or self.column > self.best_width: if self.canonical or self.column > self.best_width:
@ -333,12 +384,17 @@ def expect_first_flow_mapping_key(self):
def expect_flow_mapping_key(self): def expect_flow_mapping_key(self):
if isinstance(self.event, MappingEndEvent): if isinstance(self.event, MappingEndEvent):
# if self.event.comment and self.event.comment[1]:
# self.write_pre_comment(self.event)
self.indent = self.indents.pop() self.indent = self.indents.pop()
self.flow_level -= 1 self.flow_level -= 1
if self.canonical: if self.canonical:
self.write_indicator(u',', False) self.write_indicator(u',', False)
self.write_indent() self.write_indent()
self.write_indicator(u'}', False) self.write_indicator(u'}', False)
if self.event.comment and self.event.comment[0]:
# eol comment on flow mapping
self.write_post_comment(self.event)
self.state = self.states.pop() self.state = self.states.pop()
else: else:
self.write_indicator(u',', False) self.write_indicator(u',', False)
@ -353,14 +409,14 @@ def expect_flow_mapping_key(self):
self.expect_node(mapping=True) self.expect_node(mapping=True)
def expect_flow_mapping_simple_value(self): def expect_flow_mapping_simple_value(self):
self.write_indicator(u':', False) self.write_indicator(self.prefixed_colon, False)
self.states.append(self.expect_flow_mapping_key) self.states.append(self.expect_flow_mapping_key)
self.expect_node(mapping=True) self.expect_node(mapping=True)
def expect_flow_mapping_value(self): def expect_flow_mapping_value(self):
if self.canonical or self.column > self.best_width: if self.canonical or self.column > self.best_width:
self.write_indent() self.write_indent()
self.write_indicator(u':', True) self.write_indicator(self.prefixed_colon, True)
self.states.append(self.expect_flow_mapping_key) self.states.append(self.expect_flow_mapping_key)
self.expect_node(mapping=True) self.expect_node(mapping=True)
@ -368,7 +424,7 @@ def expect_flow_mapping_value(self):
def expect_block_sequence(self): def expect_block_sequence(self):
indentless = (self.mapping_context and not self.indention) indentless = (self.mapping_context and not self.indention)
self.increase_indent(flow=False, indentless=indentless) self.increase_indent(flow=False, sequence=True, indentless=indentless)
self.state = self.expect_first_block_sequence_item self.state = self.expect_first_block_sequence_item
def expect_first_block_sequence_item(self): def expect_first_block_sequence_item(self):
@ -376,18 +432,26 @@ def expect_first_block_sequence_item(self):
def expect_block_sequence_item(self, first=False): def expect_block_sequence_item(self, first=False):
if not first and isinstance(self.event, SequenceEndEvent): if not first and isinstance(self.event, SequenceEndEvent):
if self.event.comment and self.event.comment[1]:
# final comments from a doc
self.write_pre_comment(self.event)
self.indent = self.indents.pop() self.indent = self.indents.pop()
self.state = self.states.pop() self.state = self.states.pop()
else: else:
self.write_indent() self.write_indent()
self.write_indicator(u'-', True, indention=True) if self.event.comment and self.event.comment[1]:
self.write_pre_comment(self.event)
self.write_indent()
self.write_indicator((u' ' * self.block_seq_indent) + u'-', True, indention=True)
if self.block_seq_indent + 2 > self.best_indent:
self.no_newline = True
self.states.append(self.expect_block_sequence_item) self.states.append(self.expect_block_sequence_item)
self.expect_node(sequence=True) self.expect_node(sequence=True)
# Block mapping handlers. # Block mapping handlers.
def expect_block_mapping(self): def expect_block_mapping(self):
self.increase_indent(flow=False) self.increase_indent(flow=False, sequence=False)
self.state = self.expect_first_block_mapping_key self.state = self.expect_first_block_mapping_key
def expect_first_block_mapping_key(self): def expect_first_block_mapping_key(self):
@ -395,11 +459,19 @@ def expect_first_block_mapping_key(self):
def expect_block_mapping_key(self, first=False): def expect_block_mapping_key(self, first=False):
if not first and isinstance(self.event, MappingEndEvent): if not first and isinstance(self.event, MappingEndEvent):
if self.event.comment and self.event.comment[1]:
# final comments from a doc
self.write_pre_comment(self.event)
self.indent = self.indents.pop() self.indent = self.indents.pop()
self.state = self.states.pop() self.state = self.states.pop()
else: else:
if self.event.comment and self.event.comment[1]:
# final comments from a doc
self.write_pre_comment(self.event)
self.write_indent() self.write_indent()
if self.check_simple_key(): if self.check_simple_key():
if self.event.style == '?':
self.write_indicator(u'?', True, indention=True)
self.states.append(self.expect_block_mapping_simple_value) self.states.append(self.expect_block_mapping_simple_value)
self.expect_node(mapping=True, simple_key=True) self.expect_node(mapping=True, simple_key=True)
else: else:
@ -408,32 +480,39 @@ def expect_block_mapping_key(self, first=False):
self.expect_node(mapping=True) self.expect_node(mapping=True)
def expect_block_mapping_simple_value(self): def expect_block_mapping_simple_value(self):
self.write_indicator(u':', False) if getattr(self.event, 'style', None) != '?':
# prefix = u''
if self.indent == 0 and self.top_level_colon_align is not None:
# write non-prefixed colon
c = u' ' * (self.top_level_colon_align - self.column) + self.colon
else:
c = self.prefixed_colon
self.write_indicator(c, False)
self.states.append(self.expect_block_mapping_key) self.states.append(self.expect_block_mapping_key)
self.expect_node(mapping=True) self.expect_node(mapping=True)
def expect_block_mapping_value(self): def expect_block_mapping_value(self):
self.write_indent() self.write_indent()
self.write_indicator(u':', True, indention=True) self.write_indicator(self.prefixed_colon, True, indention=True)
self.states.append(self.expect_block_mapping_key) self.states.append(self.expect_block_mapping_key)
self.expect_node(mapping=True) self.expect_node(mapping=True)
# Checkers. # Checkers.
def check_empty_sequence(self): def check_empty_sequence(self):
return (isinstance(self.event, SequenceStartEvent) and self.events return (isinstance(self.event, SequenceStartEvent) and self.events and
and isinstance(self.events[0], SequenceEndEvent)) isinstance(self.events[0], SequenceEndEvent))
def check_empty_mapping(self): def check_empty_mapping(self):
return (isinstance(self.event, MappingStartEvent) and self.events return (isinstance(self.event, MappingStartEvent) and self.events and
and isinstance(self.events[0], MappingEndEvent)) isinstance(self.events[0], MappingEndEvent))
def check_empty_document(self): def check_empty_document(self):
if not isinstance(self.event, DocumentStartEvent) or not self.events: if not isinstance(self.event, DocumentStartEvent) or not self.events:
return False return False
event = self.events[0] event = self.events[0]
return (isinstance(event, ScalarEvent) and event.anchor is None return (isinstance(event, ScalarEvent) and event.anchor is None and
and event.tag is None and event.implicit and event.value == u'') event.tag is None and event.implicit and event.value == u'')
def check_simple_key(self): def check_simple_key(self):
length = 0 length = 0
@ -450,10 +529,11 @@ def check_simple_key(self):
if self.analysis is None: if self.analysis is None:
self.analysis = self.analyze_scalar(self.event.value) self.analysis = self.analyze_scalar(self.event.value)
length += len(self.analysis.scalar) length += len(self.analysis.scalar)
return (length < 128 and (isinstance(self.event, AliasEvent) return (length < self.MAX_SIMPLE_KEY_LENGTH and (
or (isinstance(self.event, ScalarEvent) isinstance(self.event, AliasEvent) or
and not self.analysis.empty and not self.analysis.multiline) (isinstance(self.event, ScalarEvent) and
or self.check_empty_sequence() or self.check_empty_mapping())) not self.analysis.empty and not self.analysis.multiline) or
self.check_empty_sequence() or self.check_empty_mapping()))
# Anchor, Tag, and Scalar processors. # Anchor, Tag, and Scalar processors.
@ -473,8 +553,8 @@ def process_tag(self):
if self.style is None: if self.style is None:
self.style = self.choose_scalar_style() self.style = self.choose_scalar_style()
if ((not self.canonical or tag is None) and if ((not self.canonical or tag is None) and
((self.style == '' and self.event.implicit[0]) ((self.style == '' and self.event.implicit[0]) or
or (self.style != '' and self.event.implicit[1]))): (self.style != '' and self.event.implicit[1]))):
self.prepared_tag = None self.prepared_tag = None
return return
if self.event.implicit[0] and tag is None: if self.event.implicit[0] and tag is None:
@ -497,15 +577,16 @@ def choose_scalar_style(self):
self.analysis = self.analyze_scalar(self.event.value) self.analysis = self.analyze_scalar(self.event.value)
if self.event.style == '"' or self.canonical: if self.event.style == '"' or self.canonical:
return '"' return '"'
if not self.event.style and self.event.implicit[0]: if (not self.event.style or self.event.style == '?') and \
self.event.implicit[0]:
if (not (self.simple_key_context and if (not (self.simple_key_context and
(self.analysis.empty or self.analysis.multiline)) (self.analysis.empty or self.analysis.multiline)) and
and (self.flow_level and self.analysis.allow_flow_plain (self.flow_level and self.analysis.allow_flow_plain or
or (not self.flow_level and self.analysis.allow_block_plain))): (not self.flow_level and self.analysis.allow_block_plain))):
return '' return ''
if self.event.style and self.event.style in '|>': if self.event.style and self.event.style in '|>':
if (not self.flow_level and not self.simple_key_context if (not self.flow_level and not self.simple_key_context and
and self.analysis.allow_block): self.analysis.allow_block):
return self.event.style return self.event.style
if not self.event.style or self.event.style == '\'': if not self.event.style or self.event.style == '\'':
if (self.analysis.allow_single_quoted and if (self.analysis.allow_single_quoted and
@ -519,9 +600,11 @@ def process_scalar(self):
if self.style is None: if self.style is None:
self.style = self.choose_scalar_style() self.style = self.choose_scalar_style()
split = (not self.simple_key_context) split = (not self.simple_key_context)
#if self.analysis.multiline and split \ # if self.analysis.multiline and split \
# and (not self.style or self.style in '\'\"'): # and (not self.style or self.style in '\'\"'):
# self.write_indent() # self.write_indent()
if self.sequence_context and not self.flow_level:
self.write_indent()
if self.style == '"': if self.style == '"':
self.write_double_quoted(self.analysis.scalar, split) self.write_double_quoted(self.analysis.scalar, split)
elif self.style == '\'': elif self.style == '\'':
@ -534,13 +617,16 @@ def process_scalar(self):
self.write_plain(self.analysis.scalar, split) self.write_plain(self.analysis.scalar, split)
self.analysis = None self.analysis = None
self.style = None self.style = None
if self.event.comment:
self.write_post_comment(self.event)
# Analyzers. # Analyzers.
def prepare_version(self, version): def prepare_version(self, version):
major, minor = version major, minor = version
if major != 1: if major != 1:
raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) raise EmitterError("unsupported YAML version: %d.%d" %
(major, minor))
return u'%d.%d' % (major, minor) return u'%d.%d' % (major, minor)
def prepare_tag_handle(self, handle): def prepare_tag_handle(self, handle):
@ -548,12 +634,12 @@ def prepare_tag_handle(self, handle):
raise EmitterError("tag handle must not be empty") raise EmitterError("tag handle must not be empty")
if handle[0] != u'!' or handle[-1] != u'!': if handle[0] != u'!' or handle[-1] != u'!':
raise EmitterError("tag handle must start and end with '!': %r" raise EmitterError("tag handle must start and end with '!': %r"
% (handle.encode('utf-8'))) % (utf8(handle)))
for ch in handle[1:-1]: for ch in handle[1:-1]:
if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or
or ch in u'-_'): u'a' <= ch <= u'z' or ch in u'-_'):
raise EmitterError("invalid character %r in the tag handle: %r" raise EmitterError("invalid character %r in the tag handle: %r"
% (ch.encode('utf-8'), handle.encode('utf-8'))) % (utf8(ch), utf8(handle)))
return handle return handle
def prepare_tag_prefix(self, prefix): def prepare_tag_prefix(self, prefix):
@ -572,7 +658,7 @@ def prepare_tag_prefix(self, prefix):
if start < end: if start < end:
chunks.append(prefix[start:end]) chunks.append(prefix[start:end])
start = end = end+1 start = end = end+1
data = ch.encode('utf-8') data = utf8(ch)
for ch in data: for ch in data:
chunks.append(u'%%%02X' % ord(ch)) chunks.append(u'%%%02X' % ord(ch))
if start < end: if start < end:
@ -586,8 +672,7 @@ def prepare_tag(self, tag):
return tag return tag
handle = None handle = None
suffix = tag suffix = tag
prefixes = self.tag_prefixes.keys() prefixes = sorted(self.tag_prefixes.keys())
prefixes.sort()
for prefix in prefixes: for prefix in prefixes:
if tag.startswith(prefix) \ if tag.startswith(prefix) \
and (prefix == u'!' or len(prefix) < len(tag)): and (prefix == u'!' or len(prefix) < len(tag)):
@ -605,7 +690,7 @@ def prepare_tag(self, tag):
if start < end: if start < end:
chunks.append(suffix[start:end]) chunks.append(suffix[start:end])
start = end = end+1 start = end = end+1
data = ch.encode('utf-8') data = utf8(ch)
for ch in data: for ch in data:
chunks.append(u'%%%02X' % ord(ch)) chunks.append(u'%%%02X' % ord(ch))
if start < end: if start < end:
@ -620,20 +705,21 @@ def prepare_anchor(self, anchor):
if not anchor: if not anchor:
raise EmitterError("anchor must not be empty") raise EmitterError("anchor must not be empty")
for ch in anchor: for ch in anchor:
if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or
or ch in u'-_'): u'a' <= ch <= u'z' or ch in u'-_'):
raise EmitterError("invalid character %r in the anchor: %r" raise EmitterError("invalid character %r in the anchor: %r"
% (ch.encode('utf-8'), anchor.encode('utf-8'))) % (utf8(ch), utf8(anchor)))
return anchor return anchor
def analyze_scalar(self, scalar): def analyze_scalar(self, scalar):
# Empty scalar is a special case. # Empty scalar is a special case.
if not scalar: if not scalar:
return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, return ScalarAnalysis(
allow_flow_plain=False, allow_block_plain=True, scalar=scalar, empty=True, multiline=False,
allow_single_quoted=True, allow_double_quoted=True, allow_flow_plain=False, allow_block_plain=True,
allow_block=False) allow_single_quoted=True, allow_double_quoted=True,
allow_block=False)
# Indicators and special characters. # Indicators and special characters.
block_indicators = False block_indicators = False
@ -659,7 +745,7 @@ def analyze_scalar(self, scalar):
# Last character or followed by a whitespace. # Last character or followed by a whitespace.
followed_by_whitespace = (len(scalar) == 1 or followed_by_whitespace = (len(scalar) == 1 or
scalar[1] in u'\0 \t\r\n\x85\u2028\u2029') scalar[1] in u'\0 \t\r\n\x85\u2028\u2029')
# The previous character is a space. # The previous character is a space.
previous_space = False previous_space = False
@ -674,7 +760,7 @@ def analyze_scalar(self, scalar):
# Check for indicators. # Check for indicators.
if index == 0: if index == 0:
# Leading indicators are special characters. # Leading indicators are special characters.
if ch in u'#,[]{}&*!|>\'\"%@`': if ch in u'#,[]{}&*!|>\'\"%@`':
flow_indicators = True flow_indicators = True
block_indicators = True block_indicators = True
if ch in u'?:': if ch in u'?:':
@ -700,9 +786,9 @@ def analyze_scalar(self, scalar):
if ch in u'\n\x85\u2028\u2029': if ch in u'\n\x85\u2028\u2029':
line_breaks = True line_breaks = True
if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'): if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'):
if (ch == u'\x85' or u'\xA0' <= ch <= u'\uD7FF' if (ch == u'\x85' or u'\xA0' <= ch <= u'\uD7FF' or
or u'\uE000' <= ch <= u'\uFFFD') and ch != u'\uFEFF': u'\uE000' <= ch <= u'\uFFFD') and ch != u'\uFEFF':
unicode_characters = True # unicode_characters = True
if not self.allow_unicode: if not self.allow_unicode:
special_characters = True special_characters = True
else: else:
@ -734,8 +820,9 @@ def analyze_scalar(self, scalar):
# Prepare for the next character. # Prepare for the next character.
index += 1 index += 1
preceeded_by_whitespace = (ch in u'\0 \t\r\n\x85\u2028\u2029') preceeded_by_whitespace = (ch in u'\0 \t\r\n\x85\u2028\u2029')
followed_by_whitespace = (index+1 >= len(scalar) or followed_by_whitespace = (
scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029') index+1 >= len(scalar) or
scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029')
# Let's decide what styles are allowed. # Let's decide what styles are allowed.
allow_flow_plain = True allow_flow_plain = True
@ -745,8 +832,7 @@ def analyze_scalar(self, scalar):
allow_block = True allow_block = True
# Leading and trailing whitespaces are bad for plain scalars. # Leading and trailing whitespaces are bad for plain scalars.
if (leading_space or leading_break if (leading_space or leading_break or trailing_space or trailing_break):
or trailing_space or trailing_break):
allow_flow_plain = allow_block_plain = False allow_flow_plain = allow_block_plain = False
# We do not permit trailing spaces for block scalars. # We do not permit trailing spaces for block scalars.
@ -761,8 +847,8 @@ def analyze_scalar(self, scalar):
# Spaces followed by breaks, as well as special character are only # Spaces followed by breaks, as well as special character are only
# allowed for double quoted scalars. # allowed for double quoted scalars.
if space_break or special_characters: if space_break or special_characters:
allow_flow_plain = allow_block_plain = \ allow_flow_plain = allow_block_plain = \
allow_single_quoted = allow_block = False allow_single_quoted = allow_block = False
# Although the plain scalar writer supports breaks, we never emit # Although the plain scalar writer supports breaks, we never emit
# multiline plain scalars. # multiline plain scalars.
@ -778,12 +864,12 @@ def analyze_scalar(self, scalar):
allow_block_plain = False allow_block_plain = False
return ScalarAnalysis(scalar=scalar, return ScalarAnalysis(scalar=scalar,
empty=False, multiline=line_breaks, empty=False, multiline=line_breaks,
allow_flow_plain=allow_flow_plain, allow_flow_plain=allow_flow_plain,
allow_block_plain=allow_block_plain, allow_block_plain=allow_block_plain,
allow_single_quoted=allow_single_quoted, allow_single_quoted=allow_single_quoted,
allow_double_quoted=allow_double_quoted, allow_double_quoted=allow_double_quoted,
allow_block=allow_block) allow_block=allow_block)
# Writers. # Writers.
@ -800,7 +886,7 @@ def write_stream_end(self):
self.flush_stream() self.flush_stream()
def write_indicator(self, indicator, need_whitespace, def write_indicator(self, indicator, need_whitespace,
whitespace=False, indention=False): whitespace=False, indention=False):
if self.whitespace or not need_whitespace: if self.whitespace or not need_whitespace:
data = indicator data = indicator
else: else:
@ -817,7 +903,10 @@ def write_indent(self):
indent = self.indent or 0 indent = self.indent or 0
if not self.indention or self.column > indent \ if not self.indention or self.column > indent \
or (self.column == indent and not self.whitespace): or (self.column == indent and not self.whitespace):
self.write_line_break() if self.no_newline:
self.no_newline = False
else:
self.write_line_break()
if self.column < indent: if self.column < indent:
self.whitespace = True self.whitespace = True
data = u' '*(indent-self.column) data = u' '*(indent-self.column)
@ -933,10 +1022,9 @@ def write_double_quoted(self, text, split=True):
if end < len(text): if end < len(text):
ch = text[end] ch = text[end]
if ch is None or ch in u'"\\\x85\u2028\u2029\uFEFF' \ if ch is None or ch in u'"\\\x85\u2028\u2029\uFEFF' \
or not (u'\x20' <= ch <= u'\x7E' or not (u'\x20' <= ch <= u'\x7E' or
or (self.allow_unicode (self.allow_unicode and
and (u'\xA0' <= ch <= u'\uD7FF' (u'\xA0' <= ch <= u'\uD7FF' or u'\uE000' <= ch <= u'\uFFFD'))):
or u'\uE000' <= ch <= u'\uFFFD'))):
if start < end: if start < end:
data = text[start:end] data = text[start:end]
self.column += len(data) self.column += len(data)
@ -983,7 +1071,7 @@ def determine_block_hints(self, text):
hints = u'' hints = u''
if text: if text:
if text[0] in u' \n\x85\u2028\u2029': if text[0] in u' \n\x85\u2028\u2029':
hints += unicode(self.best_indent) hints += text_type(self.best_indent)
if text[-1] not in u'\n\x85\u2028\u2029': if text[-1] not in u'\n\x85\u2028\u2029':
hints += u'-' hints += u'-'
elif len(text) == 1 or text[-2] in u'\n\x85\u2028\u2029': elif len(text) == 1 or text[-2] in u'\n\x85\u2028\u2029':
@ -1101,7 +1189,8 @@ def write_plain(self, text, split=True):
ch = text[end] ch = text[end]
if spaces: if spaces:
if ch != u' ': if ch != u' ':
if start+1 == end and self.column > self.best_width and split: if start+1 == end and self.column > self.best_width \
and split:
self.write_indent() self.write_indent()
self.whitespace = False self.whitespace = False
self.indention = False self.indention = False
@ -1138,3 +1227,56 @@ def write_plain(self, text, split=True):
breaks = (ch in u'\n\x85\u2028\u2029') breaks = (ch in u'\n\x85\u2028\u2029')
end += 1 end += 1
def write_comment(self, comment):
value = comment.value
# print('{:02d} {:02d} {}'.format(self.column, comment.start_mark.column, value))
if value[-1] == '\n':
value = value[:-1]
try:
# get original column position
col = comment.start_mark.column
if col < self.column + 1:
ValueError
except ValueError:
col = self.column + 1
# print('post_comment', self.line, self.column, value)
try:
# at least one space if the current column >= the start column of the comment
# but not at the start of a line
nr_spaces = col - self.column
if self.column and value.strip() and nr_spaces < 1:
nr_spaces = 1
value = ' ' * nr_spaces + value
try:
if self.encoding:
value = value.encode(self.encoding)
except UnicodeDecodeError:
pass
self.stream.write(value)
except TypeError:
raise
self.write_line_break()
def write_pre_comment(self, event):
comments = event.comment[1]
if comments is None:
return
try:
for comment in comments:
if isinstance(event, MappingStartEvent) and \
getattr(comment, 'pre_done', None):
continue
if self.column != 0:
self.write_line_break()
self.write_comment(comment)
if isinstance(event, MappingStartEvent):
comment.pre_done = True
except TypeError:
print ('eventtt', type(event), event)
raise
def write_post_comment(self, event):
if self.event.comment[0] is None:
return
comment = event.comment[0]
self.write_comment(comment)

View file

@ -1,8 +1,16 @@
# coding: utf-8
from __future__ import absolute_import
__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] __all__ = ['Mark', 'YAMLError', 'MarkedYAMLError']
class Mark(object): try:
from .compat import utf8
except (ImportError, ValueError): # for Jython
from ruamel.yaml.compat import utf8
class Mark(object):
def __init__(self, name, index, line, column, buffer, pointer): def __init__(self, name, index, line, column, buffer, pointer):
self.name = name self.name = name
self.index = index self.index = index
@ -16,7 +24,8 @@ def get_snippet(self, indent=4, max_length=75):
return None return None
head = '' head = ''
start = self.pointer start = self.pointer
while start > 0 and self.buffer[start-1] not in u'\0\r\n\x85\u2028\u2029': while (start > 0 and
self.buffer[start-1] not in u'\0\r\n\x85\u2028\u2029'):
start -= 1 start -= 1
if self.pointer-start > max_length/2-1: if self.pointer-start > max_length/2-1:
head = ' ... ' head = ' ... '
@ -24,15 +33,16 @@ def get_snippet(self, indent=4, max_length=75):
break break
tail = '' tail = ''
end = self.pointer end = self.pointer
while end < len(self.buffer) and self.buffer[end] not in u'\0\r\n\x85\u2028\u2029': while (end < len(self.buffer) and
self.buffer[end] not in u'\0\r\n\x85\u2028\u2029'):
end += 1 end += 1
if end-self.pointer > max_length/2-1: if end-self.pointer > max_length/2-1:
tail = ' ... ' tail = ' ... '
end -= 5 end -= 5
break break
snippet = self.buffer[start:end].encode('utf-8') snippet = utf8(self.buffer[start:end])
return ' '*indent + head + snippet + tail + '\n' \ return ' '*indent + head + snippet + tail + '\n' \
+ ' '*(indent+self.pointer-start+len(head)) + '^' + ' '*(indent+self.pointer-start+len(head)) + '^'
def __str__(self): def __str__(self):
snippet = self.get_snippet() snippet = self.get_snippet()
@ -42,13 +52,14 @@ def __str__(self):
where += ":\n"+snippet where += ":\n"+snippet
return where return where
class YAMLError(Exception): class YAMLError(Exception):
pass pass
class MarkedYAMLError(YAMLError):
class MarkedYAMLError(YAMLError):
def __init__(self, context=None, context_mark=None, def __init__(self, context=None, context_mark=None,
problem=None, problem_mark=None, note=None): problem=None, problem_mark=None, note=None):
self.context = context self.context = context
self.context_mark = context_mark self.context_mark = context_mark
self.problem = problem self.problem = problem
@ -60,10 +71,10 @@ def __str__(self):
if self.context is not None: if self.context is not None:
lines.append(self.context) lines.append(self.context)
if self.context_mark is not None \ if self.context_mark is not None \
and (self.problem is None or self.problem_mark is None and (self.problem is None or self.problem_mark is None or
or self.context_mark.name != self.problem_mark.name self.context_mark.name != self.problem_mark.name or
or self.context_mark.line != self.problem_mark.line self.context_mark.line != self.problem_mark.line or
or self.context_mark.column != self.problem_mark.column): self.context_mark.column != self.problem_mark.column):
lines.append(str(self.context_mark)) lines.append(str(self.context_mark))
if self.problem is not None: if self.problem is not None:
lines.append(self.problem) lines.append(self.problem)
@ -72,4 +83,3 @@ def __str__(self):
if self.note is not None: if self.note is not None:
lines.append(self.note) lines.append(self.note)
return '\n'.join(lines) return '\n'.join(lines)

View file

@ -1,86 +1,106 @@
# coding: utf-8
# Abstract classes. # Abstract classes.
def CommentCheck():
pass
class Event(object): class Event(object):
def __init__(self, start_mark=None, end_mark=None): def __init__(self, start_mark=None, end_mark=None, comment=CommentCheck):
self.start_mark = start_mark self.start_mark = start_mark
self.end_mark = end_mark self.end_mark = end_mark
# assert comment is not CommentCheck
if comment is CommentCheck:
comment = None
self.comment = comment
def __repr__(self): def __repr__(self):
attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] attributes = [key for key in ['anchor', 'tag', 'implicit', 'value',
if hasattr(self, key)] 'flow_style', 'style']
if hasattr(self, key)]
arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes]) for key in attributes])
if self.comment not in [None, CommentCheck]:
arguments += ', comment={!r}'.format(self.comment)
return '%s(%s)' % (self.__class__.__name__, arguments) return '%s(%s)' % (self.__class__.__name__, arguments)
class NodeEvent(Event): class NodeEvent(Event):
def __init__(self, anchor, start_mark=None, end_mark=None): def __init__(self, anchor, start_mark=None, end_mark=None, comment=None):
Event.__init__(self, start_mark, end_mark, comment)
self.anchor = anchor self.anchor = anchor
self.start_mark = start_mark
self.end_mark = end_mark
class CollectionStartEvent(NodeEvent): class CollectionStartEvent(NodeEvent):
def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None,
flow_style=None): flow_style=None, comment=None):
Event.__init__(self, start_mark, end_mark, comment)
self.anchor = anchor self.anchor = anchor
self.tag = tag self.tag = tag
self.implicit = implicit self.implicit = implicit
self.start_mark = start_mark
self.end_mark = end_mark
self.flow_style = flow_style self.flow_style = flow_style
class CollectionEndEvent(Event): class CollectionEndEvent(Event):
pass pass
# Implementations. # Implementations.
class StreamStartEvent(Event): class StreamStartEvent(Event):
def __init__(self, start_mark=None, end_mark=None, encoding=None): def __init__(self, start_mark=None, end_mark=None, encoding=None,
self.start_mark = start_mark comment=None):
self.end_mark = end_mark Event.__init__(self, start_mark, end_mark, comment)
self.encoding = encoding self.encoding = encoding
class StreamEndEvent(Event): class StreamEndEvent(Event):
pass pass
class DocumentStartEvent(Event): class DocumentStartEvent(Event):
def __init__(self, start_mark=None, end_mark=None, def __init__(self, start_mark=None, end_mark=None,
explicit=None, version=None, tags=None): explicit=None, version=None, tags=None, comment=None):
self.start_mark = start_mark Event.__init__(self, start_mark, end_mark, comment)
self.end_mark = end_mark
self.explicit = explicit self.explicit = explicit
self.version = version self.version = version
self.tags = tags self.tags = tags
class DocumentEndEvent(Event): class DocumentEndEvent(Event):
def __init__(self, start_mark=None, end_mark=None, def __init__(self, start_mark=None, end_mark=None,
explicit=None): explicit=None, comment=None):
self.start_mark = start_mark Event.__init__(self, start_mark, end_mark, comment)
self.end_mark = end_mark
self.explicit = explicit self.explicit = explicit
class AliasEvent(NodeEvent): class AliasEvent(NodeEvent):
pass pass
class ScalarEvent(NodeEvent): class ScalarEvent(NodeEvent):
def __init__(self, anchor, tag, implicit, value, def __init__(self, anchor, tag, implicit, value,
start_mark=None, end_mark=None, style=None): start_mark=None, end_mark=None, style=None, comment=None):
self.anchor = anchor NodeEvent.__init__(self, anchor, start_mark, end_mark, comment)
self.tag = tag self.tag = tag
self.implicit = implicit self.implicit = implicit
self.value = value self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style self.style = style
class SequenceStartEvent(CollectionStartEvent): class SequenceStartEvent(CollectionStartEvent):
pass pass
class SequenceEndEvent(CollectionEndEvent): class SequenceEndEvent(CollectionEndEvent):
pass pass
class MappingStartEvent(CollectionStartEvent): class MappingStartEvent(CollectionStartEvent):
pass pass
class MappingEndEvent(CollectionEndEvent): class MappingEndEvent(CollectionEndEvent):
pass pass

View file

@ -0,0 +1,61 @@
# coding: utf-8
from __future__ import absolute_import
__all__ = ['BaseLoader', 'SafeLoader', 'Loader', 'RoundTripLoader']
try:
from .reader import * # NOQA
from .scanner import * # NOQA
from .parser import * # NOQA
from .composer import * # NOQA
from .constructor import * # NOQA
from .resolver import * # NOQA
except (ImportError, ValueError): # for Jython
from ruamel.yaml.reader import * # NOQA
from ruamel.yaml.scanner import * # NOQA
from ruamel.yaml.parser import * # NOQA
from ruamel.yaml.composer import * # NOQA
from ruamel.yaml.constructor import * # NOQA
from ruamel.yaml.resolver import * # NOQA
class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):
def __init__(self, stream, version=None, preserve_quotes=None):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver):
def __init__(self, stream, version=None, preserve_quotes=None):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
def __init__(self, stream, version=None, preserve_quotes=None):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
Constructor.__init__(self)
Resolver.__init__(self)
class RoundTripLoader(Reader, RoundTripScanner, RoundTripParser, Composer,
RoundTripConstructor, VersionedResolver):
def __init__(self, stream, version=None, preserve_quotes=None):
Reader.__init__(self, stream)
RoundTripScanner.__init__(self)
RoundTripParser.__init__(self)
Composer.__init__(self)
RoundTripConstructor.__init__(self, preserve_quotes=preserve_quotes)
VersionedResolver.__init__(self, version)

View file

@ -1,21 +1,20 @@
# coding: utf-8
from .error import * from __future__ import absolute_import
from .tokens import *
from .events import *
from .nodes import *
from .loader import * from ruamel.yaml.error import * # NOQA
from .dumper import *
__version__ = '3.12' from ruamel.yaml.tokens import * # NOQA
try: from ruamel.yaml.events import * # NOQA
from .cyaml import * from ruamel.yaml.nodes import * # NOQA
__with_libyaml__ = True
except ImportError: from ruamel.yaml.loader import * # NOQA
__with_libyaml__ = False from ruamel.yaml.dumper import * # NOQA
from ruamel.yaml.compat import StringIO, BytesIO, with_metaclass, PY3
# import io
import io
def scan(stream, Loader=Loader): def scan(stream, Loader=Loader):
""" """
@ -28,6 +27,7 @@ def scan(stream, Loader=Loader):
finally: finally:
loader.dispose() loader.dispose()
def parse(stream, Loader=Loader): def parse(stream, Loader=Loader):
""" """
Parse a YAML stream and produce parsing events. Parse a YAML stream and produce parsing events.
@ -39,6 +39,7 @@ def parse(stream, Loader=Loader):
finally: finally:
loader.dispose() loader.dispose()
def compose(stream, Loader=Loader): def compose(stream, Loader=Loader):
""" """
Parse the first YAML document in a stream Parse the first YAML document in a stream
@ -50,6 +51,7 @@ def compose(stream, Loader=Loader):
finally: finally:
loader.dispose() loader.dispose()
def compose_all(stream, Loader=Loader): def compose_all(stream, Loader=Loader):
""" """
Parse all YAML documents in a stream Parse all YAML documents in a stream
@ -62,58 +64,81 @@ def compose_all(stream, Loader=Loader):
finally: finally:
loader.dispose() loader.dispose()
def load(stream, Loader=Loader):
def load(stream, Loader=Loader, version=None, preserve_quotes=None):
""" """
Parse the first YAML document in a stream Parse the first YAML document in a stream
and produce the corresponding Python object. and produce the corresponding Python object.
""" """
loader = Loader(stream) loader = Loader(stream, version, preserve_quotes=preserve_quotes)
try: try:
return loader.get_single_data() return loader.get_single_data()
finally: finally:
loader.dispose() loader.dispose()
def load_all(stream, Loader=Loader):
def load_all(stream, Loader=Loader, version=None):
""" """
Parse all YAML documents in a stream Parse all YAML documents in a stream
and produce corresponding Python objects. and produce corresponding Python objects.
""" """
loader = Loader(stream) loader = Loader(stream, version)
try: try:
while loader.check_data(): while loader.check_data():
yield loader.get_data() yield loader.get_data()
finally: finally:
loader.dispose() loader.dispose()
def safe_load(stream):
def safe_load(stream, version=None):
""" """
Parse the first YAML document in a stream Parse the first YAML document in a stream
and produce the corresponding Python object. and produce the corresponding Python object.
Resolve only basic YAML tags. Resolve only basic YAML tags.
""" """
return load(stream, SafeLoader) return load(stream, SafeLoader, version)
def safe_load_all(stream):
def safe_load_all(stream, version=None):
""" """
Parse all YAML documents in a stream Parse all YAML documents in a stream
and produce corresponding Python objects. and produce corresponding Python objects.
Resolve only basic YAML tags. Resolve only basic YAML tags.
""" """
return load_all(stream, SafeLoader) return load_all(stream, SafeLoader, version)
def round_trip_load(stream, version=None, preserve_quotes=None):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
Resolve only basic YAML tags.
"""
return load(stream, RoundTripLoader, version, preserve_quotes=preserve_quotes)
def round_trip_load_all(stream, version=None, preserve_quotes=None):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
Resolve only basic YAML tags.
"""
return load_all(stream, RoundTripLoader, version, preserve_quotes=preserve_quotes)
def emit(events, stream=None, Dumper=Dumper, def emit(events, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None, canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None): allow_unicode=None, line_break=None):
""" """
Emit YAML parsing events into a stream. Emit YAML parsing events into a stream.
If stream is None, return the produced string instead. If stream is None, return the produced string instead.
""" """
getvalue = None getvalue = None
if stream is None: if stream is None:
stream = io.StringIO() stream = StringIO()
getvalue = stream.getvalue getvalue = stream.getvalue
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break) allow_unicode=allow_unicode, line_break=line_break)
try: try:
for event in events: for event in events:
dumper.emit(event) dumper.emit(event)
@ -122,11 +147,14 @@ def emit(events, stream=None, Dumper=Dumper,
if getvalue: if getvalue:
return getvalue() return getvalue()
enc = None if PY3 else 'utf-8'
def serialize_all(nodes, stream=None, Dumper=Dumper, def serialize_all(nodes, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None, canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None, allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None, encoding=enc, explicit_start=None, explicit_end=None,
version=None, tags=None): version=None, tags=None):
""" """
Serialize a sequence of representation trees into a YAML stream. Serialize a sequence of representation trees into a YAML stream.
If stream is None, return the produced string instead. If stream is None, return the produced string instead.
@ -134,14 +162,14 @@ def serialize_all(nodes, stream=None, Dumper=Dumper,
getvalue = None getvalue = None
if stream is None: if stream is None:
if encoding is None: if encoding is None:
stream = io.StringIO() stream = StringIO()
else: else:
stream = io.BytesIO() stream = BytesIO()
getvalue = stream.getvalue getvalue = stream.getvalue
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break, allow_unicode=allow_unicode, line_break=line_break,
encoding=encoding, version=version, tags=tags, encoding=encoding, version=version, tags=tags,
explicit_start=explicit_start, explicit_end=explicit_end) explicit_start=explicit_start, explicit_end=explicit_end)
try: try:
dumper.open() dumper.open()
for node in nodes: for node in nodes:
@ -152,6 +180,7 @@ def serialize_all(nodes, stream=None, Dumper=Dumper,
if getvalue: if getvalue:
return getvalue() return getvalue()
def serialize(node, stream=None, Dumper=Dumper, **kwds): def serialize(node, stream=None, Dumper=Dumper, **kwds):
""" """
Serialize a representation tree into a YAML stream. Serialize a representation tree into a YAML stream.
@ -159,29 +188,36 @@ def serialize(node, stream=None, Dumper=Dumper, **kwds):
""" """
return serialize_all([node], stream, Dumper=Dumper, **kwds) return serialize_all([node], stream, Dumper=Dumper, **kwds)
def dump_all(documents, stream=None, Dumper=Dumper, def dump_all(documents, stream=None, Dumper=Dumper,
default_style=None, default_flow_style=None, default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None, canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None, allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None, encoding=enc, explicit_start=None, explicit_end=None,
version=None, tags=None): version=None, tags=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
""" """
Serialize a sequence of Python objects into a YAML stream. Serialize a sequence of Python objects into a YAML stream.
If stream is None, return the produced string instead. If stream is None, return the produced string instead.
""" """
getvalue = None getvalue = None
if top_level_colon_align is True:
top_level_colon_align = max([len(str(x)) for x in documents[0]])
if stream is None: if stream is None:
if encoding is None: if encoding is None:
stream = io.StringIO() stream = StringIO()
else: else:
stream = io.BytesIO() stream = BytesIO()
getvalue = stream.getvalue getvalue = stream.getvalue
dumper = Dumper(stream, default_style=default_style, dumper = Dumper(stream, default_style=default_style,
default_flow_style=default_flow_style, default_flow_style=default_flow_style,
canonical=canonical, indent=indent, width=width, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break, allow_unicode=allow_unicode, line_break=line_break,
encoding=encoding, version=version, tags=tags, encoding=encoding, explicit_start=explicit_start,
explicit_start=explicit_start, explicit_end=explicit_end) explicit_end=explicit_end, version=version,
tags=tags, block_seq_indent=block_seq_indent,
top_level_colon_align=top_level_colon_align, prefix_colon=prefix_colon,
)
try: try:
dumper.open() dumper.open()
for data in documents: for data in documents:
@ -192,12 +228,31 @@ def dump_all(documents, stream=None, Dumper=Dumper,
if getvalue: if getvalue:
return getvalue() return getvalue()
def dump(data, stream=None, Dumper=Dumper, **kwds):
def dump(data, stream=None, Dumper=Dumper,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=enc, explicit_start=None, explicit_end=None,
version=None, tags=None, block_seq_indent=None):
""" """
Serialize a Python object into a YAML stream. Serialize a Python object into a YAML stream.
If stream is None, return the produced string instead. If stream is None, return the produced string instead.
default_style None, '', '"', "'", '|', '>'
""" """
return dump_all([data], stream, Dumper=Dumper, **kwds) return dump_all([data], stream, Dumper=Dumper,
default_style=default_style,
default_flow_style=default_flow_style,
canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode,
line_break=line_break,
encoding=encoding, explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags, block_seq_indent=block_seq_indent)
def safe_dump_all(documents, stream=None, **kwds): def safe_dump_all(documents, stream=None, **kwds):
""" """
@ -207,6 +262,7 @@ def safe_dump_all(documents, stream=None, **kwds):
""" """
return dump_all(documents, stream, Dumper=SafeDumper, **kwds) return dump_all(documents, stream, Dumper=SafeDumper, **kwds)
def safe_dump(data, stream=None, **kwds): def safe_dump(data, stream=None, **kwds):
""" """
Serialize a Python object into a YAML stream. Serialize a Python object into a YAML stream.
@ -215,8 +271,30 @@ def safe_dump(data, stream=None, **kwds):
""" """
return dump_all([data], stream, Dumper=SafeDumper, **kwds) return dump_all([data], stream, Dumper=SafeDumper, **kwds)
def round_trip_dump(data, stream=None, Dumper=RoundTripDumper,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=enc, explicit_start=None, explicit_end=None,
version=None, tags=None, block_seq_indent=None,
top_level_colon_align=None, prefix_colon=None):
allow_unicode = True if allow_unicode is None else allow_unicode
return dump_all([data], stream, Dumper=Dumper,
default_style=default_style,
default_flow_style=default_flow_style,
canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode,
line_break=line_break,
encoding=encoding, explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags, block_seq_indent=block_seq_indent,
top_level_colon_align=top_level_colon_align, prefix_colon=prefix_colon)
def add_implicit_resolver(tag, regexp, first=None, def add_implicit_resolver(tag, regexp, first=None,
Loader=Loader, Dumper=Dumper): Loader=Loader, Dumper=Dumper):
""" """
Add an implicit scalar detector. Add an implicit scalar detector.
If an implicit scalar value matches the given regexp, If an implicit scalar value matches the given regexp,
@ -226,6 +304,7 @@ def add_implicit_resolver(tag, regexp, first=None,
Loader.add_implicit_resolver(tag, regexp, first) Loader.add_implicit_resolver(tag, regexp, first)
Dumper.add_implicit_resolver(tag, regexp, first) Dumper.add_implicit_resolver(tag, regexp, first)
def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper): def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper):
""" """
Add a path based resolver for the given tag. Add a path based resolver for the given tag.
@ -236,6 +315,7 @@ def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper):
Loader.add_path_resolver(tag, path, kind) Loader.add_path_resolver(tag, path, kind)
Dumper.add_path_resolver(tag, path, kind) Dumper.add_path_resolver(tag, path, kind)
def add_constructor(tag, constructor, Loader=Loader): def add_constructor(tag, constructor, Loader=Loader):
""" """
Add a constructor for the given tag. Add a constructor for the given tag.
@ -244,6 +324,7 @@ def add_constructor(tag, constructor, Loader=Loader):
""" """
Loader.add_constructor(tag, constructor) Loader.add_constructor(tag, constructor)
def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader): def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader):
""" """
Add a multi-constructor for the given tag prefix. Add a multi-constructor for the given tag prefix.
@ -253,6 +334,7 @@ def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader):
""" """
Loader.add_multi_constructor(tag_prefix, multi_constructor) Loader.add_multi_constructor(tag_prefix, multi_constructor)
def add_representer(data_type, representer, Dumper=Dumper): def add_representer(data_type, representer, Dumper=Dumper):
""" """
Add a representer for the given type. Add a representer for the given type.
@ -262,6 +344,7 @@ def add_representer(data_type, representer, Dumper=Dumper):
""" """
Dumper.add_representer(data_type, representer) Dumper.add_representer(data_type, representer)
def add_multi_representer(data_type, multi_representer, Dumper=Dumper): def add_multi_representer(data_type, multi_representer, Dumper=Dumper):
""" """
Add a representer for the given type. Add a representer for the given type.
@ -271,6 +354,7 @@ def add_multi_representer(data_type, multi_representer, Dumper=Dumper):
""" """
Dumper.add_multi_representer(data_type, multi_representer) Dumper.add_multi_representer(data_type, multi_representer)
class YAMLObjectMetaclass(type): class YAMLObjectMetaclass(type):
""" """
The metaclass for YAMLObject. The metaclass for YAMLObject.
@ -281,12 +365,12 @@ def __init__(cls, name, bases, kwds):
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
cls.yaml_dumper.add_representer(cls, cls.to_yaml) cls.yaml_dumper.add_representer(cls, cls.to_yaml)
class YAMLObject(metaclass=YAMLObjectMetaclass):
class YAMLObject(with_metaclass(YAMLObjectMetaclass)):
""" """
An object that can dump itself to a YAML stream An object that can dump itself to a YAML stream
and load itself from a YAML stream. and load itself from a YAML stream.
""" """
__slots__ = () # no direct instantiation, so allow immutable subclasses __slots__ = () # no direct instantiation, so allow immutable subclasses
yaml_loader = Loader yaml_loader = Loader
@ -308,5 +392,4 @@ def to_yaml(cls, dumper, data):
Convert a Python object to a representation node. Convert a Python object to a representation node.
""" """
return dumper.represent_yaml_object(cls.yaml_tag, data, cls, return dumper.represent_yaml_object(cls.yaml_tag, data, cls,
flow_style=cls.yaml_flow_style) flow_style=cls.yaml_flow_style)

86
lib/spack/external/ruamel/yaml/nodes.py vendored Normal file
View file

@ -0,0 +1,86 @@
# coding: utf-8
from __future__ import print_function
class Node(object):
def __init__(self, tag, value, start_mark, end_mark, comment=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.comment = comment
self.anchor = None
def __repr__(self):
value = self.value
# if isinstance(value, list):
# if len(value) == 0:
# value = '<empty>'
# elif len(value) == 1:
# value = '<1 item>'
# else:
# value = '<%d items>' % len(value)
# else:
# if len(value) > 75:
# value = repr(value[:70]+u' ... ')
# else:
# value = repr(value)
value = repr(value)
return '%s(tag=%r, value=%s)' % (self.__class__.__name__,
self.tag, value)
def dump(self, indent=0):
if isinstance(self.value, basestring):
print('{0}{1}(tag={!r}, value={!r})'.format(
' ' * indent, self.__class__.__name__, self.tag, self.value))
if self.comment:
print(' {0}comment: {1})'.format(
' ' * indent, self.comment))
return
print('{0}{1}(tag={!r})'.format(
' ' * indent, self.__class__.__name__, self.tag))
if self.comment:
print(' {0}comment: {1})'.format(
' ' * indent, self.comment))
for v in self.value:
if isinstance(v, tuple):
for v1 in v:
v1.dump(indent+1)
elif isinstance(v, Node):
v.dump(indent+1)
else:
print('Node value type?', type(v))
class ScalarNode(Node):
"""
styles:
? -> set() ? key, no value
" -> double quoted
' -> single quoted
| -> literal style
> ->
"""
id = 'scalar'
def __init__(self, tag, value, start_mark=None, end_mark=None, style=None,
comment=None):
Node.__init__(self, tag, value, start_mark, end_mark, comment=comment)
self.style = style
class CollectionNode(Node):
def __init__(self, tag, value, start_mark=None, end_mark=None,
flow_style=None, comment=None, anchor=None):
Node.__init__(self, tag, value, start_mark, end_mark, comment=comment)
self.flow_style = flow_style
self.anchor = anchor
class SequenceNode(CollectionNode):
id = 'sequence'
class MappingNode(CollectionNode):
id = 'mapping'

View file

@ -1,13 +1,18 @@
# coding: utf-8
from __future__ import absolute_import
# The following YAML grammar is LL(1) and is parsed by a recursive descent # The following YAML grammar is LL(1) and is parsed by a recursive descent
# parser. # parser.
# #
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END # stream ::= STREAM-START implicit_document? explicit_document*
# STREAM-END
# implicit_document ::= block_node DOCUMENT-END* # implicit_document ::= block_node DOCUMENT-END*
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
# block_node_or_indentless_sequence ::= # block_node_or_indentless_sequence ::=
# ALIAS # ALIAS
# | properties (block_content | indentless_block_sequence)? # | properties (block_content |
# indentless_block_sequence)?
# | block_content # | block_content
# | indentless_block_sequence # | indentless_block_sequence
# block_node ::= ALIAS # block_node ::= ALIAS
@ -21,7 +26,8 @@
# flow_content ::= flow_collection | SCALAR # flow_content ::= flow_collection | SCALAR
# block_collection ::= block_sequence | block_mapping # block_collection ::= block_sequence | block_mapping
# flow_collection ::= flow_sequence | flow_mapping # flow_collection ::= flow_sequence | flow_mapping
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)*
# BLOCK-END
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ # indentless_sequence ::= (BLOCK-ENTRY block_node?)+
# block_mapping ::= BLOCK-MAPPING_START # block_mapping ::= BLOCK-MAPPING_START
# ((KEY block_node_or_indentless_sequence?)? # ((KEY block_node_or_indentless_sequence?)?
@ -43,32 +49,44 @@
# stream: { STREAM-START } # stream: { STREAM-START }
# explicit_document: { DIRECTIVE DOCUMENT-START } # explicit_document: { DIRECTIVE DOCUMENT-START }
# implicit_document: FIRST(block_node) # implicit_document: FIRST(block_node)
# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } # block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START
# BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START }
# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } # flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START }
# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } # block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START
# FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } # flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } # block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START }
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } # flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
# block_sequence: { BLOCK-SEQUENCE-START } # block_sequence: { BLOCK-SEQUENCE-START }
# block_mapping: { BLOCK-MAPPING-START } # block_mapping: { BLOCK-MAPPING-START }
# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } # block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR
# BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START
# FLOW-MAPPING-START BLOCK-ENTRY }
# indentless_sequence: { ENTRY } # indentless_sequence: { ENTRY }
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } # flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
# flow_sequence: { FLOW-SEQUENCE-START } # flow_sequence: { FLOW-SEQUENCE-START }
# flow_mapping: { FLOW-MAPPING-START } # flow_mapping: { FLOW-MAPPING-START }
# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } # flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START
# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } # FLOW-MAPPING-START KEY }
# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START
# FLOW-MAPPING-START KEY }
__all__ = ['Parser', 'ParserError'] __all__ = ['Parser', 'RoundTripParser', 'ParserError']
# need to have full path, as pkg_resources tries to load parser.py in __init__.py
# only to not do anything with the package afterwards
# and for Jython too
from ruamel.yaml.error import MarkedYAMLError # NOQA
from ruamel.yaml.tokens import * # NOQA
from ruamel.yaml.events import * # NOQA
from ruamel.yaml.scanner import * # NOQA
from ruamel.yaml.compat import utf8 # NOQA
from error import MarkedYAMLError
from tokens import *
from events import *
from scanner import *
class ParserError(MarkedYAMLError): class ParserError(MarkedYAMLError):
pass pass
class Parser(object): class Parser(object):
# Since writing a recursive-descendant parser is a straightforward task, we # Since writing a recursive-descendant parser is a straightforward task, we
# do not give many comments here. # do not give many comments here.
@ -120,7 +138,8 @@ def get_event(self):
self.current_event = None self.current_event = None
return value return value
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END # stream ::= STREAM-START implicit_document? explicit_document*
# STREAM-END
# implicit_document ::= block_node DOCUMENT-END* # implicit_document ::= block_node DOCUMENT-END*
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
@ -128,8 +147,9 @@ def parse_stream_start(self):
# Parse the stream start. # Parse the stream start.
token = self.get_token() token = self.get_token()
token.move_comment(self.peek_token())
event = StreamStartEvent(token.start_mark, token.end_mark, event = StreamStartEvent(token.start_mark, token.end_mark,
encoding=token.encoding) encoding=token.encoding)
# Prepare the next state. # Prepare the next state.
self.state = self.parse_implicit_document_start self.state = self.parse_implicit_document_start
@ -140,12 +160,12 @@ def parse_implicit_document_start(self):
# Parse an implicit document. # Parse an implicit document.
if not self.check_token(DirectiveToken, DocumentStartToken, if not self.check_token(DirectiveToken, DocumentStartToken,
StreamEndToken): StreamEndToken):
self.tag_handles = self.DEFAULT_TAGS self.tag_handles = self.DEFAULT_TAGS
token = self.peek_token() token = self.peek_token()
start_mark = end_mark = token.start_mark start_mark = end_mark = token.start_mark
event = DocumentStartEvent(start_mark, end_mark, event = DocumentStartEvent(start_mark, end_mark,
explicit=False) explicit=False)
# Prepare the next state. # Prepare the next state.
self.states.append(self.parse_document_end) self.states.append(self.parse_document_end)
@ -169,19 +189,21 @@ def parse_document_start(self):
version, tags = self.process_directives() version, tags = self.process_directives()
if not self.check_token(DocumentStartToken): if not self.check_token(DocumentStartToken):
raise ParserError(None, None, raise ParserError(None, None,
"expected '<document start>', but found %r" "expected '<document start>', but found %r"
% self.peek_token().id, % self.peek_token().id,
self.peek_token().start_mark) self.peek_token().start_mark)
token = self.get_token() token = self.get_token()
end_mark = token.end_mark end_mark = token.end_mark
event = DocumentStartEvent(start_mark, end_mark, event = DocumentStartEvent(
explicit=True, version=version, tags=tags) start_mark, end_mark,
explicit=True, version=version, tags=tags)
self.states.append(self.parse_document_end) self.states.append(self.parse_document_end)
self.state = self.parse_document_content self.state = self.parse_document_content
else: else:
# Parse the end of the stream. # Parse the end of the stream.
token = self.get_token() token = self.get_token()
event = StreamEndEvent(token.start_mark, token.end_mark) event = StreamEndEvent(token.start_mark, token.end_mark,
comment=token.comment)
assert not self.states assert not self.states
assert not self.marks assert not self.marks
self.state = None self.state = None
@ -197,8 +219,7 @@ def parse_document_end(self):
token = self.get_token() token = self.get_token()
end_mark = token.end_mark end_mark = token.end_mark
explicit = True explicit = True
event = DocumentEndEvent(start_mark, end_mark, event = DocumentEndEvent(start_mark, end_mark, explicit=explicit)
explicit=explicit)
# Prepare the next state. # Prepare the next state.
self.state = self.parse_document_start self.state = self.parse_document_start
@ -206,8 +227,9 @@ def parse_document_end(self):
return event return event
def parse_document_content(self): def parse_document_content(self):
if self.check_token(DirectiveToken, if self.check_token(
DocumentStartToken, DocumentEndToken, StreamEndToken): DirectiveToken,
DocumentStartToken, DocumentEndToken, StreamEndToken):
event = self.process_empty_scalar(self.peek_token().start_mark) event = self.process_empty_scalar(self.peek_token().start_mark)
self.state = self.states.pop() self.state = self.states.pop()
return event return event
@ -221,20 +243,23 @@ def process_directives(self):
token = self.get_token() token = self.get_token()
if token.name == u'YAML': if token.name == u'YAML':
if self.yaml_version is not None: if self.yaml_version is not None:
raise ParserError(None, None, raise ParserError(
"found duplicate YAML directive", token.start_mark) None, None,
"found duplicate YAML directive", token.start_mark)
major, minor = token.value major, minor = token.value
if major != 1: if major != 1:
raise ParserError(None, None, raise ParserError(
"found incompatible YAML document (version 1.* is required)", None, None,
token.start_mark) "found incompatible YAML document (version 1.* is "
"required)",
token.start_mark)
self.yaml_version = token.value self.yaml_version = token.value
elif token.name == u'TAG': elif token.name == u'TAG':
handle, prefix = token.value handle, prefix = token.value
if handle in self.tag_handles: if handle in self.tag_handles:
raise ParserError(None, None, raise ParserError(None, None,
"duplicate tag handle %r" % handle.encode('utf-8'), "duplicate tag handle %r" % utf8(handle),
token.start_mark) token.start_mark)
self.tag_handles[handle] = prefix self.tag_handles[handle] = prefix
if self.tag_handles: if self.tag_handles:
value = self.yaml_version, self.tag_handles.copy() value = self.yaml_version, self.tag_handles.copy()
@ -270,6 +295,9 @@ def parse_flow_node(self):
def parse_block_node_or_indentless_sequence(self): def parse_block_node_or_indentless_sequence(self):
return self.parse_node(block=True, indentless_sequence=True) return self.parse_node(block=True, indentless_sequence=True)
def transform_tag(self, handle, suffix):
return self.tag_handles[handle] + suffix
def parse_node(self, block=False, indentless_sequence=False): def parse_node(self, block=False, indentless_sequence=False):
if self.check_token(AliasToken): if self.check_token(AliasToken):
token = self.get_token() token = self.get_token()
@ -302,16 +330,18 @@ def parse_node(self, block=False, indentless_sequence=False):
handle, suffix = tag handle, suffix = tag
if handle is not None: if handle is not None:
if handle not in self.tag_handles: if handle not in self.tag_handles:
raise ParserError("while parsing a node", start_mark, raise ParserError(
"found undefined tag handle %r" % handle.encode('utf-8'), "while parsing a node", start_mark,
tag_mark) "found undefined tag handle %r" % utf8(handle),
tag = self.tag_handles[handle]+suffix tag_mark)
tag = self.transform_tag(handle, suffix)
else: else:
tag = suffix tag = suffix
#if tag == u'!': # if tag == u'!':
# raise ParserError("while parsing a node", start_mark, # raise ParserError("while parsing a node", start_mark,
# "found non-specific tag '!'", tag_mark, # "found non-specific tag '!'", tag_mark,
# "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.") # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag'
# and share your opinion.")
if start_mark is None: if start_mark is None:
start_mark = end_mark = self.peek_token().start_mark start_mark = end_mark = self.peek_token().start_mark
event = None event = None
@ -319,7 +349,7 @@ def parse_node(self, block=False, indentless_sequence=False):
if indentless_sequence and self.check_token(BlockEntryToken): if indentless_sequence and self.check_token(BlockEntryToken):
end_mark = self.peek_token().end_mark end_mark = self.peek_token().end_mark
event = SequenceStartEvent(anchor, tag, implicit, event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark) start_mark, end_mark)
self.state = self.parse_indentless_sequence_entry self.state = self.parse_indentless_sequence_entry
else: else:
if self.check_token(ScalarToken): if self.check_token(ScalarToken):
@ -331,34 +361,52 @@ def parse_node(self, block=False, indentless_sequence=False):
implicit = (False, True) implicit = (False, True)
else: else:
implicit = (False, False) implicit = (False, False)
event = ScalarEvent(anchor, tag, implicit, token.value, event = ScalarEvent(
start_mark, end_mark, style=token.style) anchor, tag, implicit, token.value,
start_mark, end_mark, style=token.style,
comment=token.comment
)
self.state = self.states.pop() self.state = self.states.pop()
elif self.check_token(FlowSequenceStartToken): elif self.check_token(FlowSequenceStartToken):
end_mark = self.peek_token().end_mark end_mark = self.peek_token().end_mark
event = SequenceStartEvent(anchor, tag, implicit, event = SequenceStartEvent(
start_mark, end_mark, flow_style=True) anchor, tag, implicit,
start_mark, end_mark, flow_style=True)
self.state = self.parse_flow_sequence_first_entry self.state = self.parse_flow_sequence_first_entry
elif self.check_token(FlowMappingStartToken): elif self.check_token(FlowMappingStartToken):
end_mark = self.peek_token().end_mark end_mark = self.peek_token().end_mark
event = MappingStartEvent(anchor, tag, implicit, event = MappingStartEvent(
start_mark, end_mark, flow_style=True) anchor, tag, implicit,
start_mark, end_mark, flow_style=True)
self.state = self.parse_flow_mapping_first_key self.state = self.parse_flow_mapping_first_key
elif block and self.check_token(BlockSequenceStartToken): elif block and self.check_token(BlockSequenceStartToken):
end_mark = self.peek_token().start_mark end_mark = self.peek_token().start_mark
event = SequenceStartEvent(anchor, tag, implicit, # should inserting the comment be dependent on the
start_mark, end_mark, flow_style=False) # indentation?
pt = self.peek_token()
comment = pt.comment
# print('pt0', type(pt))
if comment is None or comment[1] is None:
comment = pt.split_comment()
# print('pt1', comment)
event = SequenceStartEvent(
anchor, tag, implicit, start_mark, end_mark,
flow_style=False,
comment=comment,
)
self.state = self.parse_block_sequence_first_entry self.state = self.parse_block_sequence_first_entry
elif block and self.check_token(BlockMappingStartToken): elif block and self.check_token(BlockMappingStartToken):
end_mark = self.peek_token().start_mark end_mark = self.peek_token().start_mark
event = MappingStartEvent(anchor, tag, implicit, comment = self.peek_token().comment
start_mark, end_mark, flow_style=False) event = MappingStartEvent(
anchor, tag, implicit, start_mark, end_mark,
flow_style=False, comment=comment)
self.state = self.parse_block_mapping_first_key self.state = self.parse_block_mapping_first_key
elif anchor is not None or tag is not None: elif anchor is not None or tag is not None:
# Empty scalars are allowed even if a tag or an anchor is # Empty scalars are allowed even if a tag or an anchor is
# specified. # specified.
event = ScalarEvent(anchor, tag, (implicit, False), u'', event = ScalarEvent(anchor, tag, (implicit, False), u'',
start_mark, end_mark) start_mark, end_mark)
self.state = self.states.pop() self.state = self.states.pop()
else: else:
if block: if block:
@ -366,21 +414,26 @@ def parse_node(self, block=False, indentless_sequence=False):
else: else:
node = 'flow' node = 'flow'
token = self.peek_token() token = self.peek_token()
raise ParserError("while parsing a %s node" % node, start_mark, raise ParserError(
"expected the node content, but found %r" % token.id, "while parsing a %s node" % node, start_mark,
token.start_mark) "expected the node content, but found %r" % token.id,
token.start_mark)
return event return event
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)*
# BLOCK-END
def parse_block_sequence_first_entry(self): def parse_block_sequence_first_entry(self):
token = self.get_token() token = self.get_token()
# move any comment from start token
# token.move_comment(self.peek_token())
self.marks.append(token.start_mark) self.marks.append(token.start_mark)
return self.parse_block_sequence_entry() return self.parse_block_sequence_entry()
def parse_block_sequence_entry(self): def parse_block_sequence_entry(self):
if self.check_token(BlockEntryToken): if self.check_token(BlockEntryToken):
token = self.get_token() token = self.get_token()
token.move_comment(self.peek_token())
if not self.check_token(BlockEntryToken, BlockEndToken): if not self.check_token(BlockEntryToken, BlockEndToken):
self.states.append(self.parse_block_sequence_entry) self.states.append(self.parse_block_sequence_entry)
return self.parse_block_node() return self.parse_block_node()
@ -389,28 +442,38 @@ def parse_block_sequence_entry(self):
return self.process_empty_scalar(token.end_mark) return self.process_empty_scalar(token.end_mark)
if not self.check_token(BlockEndToken): if not self.check_token(BlockEndToken):
token = self.peek_token() token = self.peek_token()
raise ParserError("while parsing a block collection", self.marks[-1], raise ParserError(
"expected <block end>, but found %r" % token.id, token.start_mark) "while parsing a block collection", self.marks[-1],
token = self.get_token() "expected <block end>, but found %r" %
event = SequenceEndEvent(token.start_mark, token.end_mark) token.id, token.start_mark)
token = self.get_token() # BlockEndToken
event = SequenceEndEvent(token.start_mark, token.end_mark,
comment=token.comment)
self.state = self.states.pop() self.state = self.states.pop()
self.marks.pop() self.marks.pop()
return event return event
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ # indentless_sequence ::= (BLOCK-ENTRY block_node?)+
# indentless_sequence?
# sequence:
# - entry
# - nested
def parse_indentless_sequence_entry(self): def parse_indentless_sequence_entry(self):
if self.check_token(BlockEntryToken): if self.check_token(BlockEntryToken):
token = self.get_token() token = self.get_token()
token.move_comment(self.peek_token())
if not self.check_token(BlockEntryToken, if not self.check_token(BlockEntryToken,
KeyToken, ValueToken, BlockEndToken): KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_indentless_sequence_entry) self.states.append(self.parse_indentless_sequence_entry)
return self.parse_block_node() return self.parse_block_node()
else: else:
self.state = self.parse_indentless_sequence_entry self.state = self.parse_indentless_sequence_entry
return self.process_empty_scalar(token.end_mark) return self.process_empty_scalar(token.end_mark)
token = self.peek_token() token = self.peek_token()
event = SequenceEndEvent(token.start_mark, token.start_mark) event = SequenceEndEvent(token.start_mark, token.start_mark,
comment=token.comment)
self.state = self.states.pop() self.state = self.states.pop()
return event return event
@ -427,6 +490,7 @@ def parse_block_mapping_first_key(self):
def parse_block_mapping_key(self): def parse_block_mapping_key(self):
if self.check_token(KeyToken): if self.check_token(KeyToken):
token = self.get_token() token = self.get_token()
token.move_comment(self.peek_token())
if not self.check_token(KeyToken, ValueToken, BlockEndToken): if not self.check_token(KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_block_mapping_value) self.states.append(self.parse_block_mapping_value)
return self.parse_block_node_or_indentless_sequence() return self.parse_block_node_or_indentless_sequence()
@ -435,10 +499,14 @@ def parse_block_mapping_key(self):
return self.process_empty_scalar(token.end_mark) return self.process_empty_scalar(token.end_mark)
if not self.check_token(BlockEndToken): if not self.check_token(BlockEndToken):
token = self.peek_token() token = self.peek_token()
raise ParserError("while parsing a block mapping", self.marks[-1], raise ParserError(
"expected <block end>, but found %r" % token.id, token.start_mark) "while parsing a block mapping", self.marks[-1],
"expected <block end>, but found %r" % token.id,
token.start_mark)
token = self.get_token() token = self.get_token()
event = MappingEndEvent(token.start_mark, token.end_mark) token.move_comment(self.peek_token())
event = MappingEndEvent(token.start_mark, token.end_mark,
comment=token.comment)
self.state = self.states.pop() self.state = self.states.pop()
self.marks.pop() self.marks.pop()
return event return event
@ -446,6 +514,8 @@ def parse_block_mapping_key(self):
def parse_block_mapping_value(self): def parse_block_mapping_value(self):
if self.check_token(ValueToken): if self.check_token(ValueToken):
token = self.get_token() token = self.get_token()
# value token might have post comment move it to e.g. block
token.move_comment(self.peek_token())
if not self.check_token(KeyToken, ValueToken, BlockEndToken): if not self.check_token(KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_block_mapping_key) self.states.append(self.parse_block_mapping_key)
return self.parse_block_node_or_indentless_sequence() return self.parse_block_node_or_indentless_sequence()
@ -480,21 +550,24 @@ def parse_flow_sequence_entry(self, first=False):
self.get_token() self.get_token()
else: else:
token = self.peek_token() token = self.peek_token()
raise ParserError("while parsing a flow sequence", self.marks[-1], raise ParserError(
"expected ',' or ']', but got %r" % token.id, token.start_mark) "while parsing a flow sequence", self.marks[-1],
"expected ',' or ']', but got %r" % token.id,
token.start_mark)
if self.check_token(KeyToken): if self.check_token(KeyToken):
token = self.peek_token() token = self.peek_token()
event = MappingStartEvent(None, None, True, event = MappingStartEvent(None, None, True,
token.start_mark, token.end_mark, token.start_mark, token.end_mark,
flow_style=True) flow_style=True)
self.state = self.parse_flow_sequence_entry_mapping_key self.state = self.parse_flow_sequence_entry_mapping_key
return event return event
elif not self.check_token(FlowSequenceEndToken): elif not self.check_token(FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry) self.states.append(self.parse_flow_sequence_entry)
return self.parse_flow_node() return self.parse_flow_node()
token = self.get_token() token = self.get_token()
event = SequenceEndEvent(token.start_mark, token.end_mark) event = SequenceEndEvent(token.start_mark, token.end_mark,
comment=token.comment)
self.state = self.states.pop() self.state = self.states.pop()
self.marks.pop() self.marks.pop()
return event return event
@ -502,7 +575,7 @@ def parse_flow_sequence_entry(self, first=False):
def parse_flow_sequence_entry_mapping_key(self): def parse_flow_sequence_entry_mapping_key(self):
token = self.get_token() token = self.get_token()
if not self.check_token(ValueToken, if not self.check_token(ValueToken,
FlowEntryToken, FlowSequenceEndToken): FlowEntryToken, FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry_mapping_value) self.states.append(self.parse_flow_sequence_entry_mapping_value)
return self.parse_flow_node() return self.parse_flow_node()
else: else:
@ -546,12 +619,14 @@ def parse_flow_mapping_key(self, first=False):
self.get_token() self.get_token()
else: else:
token = self.peek_token() token = self.peek_token()
raise ParserError("while parsing a flow mapping", self.marks[-1], raise ParserError(
"expected ',' or '}', but got %r" % token.id, token.start_mark) "while parsing a flow mapping", self.marks[-1],
"expected ',' or '}', but got %r" % token.id,
token.start_mark)
if self.check_token(KeyToken): if self.check_token(KeyToken):
token = self.get_token() token = self.get_token()
if not self.check_token(ValueToken, if not self.check_token(ValueToken,
FlowEntryToken, FlowMappingEndToken): FlowEntryToken, FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_value) self.states.append(self.parse_flow_mapping_value)
return self.parse_flow_node() return self.parse_flow_node()
else: else:
@ -561,7 +636,8 @@ def parse_flow_mapping_key(self, first=False):
self.states.append(self.parse_flow_mapping_empty_value) self.states.append(self.parse_flow_mapping_empty_value)
return self.parse_flow_node() return self.parse_flow_node()
token = self.get_token() token = self.get_token()
event = MappingEndEvent(token.start_mark, token.end_mark) event = MappingEndEvent(token.start_mark, token.end_mark,
comment=token.comment)
self.state = self.states.pop() self.state = self.states.pop()
self.marks.pop() self.marks.pop()
return event return event
@ -587,3 +663,13 @@ def parse_flow_mapping_empty_value(self):
def process_empty_scalar(self, mark): def process_empty_scalar(self, mark):
return ScalarEvent(None, None, (True, False), u'', mark, mark) return ScalarEvent(None, None, (True, False), u'', mark, mark)
class RoundTripParser(Parser):
"""roundtrip is a safe loader, that wants to see the unmangled tag"""
def transform_tag(self, handle, suffix):
# return self.tag_handles[handle]+suffix
if handle == '!!' and suffix in (u'null', u'bool', u'int', u'float', u'binary',
u'timestamp', u'omap', u'pairs', u'set', u'str',
u'seq', u'map'):
return Parser.transform_tag(self, handle, suffix)
return handle+suffix

View file

@ -1,3 +1,6 @@
# coding: utf-8
from __future__ import absolute_import
# This module contains abstractions for the input stream. You don't have to # This module contains abstractions for the input stream. You don't have to
# looks further, there are no pretty code. # looks further, there are no pretty code.
# #
@ -11,15 +14,24 @@
# Reader determines the encoding of `data` and converts it to unicode. # Reader determines the encoding of `data` and converts it to unicode.
# Reader provides the following methods and attributes: # Reader provides the following methods and attributes:
# reader.peek(length=1) - return the next `length` characters # reader.peek(length=1) - return the next `length` characters
# reader.forward(length=1) - move the current position to `length` characters. # reader.forward(length=1) - move the current position to `length`
# characters.
# reader.index - the number of the current character. # reader.index - the number of the current character.
# reader.line, stream.column - the line and the column of the current character. # reader.line, stream.column - the line and the column of the current
# character.
import codecs
import re
try:
from .error import YAMLError, Mark
from .compat import text_type, binary_type, PY3
except (ImportError, ValueError): # for Jython
from ruamel.yaml.error import YAMLError, Mark
from ruamel.yaml.compat import text_type, binary_type, PY3
__all__ = ['Reader', 'ReaderError'] __all__ = ['Reader', 'ReaderError']
from .error import YAMLError, Mark
import codecs, re
class ReaderError(YAMLError): class ReaderError(YAMLError):
@ -31,16 +43,17 @@ def __init__(self, name, position, character, encoding, reason):
self.reason = reason self.reason = reason
def __str__(self): def __str__(self):
if isinstance(self.character, bytes): if isinstance(self.character, binary_type):
return "'%s' codec can't decode byte #x%02x: %s\n" \ return "'%s' codec can't decode byte #x%02x: %s\n" \
" in \"%s\", position %d" \ " in \"%s\", position %d" \
% (self.encoding, ord(self.character), self.reason, % (self.encoding, ord(self.character), self.reason,
self.name, self.position) self.name, self.position)
else: else:
return "unacceptable character #x%04x: %s\n" \ return "unacceptable character #x%04x: %s\n" \
" in \"%s\", position %d" \ " in \"%s\", position %d" \
% (self.character, self.reason, % (self.character, self.reason,
self.name, self.position) self.name, self.position)
class Reader(object): class Reader(object):
# Reader: # Reader:
@ -49,8 +62,8 @@ class Reader(object):
# - adds '\0' to the end. # - adds '\0' to the end.
# Reader accepts # Reader accepts
# - a `bytes` object, # - a `str` object (PY2) / a `bytes` object (PY3),
# - a `str` object, # - a `unicode` object (PY2) / a `str` object (PY3),
# - a file-like object with its `read` method returning `str`, # - a file-like object with its `read` method returning `str`,
# - a file-like object with its `read` method returning `unicode`. # - a file-like object with its `read` method returning `unicode`.
@ -61,7 +74,7 @@ def __init__(self, stream):
self.stream = None self.stream = None
self.stream_pointer = 0 self.stream_pointer = 0
self.eof = True self.eof = True
self.buffer = '' self.buffer = u''
self.pointer = 0 self.pointer = 0
self.raw_buffer = None self.raw_buffer = None
self.raw_decode = None self.raw_decode = None
@ -69,11 +82,11 @@ def __init__(self, stream):
self.index = 0 self.index = 0
self.line = 0 self.line = 0
self.column = 0 self.column = 0
if isinstance(stream, str): if isinstance(stream, text_type):
self.name = "<unicode string>" self.name = "<unicode string>"
self.check_printable(stream) self.check_printable(stream)
self.buffer = stream+'\0' self.buffer = stream+u'\0'
elif isinstance(stream, bytes): elif isinstance(stream, binary_type):
self.name = "<byte string>" self.name = "<byte string>"
self.raw_buffer = stream self.raw_buffer = stream
self.determine_encoding() self.determine_encoding()
@ -103,26 +116,27 @@ def forward(self, length=1):
ch = self.buffer[self.pointer] ch = self.buffer[self.pointer]
self.pointer += 1 self.pointer += 1
self.index += 1 self.index += 1
if ch in '\n\x85\u2028\u2029' \ if ch in u'\n\x85\u2028\u2029' \
or (ch == '\r' and self.buffer[self.pointer] != '\n'): or (ch == u'\r' and self.buffer[self.pointer] != u'\n'):
self.line += 1 self.line += 1
self.column = 0 self.column = 0
elif ch != '\uFEFF': elif ch != u'\uFEFF':
self.column += 1 self.column += 1
length -= 1 length -= 1
def get_mark(self): def get_mark(self):
if self.stream is None: if self.stream is None:
return Mark(self.name, self.index, self.line, self.column, return Mark(self.name, self.index, self.line, self.column,
self.buffer, self.pointer) self.buffer, self.pointer)
else: else:
return Mark(self.name, self.index, self.line, self.column, return Mark(self.name, self.index, self.line, self.column,
None, None) None, None)
def determine_encoding(self): def determine_encoding(self):
while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): while not self.eof and (self.raw_buffer is None or
len(self.raw_buffer) < 2):
self.update_raw() self.update_raw()
if isinstance(self.raw_buffer, bytes): if isinstance(self.raw_buffer, binary_type):
if self.raw_buffer.startswith(codecs.BOM_UTF16_LE): if self.raw_buffer.startswith(codecs.BOM_UTF16_LE):
self.raw_decode = codecs.utf_16_le_decode self.raw_decode = codecs.utf_16_le_decode
self.encoding = 'utf-16-le' self.encoding = 'utf-16-le'
@ -134,14 +148,16 @@ def determine_encoding(self):
self.encoding = 'utf-8' self.encoding = 'utf-8'
self.update(1) self.update(1)
NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]') NON_PRINTABLE = re.compile(
u'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]')
def check_printable(self, data): def check_printable(self, data):
match = self.NON_PRINTABLE.search(data) match = self.NON_PRINTABLE.search(data)
if match: if match:
character = match.group() character = match.group()
position = self.index+(len(self.buffer)-self.pointer)+match.start() position = self.index+(len(self.buffer)-self.pointer)+match.start()
raise ReaderError(self.name, position, ord(character), raise ReaderError(self.name, position, ord(character),
'unicode', "special characters are not allowed") 'unicode', "special characters are not allowed")
def update(self, length): def update(self, length):
if self.raw_buffer is None: if self.raw_buffer is None:
@ -154,15 +170,19 @@ def update(self, length):
if self.raw_decode is not None: if self.raw_decode is not None:
try: try:
data, converted = self.raw_decode(self.raw_buffer, data, converted = self.raw_decode(self.raw_buffer,
'strict', self.eof) 'strict', self.eof)
except UnicodeDecodeError as exc: except UnicodeDecodeError as exc:
character = self.raw_buffer[exc.start] if PY3:
character = self.raw_buffer[exc.start]
else:
character = exc.object[exc.start]
if self.stream is not None: if self.stream is not None:
position = self.stream_pointer-len(self.raw_buffer)+exc.start position = self.stream_pointer - \
len(self.raw_buffer) + exc.start
else: else:
position = exc.start position = exc.start
raise ReaderError(self.name, position, character, raise ReaderError(self.name, position, character,
exc.encoding, exc.reason) exc.encoding, exc.reason)
else: else:
data = self.raw_buffer data = self.raw_buffer
converted = len(data) converted = len(data)
@ -170,11 +190,13 @@ def update(self, length):
self.buffer += data self.buffer += data
self.raw_buffer = self.raw_buffer[converted:] self.raw_buffer = self.raw_buffer[converted:]
if self.eof: if self.eof:
self.buffer += '\0' self.buffer += u'\0'
self.raw_buffer = None self.raw_buffer = None
break break
def update_raw(self, size=4096): def update_raw(self, size=None):
if size is None:
size = 4096 if PY3 else 1024
data = self.stream.read(size) data = self.stream.read(size)
if self.raw_buffer is None: if self.raw_buffer is None:
self.raw_buffer = data self.raw_buffer = data
@ -184,9 +206,8 @@ def update_raw(self, size=4096):
if not data: if not data:
self.eof = True self.eof = True
#try: # try:
# import psyco # import psyco
# psyco.bind(Reader) # psyco.bind(Reader)
#except ImportError: # except ImportError:
# pass # pass

View file

@ -0,0 +1,888 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import print_function
try:
from .error import * # NOQA
from .nodes import * # NOQA
from .compat import text_type, binary_type, to_unicode, PY2, PY3, ordereddict
from .scalarstring import * # NOQA
except (ImportError, ValueError): # for Jython
from ruamel.yaml.error import * # NOQA
from ruamel.yaml.nodes import * # NOQA
from ruamel.yaml.compat import text_type, binary_type, to_unicode, PY2, PY3, ordereddict
from ruamel.yaml.scalarstring import * # NOQA
import datetime
import sys
import types
if PY3:
import copyreg
import base64
else:
import copy_reg as copyreg
__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
'RepresenterError', 'RoundTripRepresenter']
class RepresenterError(YAMLError):
pass
class BaseRepresenter(object):
yaml_representers = {}
yaml_multi_representers = {}
def __init__(self, default_style=None, default_flow_style=None):
self.default_style = default_style
self.default_flow_style = default_flow_style
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def represent(self, data):
node = self.represent_data(data)
self.serialize(node)
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
if PY2:
def get_classobj_bases(self, cls):
bases = [cls]
for base in cls.__bases__:
bases.extend(self.get_classobj_bases(base))
return bases
def represent_data(self, data):
if self.ignore_aliases(data):
self.alias_key = None
else:
self.alias_key = id(data)
if self.alias_key is not None:
if self.alias_key in self.represented_objects:
node = self.represented_objects[self.alias_key]
# if node is None:
# raise RepresenterError(
# "recursive objects are not allowed: %r" % data)
return node
# self.represented_objects[alias_key] = None
self.object_keeper.append(data)
data_types = type(data).__mro__
if PY2:
# if type(data) is types.InstanceType:
if isinstance(data, types.InstanceType):
data_types = self.get_classobj_bases(data.__class__) + \
list(data_types)
if data_types[0] in self.yaml_representers:
node = self.yaml_representers[data_types[0]](self, data)
else:
for data_type in data_types:
if data_type in self.yaml_multi_representers:
node = self.yaml_multi_representers[data_type](self, data)
break
else:
if None in self.yaml_multi_representers:
node = self.yaml_multi_representers[None](self, data)
elif None in self.yaml_representers:
node = self.yaml_representers[None](self, data)
else:
node = ScalarNode(None, text_type(data))
# if alias_key is not None:
# self.represented_objects[alias_key] = node
return node
def represent_key(self, data):
"""
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
https://bitbucket.org/davidfraser/pyyaml/commits/d81df6eb95f20cac4a79eed95ae553b5c6f77b8c
"""
return self.represent_data(data)
@classmethod
def add_representer(cls, data_type, representer):
if 'yaml_representers' not in cls.__dict__:
cls.yaml_representers = cls.yaml_representers.copy()
cls.yaml_representers[data_type] = representer
@classmethod
def add_multi_representer(cls, data_type, representer):
if 'yaml_multi_representers' not in cls.__dict__:
cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
cls.yaml_multi_representers[data_type] = representer
def represent_scalar(self, tag, value, style=None):
if style is None:
style = self.default_style
node = ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
def represent_sequence(self, tag, sequence, flow_style=None):
value = []
node = SequenceNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
for item in sequence:
node_item = self.represent_data(item)
if not (isinstance(node_item, ScalarNode) and not node_item.style):
best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_omap(self, tag, omap, flow_style=None):
value = []
node = SequenceNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
for item_key in omap:
item_val = omap[item_key]
node_item = self.represent_data({item_key: item_val})
# if not (isinstance(node_item, ScalarNode) \
# and not node_item.style):
# best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
node = MappingNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
if hasattr(mapping, 'items'):
mapping = list(mapping.items())
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)
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not
node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def ignore_aliases(self, data):
return False
class SafeRepresenter(BaseRepresenter):
def ignore_aliases(self, data):
# https://docs.python.org/3/reference/expressions.html#parenthesized-forms :
# "i.e. two occurrences of the empty tuple may or may not yield the same object"
# so "data is ()" should not be used
if data is None or data == ():
return True
if isinstance(data, (binary_type, text_type, bool, int, float)):
return True
def represent_none(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:null',
u'null')
if PY3:
def represent_str(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:str', data)
def represent_binary(self, data):
if hasattr(base64, 'encodebytes'):
data = base64.encodebytes(data).decode('ascii')
else:
data = base64.encodestring(data).decode('ascii')
return self.represent_scalar(u'tag:yaml.org,2002:binary', data,
style='|')
else:
def represent_str(self, data):
tag = None
style = None
try:
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
except UnicodeDecodeError:
try:
data = unicode(data, 'utf-8')
tag = u'tag:yaml.org,2002:str'
except UnicodeDecodeError:
data = data.encode('base64')
tag = u'tag:yaml.org,2002:binary'
style = '|'
return self.represent_scalar(tag, data, style=style)
def represent_unicode(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:str', data)
def represent_bool(self, data):
if data:
value = u'true'
else:
value = u'false'
return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
def represent_int(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:int', text_type(data))
if PY2:
def represent_long(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:int',
text_type(data))
inf_value = 1e300
while repr(inf_value) != repr(inf_value*inf_value):
inf_value *= inf_value
def represent_float(self, data):
if data != data or (data == 0.0 and data == 1.0):
value = u'.nan'
elif data == self.inf_value:
value = u'.inf'
elif data == -self.inf_value:
value = u'-.inf'
else:
value = to_unicode(repr(data)).lower()
# Note that in some cases `repr(data)` represents a float number
# without the decimal parts. For instance:
# >>> repr(1e17)
# '1e17'
# Unfortunately, this is not a valid float representation according
# to the definition of the `!!float` tag. We fix this by adding
# '.0' before the 'e' symbol.
if u'.' not in value and u'e' in value:
value = value.replace(u'e', u'.0e', 1)
return self.represent_scalar(u'tag:yaml.org,2002:float', value)
def represent_list(self, data):
# pairs = (len(data) > 0 and isinstance(data, list))
# if pairs:
# for item in data:
# if not isinstance(item, tuple) or len(item) != 2:
# pairs = False
# break
# if not pairs:
return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
# value = []
# for item_key, item_value in data:
# value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
# [(item_key, item_value)]))
# return SequenceNode(u'tag:yaml.org,2002:pairs', value)
def represent_dict(self, data):
return self.represent_mapping(u'tag:yaml.org,2002:map', data)
def represent_ordereddict(self, data):
return self.represent_omap(u'tag:yaml.org,2002:omap', data)
def represent_set(self, data):
value = {}
for key in data:
value[key] = None
return self.represent_mapping(u'tag:yaml.org,2002:set', value)
def represent_date(self, data):
value = to_unicode(data.isoformat())
return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
def represent_datetime(self, data):
value = to_unicode(data.isoformat(' '))
return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
def represent_yaml_object(self, tag, data, cls, flow_style=None):
if hasattr(data, '__getstate__'):
state = data.__getstate__()
else:
state = data.__dict__.copy()
return self.represent_mapping(tag, state, flow_style=flow_style)
def represent_undefined(self, data):
raise RepresenterError("cannot represent an object: %s" % data)
SafeRepresenter.add_representer(type(None),
SafeRepresenter.represent_none)
SafeRepresenter.add_representer(str,
SafeRepresenter.represent_str)
if PY2:
SafeRepresenter.add_representer(unicode,
SafeRepresenter.represent_unicode)
else:
SafeRepresenter.add_representer(bytes,
SafeRepresenter.represent_binary)
SafeRepresenter.add_representer(bool,
SafeRepresenter.represent_bool)
SafeRepresenter.add_representer(int,
SafeRepresenter.represent_int)
if PY2:
SafeRepresenter.add_representer(long,
SafeRepresenter.represent_long)
SafeRepresenter.add_representer(float,
SafeRepresenter.represent_float)
SafeRepresenter.add_representer(list,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(tuple,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(dict,
SafeRepresenter.represent_dict)
SafeRepresenter.add_representer(set,
SafeRepresenter.represent_set)
SafeRepresenter.add_representer(ordereddict,
SafeRepresenter.represent_ordereddict)
SafeRepresenter.add_representer(datetime.date,
SafeRepresenter.represent_date)
SafeRepresenter.add_representer(datetime.datetime,
SafeRepresenter.represent_datetime)
SafeRepresenter.add_representer(None,
SafeRepresenter.represent_undefined)
class Representer(SafeRepresenter):
if PY2:
def represent_str(self, data):
tag = None
style = None
try:
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
except UnicodeDecodeError:
try:
data = unicode(data, 'utf-8')
tag = u'tag:yaml.org,2002:python/str'
except UnicodeDecodeError:
data = data.encode('base64')
tag = u'tag:yaml.org,2002:binary'
style = '|'
return self.represent_scalar(tag, data, style=style)
def represent_unicode(self, data):
tag = None
try:
data.encode('ascii')
tag = u'tag:yaml.org,2002:python/unicode'
except UnicodeEncodeError:
tag = u'tag:yaml.org,2002:str'
return self.represent_scalar(tag, data)
def represent_long(self, data):
tag = u'tag:yaml.org,2002:int'
if int(data) is not data:
tag = u'tag:yaml.org,2002:python/long'
return self.represent_scalar(tag, to_unicode(data))
def represent_complex(self, data):
if data.imag == 0.0:
data = u'%r' % data.real
elif data.real == 0.0:
data = u'%rj' % data.imag
elif data.imag > 0:
data = u'%r+%rj' % (data.real, data.imag)
else:
data = u'%r%rj' % (data.real, data.imag)
return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
def represent_tuple(self, data):
return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
def represent_name(self, data):
name = u'%s.%s' % (data.__module__, data.__name__)
return self.represent_scalar(u'tag:yaml.org,2002:python/name:' +
name, u'')
def represent_module(self, data):
return self.represent_scalar(
u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
if PY2:
def represent_instance(self, data):
# For instances of classic classes, we use __getinitargs__ and
# __getstate__ to serialize the data.
# If data.__getinitargs__ exists, the object must be reconstructed
# by calling cls(**args), where args is a tuple returned by
# __getinitargs__. Otherwise, the cls.__init__ method should never
# be called and the class instance is created by instantiating a
# trivial class and assigning to the instance's __class__ variable.
# If data.__getstate__ exists, it returns the state of the object.
# Otherwise, the state of the object is data.__dict__.
# We produce either a !!python/object or !!python/object/new node.
# If data.__getinitargs__ does not exist and state is a dictionary,
# we produce a !!python/object node . Otherwise we produce a
# !!python/object/new node.
cls = data.__class__
class_name = u'%s.%s' % (cls.__module__, cls.__name__)
args = None
state = None
if hasattr(data, '__getinitargs__'):
args = list(data.__getinitargs__())
if hasattr(data, '__getstate__'):
state = data.__getstate__()
else:
state = data.__dict__
if args is None and isinstance(state, dict):
return self.represent_mapping(
u'tag:yaml.org,2002:python/object:'+class_name, state)
if isinstance(state, dict) and not state:
return self.represent_sequence(
u'tag:yaml.org,2002:python/object/new:' +
class_name, args)
value = {}
if args:
value['args'] = args
value['state'] = state
return self.represent_mapping(
u'tag:yaml.org,2002:python/object/new:'+class_name, value)
def represent_object(self, data):
# We use __reduce__ API to save the data. data.__reduce__ returns
# a tuple of length 2-5:
# (function, args, state, listitems, dictitems)
# For reconstructing, we calls function(*args), then set its state,
# listitems, and dictitems if they are not None.
# A special case is when function.__name__ == '__newobj__'. In this
# case we create the object with args[0].__new__(*args).
# Another special case is when __reduce__ returns a string - we don't
# support it.
# We produce a !!python/object, !!python/object/new or
# !!python/object/apply node.
cls = type(data)
if cls in copyreg.dispatch_table:
reduce = copyreg.dispatch_table[cls](data)
elif hasattr(data, '__reduce_ex__'):
reduce = data.__reduce_ex__(2)
elif hasattr(data, '__reduce__'):
reduce = data.__reduce__()
else:
raise RepresenterError("cannot represent object: %r" % data)
reduce = (list(reduce)+[None]*5)[:5]
function, args, state, listitems, dictitems = reduce
args = list(args)
if state is None:
state = {}
if listitems is not None:
listitems = list(listitems)
if dictitems is not None:
dictitems = dict(dictitems)
if function.__name__ == '__newobj__':
function = args[0]
args = args[1:]
tag = u'tag:yaml.org,2002:python/object/new:'
newobj = True
else:
tag = u'tag:yaml.org,2002:python/object/apply:'
newobj = False
function_name = u'%s.%s' % (function.__module__, function.__name__)
if not args and not listitems and not dictitems \
and isinstance(state, dict) and newobj:
return self.represent_mapping(
u'tag:yaml.org,2002:python/object:'+function_name, state)
if not listitems and not dictitems \
and isinstance(state, dict) and not state:
return self.represent_sequence(tag+function_name, args)
value = {}
if args:
value['args'] = args
if state or not isinstance(state, dict):
value['state'] = state
if listitems:
value['listitems'] = listitems
if dictitems:
value['dictitems'] = dictitems
return self.represent_mapping(tag+function_name, value)
if PY2:
Representer.add_representer(str,
Representer.represent_str)
Representer.add_representer(unicode,
Representer.represent_unicode)
Representer.add_representer(long,
Representer.represent_long)
Representer.add_representer(complex,
Representer.represent_complex)
Representer.add_representer(tuple,
Representer.represent_tuple)
Representer.add_representer(type,
Representer.represent_name)
if PY2:
Representer.add_representer(types.ClassType,
Representer.represent_name)
Representer.add_representer(types.FunctionType,
Representer.represent_name)
Representer.add_representer(types.BuiltinFunctionType,
Representer.represent_name)
Representer.add_representer(types.ModuleType,
Representer.represent_module)
if PY2:
Representer.add_multi_representer(types.InstanceType,
Representer.represent_instance)
Representer.add_multi_representer(object,
Representer.represent_object)
try:
from .comments import CommentedMap, CommentedOrderedMap, CommentedSeq, \
CommentedSet, comment_attrib, merge_attrib
except ImportError: # for Jython
from ruamel.yaml.comments import CommentedMap, CommentedOrderedMap, \
CommentedSeq, CommentedSet, comment_attrib, merge_attrib
class RoundTripRepresenter(SafeRepresenter):
# need to add type here and write out the .comment
# in serializer and emitter
def __init__(self, default_style=None, default_flow_style=None):
if default_flow_style is None:
default_flow_style = False
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
def represent_none(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:null',
u'')
def represent_preserved_scalarstring(self, data):
tag = None
style = '|'
if PY2 and not isinstance(data, unicode):
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
return self.represent_scalar(tag, data, style=style)
def represent_single_quoted_scalarstring(self, data):
tag = None
style = "'"
if PY2 and not isinstance(data, unicode):
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
return self.represent_scalar(tag, data, style=style)
def represent_double_quoted_scalarstring(self, data):
tag = None
style = '"'
if PY2 and not isinstance(data, unicode):
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
return self.represent_scalar(tag, data, style=style)
def represent_sequence(self, tag, sequence, flow_style=None):
value = []
# if 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
try:
flow_style = sequence.fa.flow_style(flow_style)
except AttributeError:
flow_style = flow_style
try:
anchor = sequence.yaml_anchor()
except AttributeError:
anchor = None
node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
try:
comment = getattr(sequence, comment_attrib)
item_comments = comment.items
node.comment = comment.comment
try:
node.comment.append(comment.end)
except AttributeError:
pass
except AttributeError:
item_comments = {}
for idx, item in enumerate(sequence):
node_item = self.represent_data(item)
node_item.comment = item_comments.get(idx)
if not (isinstance(node_item, ScalarNode) and not node_item.style):
best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
try:
flow_style = mapping.fa.flow_style(flow_style)
except AttributeError:
flow_style = flow_style
try:
anchor = mapping.yaml_anchor()
except AttributeError:
anchor = None
node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
# no sorting! !!
try:
comment = getattr(mapping, comment_attrib)
node.comment = comment.comment
if node.comment and node.comment[1]:
for ct in node.comment[1]:
ct.reset()
item_comments = comment.items
for v in item_comments.values():
if v and v[1]:
for ct in v[1]:
ct.reset()
try:
node.comment.append(comment.end)
except AttributeError:
pass
except AttributeError:
item_comments = {}
for item_key, item_value in mapping.items():
node_key = self.represent_key(item_key)
node_value = self.represent_data(item_value)
item_comment = item_comments.get(item_key)
if item_comment:
assert getattr(node_key, 'comment', None) is None
node_key.comment = item_comment[:2]
nvc = getattr(node_value, 'comment', None)
if nvc is not None: # end comment already there
nvc[0] = item_comment[2]
nvc[1] = item_comment[3]
else:
node_value.comment = item_comment[2:]
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not
node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
merge_list = [m[1] for m in getattr(mapping, merge_attrib, [])]
if merge_list:
# because of the call to represent_data here, the anchors
# are marked as being used and thereby created
if len(merge_list) == 1:
arg = self.represent_data(merge_list[0])
else:
arg = self.represent_data(merge_list)
arg.flow_style = True
value.insert(0,
(ScalarNode(u'tag:yaml.org,2002:merge', '<<'), arg))
return node
def represent_omap(self, tag, omap, flow_style=None):
value = []
try:
flow_style = omap.fa.flow_style(flow_style)
except AttributeError:
flow_style = flow_style
try:
anchor = omap.yaml_anchor()
except AttributeError:
anchor = None
node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
try:
comment = getattr(omap, comment_attrib)
node.comment = comment.comment
if node.comment and node.comment[1]:
for ct in node.comment[1]:
ct.reset()
item_comments = comment.items
for v in item_comments.values():
if v and v[1]:
for ct in v[1]:
ct.reset()
try:
node.comment.append(comment.end)
except AttributeError:
pass
except AttributeError:
item_comments = {}
for item_key in omap:
item_val = omap[item_key]
node_item = self.represent_data({item_key: item_val})
# node item has two scalars in value: node_key and node_value
item_comment = item_comments.get(item_key)
if item_comment:
if item_comment[1]:
node_item.comment = [None, item_comment[1]]
assert getattr(node_item.value[0][0], 'comment', None) is None
node_item.value[0][0].comment = [item_comment[0], None]
nvc = getattr(node_item.value[0][1], 'comment', None)
if nvc is not None: # end comment already there
nvc[0] = item_comment[2]
nvc[1] = item_comment[3]
else:
node_item.value[0][1].comment = item_comment[2:]
# if not (isinstance(node_item, ScalarNode) \
# and not node_item.style):
# best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_set(self, setting):
flow_style = False
tag = u'tag:yaml.org,2002:set'
# return self.represent_mapping(tag, value)
value = []
flow_style = setting.fa.flow_style(flow_style)
try:
anchor = setting.yaml_anchor()
except AttributeError:
anchor = None
node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
# no sorting! !!
try:
comment = getattr(setting, comment_attrib)
node.comment = comment.comment
if node.comment and node.comment[1]:
for ct in node.comment[1]:
ct.reset()
item_comments = comment.items
for v in item_comments.values():
if v and v[1]:
for ct in v[1]:
ct.reset()
try:
node.comment.append(comment.end)
except AttributeError:
pass
except AttributeError:
item_comments = {}
for item_key in setting.odict:
node_key = self.represent_key(item_key)
node_value = self.represent_data(None)
item_comment = item_comments.get(item_key)
if item_comment:
assert getattr(node_key, 'comment', None) is None
node_key.comment = item_comment[:2]
node_key.style = node_value.style = "?"
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not
node_value.style):
best_style = False
value.append((node_key, node_value))
best_style = best_style
return node
def represent_dict(self, data):
"""write out tag if saved on loading"""
try:
t = data.tag.value
except AttributeError:
t = None
if t:
while t and t[0] == '!':
t = t[1:]
tag = 'tag:yaml.org,2002:' + t
else:
tag = u'tag:yaml.org,2002:map'
return self.represent_mapping(tag, data)
RoundTripRepresenter.add_representer(type(None),
RoundTripRepresenter.represent_none)
RoundTripRepresenter.add_representer(
PreservedScalarString,
RoundTripRepresenter.represent_preserved_scalarstring)
RoundTripRepresenter.add_representer(
SingleQuotedScalarString,
RoundTripRepresenter.represent_single_quoted_scalarstring)
RoundTripRepresenter.add_representer(
DoubleQuotedScalarString,
RoundTripRepresenter.represent_double_quoted_scalarstring)
RoundTripRepresenter.add_representer(CommentedSeq,
RoundTripRepresenter.represent_list)
RoundTripRepresenter.add_representer(CommentedMap,
RoundTripRepresenter.represent_dict)
RoundTripRepresenter.add_representer(CommentedOrderedMap,
RoundTripRepresenter.represent_ordereddict)
if sys.version_info >= (2, 7):
import collections
RoundTripRepresenter.add_representer(collections.OrderedDict,
RoundTripRepresenter.represent_ordereddict)
RoundTripRepresenter.add_representer(CommentedSet,
RoundTripRepresenter.represent_set)

View file

@ -0,0 +1,397 @@
# coding: utf-8
from __future__ import absolute_import
import re
try:
from .error import * # NOQA
from .nodes import * # NOQA
from .compat import string_types
except (ImportError, ValueError): # for Jython
from ruamel.yaml.error import * # NOQA
from ruamel.yaml.nodes import * # NOQA
from ruamel.yaml.compat import string_types
__all__ = ['BaseResolver', 'Resolver', 'VersionedResolver']
_DEFAULT_VERSION = (1, 2)
class ResolverError(YAMLError):
pass
class BaseResolver(object):
DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'
yaml_implicit_resolvers = {}
yaml_path_resolvers = {}
def __init__(self):
self._loader_version = None
self.resolver_exact_paths = []
self.resolver_prefix_paths = []
@classmethod
def add_implicit_resolver(cls, tag, regexp, first):
if 'yaml_implicit_resolvers' not in cls.__dict__:
cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy()
if first is None:
first = [None]
for ch in first:
cls.yaml_implicit_resolvers.setdefault(ch, []).append(
(tag, regexp))
@classmethod
def add_path_resolver(cls, tag, path, kind=None):
# Note: `add_path_resolver` is experimental. The API could be changed.
# `new_path` is a pattern that is matched against the path from the
# root to the node that is being considered. `node_path` elements are
# tuples `(node_check, index_check)`. `node_check` is a node class:
# `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None`
# matches any kind of a node. `index_check` could be `None`, a boolean
# value, a string value, or a number. `None` and `False` match against
# any _value_ of sequence and mapping nodes. `True` matches against
# any _key_ of a mapping node. A string `index_check` matches against
# a mapping value that corresponds to a scalar key which content is
# equal to the `index_check` value. An integer `index_check` matches
# against a sequence value with the index equal to `index_check`.
if 'yaml_path_resolvers' not in cls.__dict__:
cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
new_path = []
for element in path:
if isinstance(element, (list, tuple)):
if len(element) == 2:
node_check, index_check = element
elif len(element) == 1:
node_check = element[0]
index_check = True
else:
raise ResolverError("Invalid path element: %s" % element)
else:
node_check = None
index_check = element
if node_check is str:
node_check = ScalarNode
elif node_check is list:
node_check = SequenceNode
elif node_check is dict:
node_check = MappingNode
elif node_check not in [ScalarNode, SequenceNode, MappingNode] \
and not isinstance(node_check, string_types) \
and node_check is not None:
raise ResolverError("Invalid node checker: %s" % node_check)
if not isinstance(index_check, (string_types, int)) \
and index_check is not None:
raise ResolverError("Invalid index checker: %s" % index_check)
new_path.append((node_check, index_check))
if kind is str:
kind = ScalarNode
elif kind is list:
kind = SequenceNode
elif kind is dict:
kind = MappingNode
elif kind not in [ScalarNode, SequenceNode, MappingNode] \
and kind is not None:
raise ResolverError("Invalid node kind: %s" % kind)
cls.yaml_path_resolvers[tuple(new_path), kind] = tag
def descend_resolver(self, current_node, current_index):
if not self.yaml_path_resolvers:
return
exact_paths = {}
prefix_paths = []
if current_node:
depth = len(self.resolver_prefix_paths)
for path, kind in self.resolver_prefix_paths[-1]:
if self.check_resolver_prefix(depth, path, kind,
current_node, current_index):
if len(path) > depth:
prefix_paths.append((path, kind))
else:
exact_paths[kind] = self.yaml_path_resolvers[path,
kind]
else:
for path, kind in self.yaml_path_resolvers:
if not path:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
prefix_paths.append((path, kind))
self.resolver_exact_paths.append(exact_paths)
self.resolver_prefix_paths.append(prefix_paths)
def ascend_resolver(self):
if not self.yaml_path_resolvers:
return
self.resolver_exact_paths.pop()
self.resolver_prefix_paths.pop()
def check_resolver_prefix(self, depth, path, kind,
current_node, current_index):
node_check, index_check = path[depth-1]
if isinstance(node_check, string_types):
if current_node.tag != node_check:
return
elif node_check is not None:
if not isinstance(current_node, node_check):
return
if index_check is True and current_index is not None:
return
if (index_check is False or index_check is None) \
and current_index is None:
return
if isinstance(index_check, string_types):
if not (isinstance(current_index, ScalarNode) and
index_check == current_index.value):
return
elif isinstance(index_check, int) and not isinstance(index_check,
bool):
if index_check != current_index:
return
return True
def resolve(self, kind, value, implicit):
if kind is ScalarNode and implicit[0]:
if value == u'':
resolvers = self.yaml_implicit_resolvers.get(u'', [])
else:
resolvers = self.yaml_implicit_resolvers.get(value[0], [])
resolvers += self.yaml_implicit_resolvers.get(None, [])
for tag, regexp in resolvers:
if regexp.match(value):
return tag
implicit = implicit[1]
if self.yaml_path_resolvers:
exact_paths = self.resolver_exact_paths[-1]
if kind in exact_paths:
return exact_paths[kind]
if None in exact_paths:
return exact_paths[None]
if kind is ScalarNode:
return self.DEFAULT_SCALAR_TAG
elif kind is SequenceNode:
return self.DEFAULT_SEQUENCE_TAG
elif kind is MappingNode:
return self.DEFAULT_MAPPING_TAG
@property
def processing_version(self):
return None
class Resolver(BaseResolver):
pass
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:bool',
re.compile(u'''^(?:yes|Yes|YES|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF)$''', re.X),
list(u'yYnNtTfFoO'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:float',
re.compile(u'''^(?:
[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
|[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
|\\.[0-9_]+(?:[eE][-+][0-9]+)?
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
|[-+]?\\.(?:inf|Inf|INF)
|\\.(?:nan|NaN|NAN))$''', re.X),
list(u'-+0123456789.'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:int',
re.compile(u'''^(?:[-+]?0b[0-1_]+
|[-+]?0o?[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list(u'-+0123456789'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:merge',
re.compile(u'^(?:<<)$'),
[u'<'])
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:null',
re.compile(u'''^(?: ~
|null|Null|NULL
| )$''', re.X),
[u'~', u'n', u'N', u''])
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:timestamp',
re.compile(u'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
(?:[Tt]|[ \\t]+)[0-9][0-9]?
:[0-9][0-9] :[0-9][0-9] (?:\\.[0-9]*)?
(?:[ \\t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
list(u'0123456789'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:value',
re.compile(u'^(?:=)$'),
[u'='])
# The following resolver is only for documentation purposes. It cannot work
# because plain scalars cannot start with '!', '&', or '*'.
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:yaml',
re.compile(u'^(?:!|&|\\*)$'),
list(u'!&*'))
# resolvers consist of
# - a list of applicable version
# - a tag
# - a regexp
# - a list of first characters to match
implicit_resolvers = [
([(1, 2)],
u'tag:yaml.org,2002:bool',
re.compile(u'''^(?:true|True|TRUE|false|False|FALSE)$''', re.X),
list(u'tTfF')),
([(1, 1)],
u'tag:yaml.org,2002:bool',
re.compile(u'''^(?:yes|Yes|YES|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF)$''', re.X),
list(u'yYnNtTfFoO')),
([(1, 2), (1, 1)],
u'tag:yaml.org,2002:float',
re.compile(u'''^(?:
[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
|[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
|\\.[0-9_]+(?:[eE][-+][0-9]+)?
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
|[-+]?\\.(?:inf|Inf|INF)
|\\.(?:nan|NaN|NAN))$''', re.X),
list(u'-+0123456789.')),
([(1, 2)],
u'tag:yaml.org,2002:int',
re.compile(u'''^(?:[-+]?0b[0-1_]+
|[-+]?0o?[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+)$''', re.X),
list(u'-+0123456789')),
([(1, 1)],
u'tag:yaml.org,2002:int',
re.compile(u'''^(?:[-+]?0b[0-1_]+
|[-+]?0o?[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list(u'-+0123456789')),
([(1, 2), (1, 1)],
u'tag:yaml.org,2002:merge',
re.compile(u'^(?:<<)$'),
[u'<']),
([(1, 2), (1, 1)],
u'tag:yaml.org,2002:null',
re.compile(u'''^(?: ~
|null|Null|NULL
| )$''', re.X),
[u'~', u'n', u'N', u'']),
([(1, 2), (1, 1)],
u'tag:yaml.org,2002:timestamp',
re.compile(u'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
(?:[Tt]|[ \\t]+)[0-9][0-9]?
:[0-9][0-9] :[0-9][0-9] (?:\\.[0-9]*)?
(?:[ \\t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
list(u'0123456789')),
([(1, 2), (1, 1)],
u'tag:yaml.org,2002:value',
re.compile(u'^(?:=)$'),
[u'=']),
# The following resolver is only for documentation purposes. It cannot work
# because plain scalars cannot start with '!', '&', or '*'.
([(1, 2), (1, 1)],
u'tag:yaml.org,2002:yaml',
re.compile(u'^(?:!|&|\\*)$'),
list(u'!&*')),
]
class VersionedResolver(BaseResolver):
"""
contrary to the "normal" resolver, the smart resolver delays loading
the pattern matching rules. That way it can decide to load 1.1 rules
or the (default) 1.2 that no longer support octal without 0o, sexagesimals
and Yes/No/On/Off booleans.
"""
def __init__(self, version=None):
BaseResolver.__init__(self)
self._loader_version = self.get_loader_version(version)
self._version_implicit_resolver = {}
def add_version_implicit_resolver(self, version, tag, regexp, first):
if first is None:
first = [None]
impl_resolver = self._version_implicit_resolver.setdefault(version, {})
for ch in first:
impl_resolver.setdefault(ch, []).append((tag, regexp))
def get_loader_version(self, version):
if version is None or isinstance(version, tuple):
return version
if isinstance(version, list):
return tuple(version)
# assume string
return tuple(map(int, version.split(u'.')))
@property
def resolver(self):
"""
select the resolver based on the version we are parsing
"""
version = self.processing_version
if version not in self._version_implicit_resolver:
for x in implicit_resolvers:
if version in x[0]:
self.add_version_implicit_resolver(version, x[1], x[2], x[3])
return self._version_implicit_resolver[version]
def resolve(self, kind, value, implicit):
if kind is ScalarNode and implicit[0]:
if value == u'':
resolvers = self.resolver.get(u'', [])
else:
resolvers = self.resolver.get(value[0], [])
resolvers += self.resolver.get(None, [])
for tag, regexp in resolvers:
if regexp.match(value):
return tag
implicit = implicit[1]
if self.yaml_path_resolvers:
exact_paths = self.resolver_exact_paths[-1]
if kind in exact_paths:
return exact_paths[kind]
if None in exact_paths:
return exact_paths[None]
if kind is ScalarNode:
return self.DEFAULT_SCALAR_TAG
elif kind is SequenceNode:
return self.DEFAULT_SEQUENCE_TAG
elif kind is MappingNode:
return self.DEFAULT_MAPPING_TAG
@property
def processing_version(self):
try:
version = self.yaml_version
except AttributeError:
# dumping
version = self.use_version
if version is None:
version = self._loader_version
if version is None:
version = _DEFAULT_VERSION
return version

View file

@ -0,0 +1,60 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import print_function
__all__ = ["ScalarString", "PreservedScalarString", "SingleQuotedScalarString",
"DoubleQuotedScalarString"]
try:
from .compat import text_type
except (ImportError, ValueError): # for Jython
from ruamel.yaml.compat import text_type
class ScalarString(text_type):
def __new__(cls, *args, **kw):
return text_type.__new__(cls, *args, **kw)
class PreservedScalarString(ScalarString):
def __new__(cls, value):
return ScalarString.__new__(cls, value)
class SingleQuotedScalarString(ScalarString):
def __new__(cls, value):
return ScalarString.__new__(cls, value)
class DoubleQuotedScalarString(ScalarString):
def __new__(cls, value):
return ScalarString.__new__(cls, value)
def preserve_literal(s):
return PreservedScalarString(s.replace('\r\n', '\n').replace('\r', '\n'))
def walk_tree(base):
"""
the routine here walks over a simple yaml tree (recursing in
dict values and list items) and converts strings that
have multiple lines to literal scalars
"""
from ruamel.yaml.compat import string_types
if isinstance(base, dict):
for k in base:
v = base[k]
if isinstance(v, string_types) and '\n' in v:
base[k] = preserve_literal(v)
else:
walk_tree(v)
elif isinstance(base, list):
for idx, elem in enumerate(base):
if isinstance(elem, string_types) and '\n' in elem:
print(elem)
base[idx] = preserve_literal(elem)
else:
walk_tree(elem)

View file

@ -1,3 +1,7 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import print_function
# Scanner produces tokens of the following types: # Scanner produces tokens of the following types:
# STREAM-START # STREAM-START
@ -21,17 +25,28 @@
# TAG(value) # TAG(value)
# SCALAR(value, plain, style) # SCALAR(value, plain, style)
# #
# RoundTripScanner
# COMMENT(value)
#
# Read comments in the Scanner code for more details. # Read comments in the Scanner code for more details.
# #
__all__ = ['Scanner', 'ScannerError'] __all__ = ['Scanner', 'RoundTripScanner', 'ScannerError']
try:
from .error import MarkedYAMLError
from .tokens import * # NOQA
from .compat import utf8, unichr, PY3
except (ImportError, ValueError): # for Jython
from ruamel.yaml.error import MarkedYAMLError
from ruamel.yaml.tokens import * # NOQA
from ruamel.yaml.compat import utf8, unichr, PY3
from error import MarkedYAMLError
from tokens import *
class ScannerError(MarkedYAMLError): class ScannerError(MarkedYAMLError):
pass pass
class SimpleKey(object): class SimpleKey(object):
# See below simple keys treatment. # See below simple keys treatment.
@ -43,6 +58,7 @@ def __init__(self, token_number, required, index, line, column, mark):
self.column = column self.column = column
self.mark = mark self.mark = mark
class Scanner(object): class Scanner(object):
def __init__(self): def __init__(self):
@ -52,9 +68,9 @@ def __init__(self):
# input data to Unicode. It also adds NUL to the end. # input data to Unicode. It also adds NUL to the end.
# #
# Reader supports the following methods # Reader supports the following methods
# self.peek(i=0) # peek the next i-th character # self.peek(i=0) # peek the next i-th character
# self.prefix(l=1) # peek the next l characters # self.prefix(l=1) # peek the next l characters
# self.forward(l=1) # read the next l characters and move the pointer. # self.forward(l=1) # read the next l characters and move the pointer
# Had we reached the end of the stream? # Had we reached the end of the stream?
self.done = False self.done = False
@ -153,7 +169,10 @@ def need_more_tokens(self):
def fetch_more_tokens(self): def fetch_more_tokens(self):
# Eat whitespaces and comments until we reach the next token. # Eat whitespaces and comments until we reach the next token.
self.scan_to_next_token() comment = self.scan_to_next_token()
if comment is not None: # never happens for base scanner
return self.fetch_comment(comment)
# Remove obsolete possible simple keys. # Remove obsolete possible simple keys.
self.stale_possible_simple_keys() self.stale_possible_simple_keys()
@ -182,8 +201,8 @@ def fetch_more_tokens(self):
return self.fetch_document_end() return self.fetch_document_end()
# TODO: support for BOM within a stream. # TODO: support for BOM within a stream.
#if ch == u'\uFEFF': # if ch == u'\uFEFF':
# return self.fetch_bom() <-- issue BOMToken # return self.fetch_bom() <-- issue BOMToken
# Note: the order of the following checks is NOT significant. # Note: the order of the following checks is NOT significant.
@ -253,8 +272,8 @@ def fetch_more_tokens(self):
# No? It's an error. Let's produce a nice error message. # No? It's an error. Let's produce a nice error message.
raise ScannerError("while scanning for the next token", None, raise ScannerError("while scanning for the next token", None,
"found character %r that cannot start any token" "found character %r that cannot start any token"
% ch.encode('utf-8'), self.get_mark()) % utf8(ch), self.get_mark())
# Simple keys treatment. # Simple keys treatment.
@ -280,13 +299,14 @@ def stale_possible_simple_keys(self):
# - should be no longer than 1024 characters. # - should be no longer than 1024 characters.
# Disabling this procedure will allow simple keys of any length and # Disabling this procedure will allow simple keys of any length and
# height (may cause problems if indentation is broken though). # height (may cause problems if indentation is broken though).
for level in self.possible_simple_keys.keys(): for level in list(self.possible_simple_keys):
key = self.possible_simple_keys[level] key = self.possible_simple_keys[level]
if key.line != self.line \ if key.line != self.line \
or self.index-key.index > 1024: or self.index-key.index > 1024:
if key.required: if key.required:
raise ScannerError("while scanning a simple key", key.mark, raise ScannerError(
"could not find expected ':'", self.get_mark()) "while scanning a simple key", key.mark,
"could not find expected ':'", self.get_mark())
del self.possible_simple_keys[level] del self.possible_simple_keys[level]
def save_possible_simple_key(self): def save_possible_simple_key(self):
@ -302,18 +322,20 @@ def save_possible_simple_key(self):
if self.allow_simple_key: if self.allow_simple_key:
self.remove_possible_simple_key() self.remove_possible_simple_key()
token_number = self.tokens_taken+len(self.tokens) token_number = self.tokens_taken+len(self.tokens)
key = SimpleKey(token_number, required, key = SimpleKey(
self.index, self.line, self.column, self.get_mark()) token_number, required,
self.index, self.line, self.column, self.get_mark())
self.possible_simple_keys[self.flow_level] = key self.possible_simple_keys[self.flow_level] = key
def remove_possible_simple_key(self): def remove_possible_simple_key(self):
# Remove the saved possible key position at the current flow level. # Remove the saved possible key position at the current flow level.
if self.flow_level in self.possible_simple_keys: if self.flow_level in self.possible_simple_keys:
key = self.possible_simple_keys[self.flow_level] key = self.possible_simple_keys[self.flow_level]
if key.required: if key.required:
raise ScannerError("while scanning a simple key", key.mark, raise ScannerError(
"could not find expected ':'", self.get_mark()) "while scanning a simple key", key.mark,
"could not find expected ':'", self.get_mark())
del self.possible_simple_keys[self.flow_level] del self.possible_simple_keys[self.flow_level]
@ -321,16 +343,17 @@ def remove_possible_simple_key(self):
def unwind_indent(self, column): def unwind_indent(self, column):
## In flow context, tokens should respect indentation. # In flow context, tokens should respect indentation.
## Actually the condition should be `self.indent >= column` according to # Actually the condition should be `self.indent >= column` according to
## the spec. But this condition will prohibit intuitively correct # the spec. But this condition will prohibit intuitively correct
## constructions such as # constructions such as
## key : { # key : {
## } # }
#if self.flow_level and self.indent > column: # ####
# raise ScannerError(None, None, # if self.flow_level and self.indent > column:
# "invalid intendation or unclosed '[' or '{'", # raise ScannerError(None, None,
# self.get_mark()) # "invalid intendation or unclosed '[' or '{'",
# self.get_mark())
# In the flow context, indentation is ignored. We make the scanner less # In the flow context, indentation is ignored. We make the scanner less
# restrictive then specification requires. # restrictive then specification requires.
@ -359,11 +382,10 @@ def fetch_stream_start(self):
# Read the token. # Read the token.
mark = self.get_mark() mark = self.get_mark()
# Add STREAM-START. # Add STREAM-START.
self.tokens.append(StreamStartToken(mark, mark, self.tokens.append(StreamStartToken(mark, mark,
encoding=self.encoding)) encoding=self.encoding))
def fetch_stream_end(self): def fetch_stream_end(self):
@ -377,7 +399,7 @@ def fetch_stream_end(self):
# Read the token. # Read the token.
mark = self.get_mark() mark = self.get_mark()
# Add STREAM-END. # Add STREAM-END.
self.tokens.append(StreamEndToken(mark, mark)) self.tokens.append(StreamEndToken(mark, mark))
@ -385,7 +407,7 @@ def fetch_stream_end(self):
self.done = True self.done = True
def fetch_directive(self): def fetch_directive(self):
# Set the current intendation to -1. # Set the current intendation to -1.
self.unwind_indent(-1) self.unwind_indent(-1)
@ -486,8 +508,8 @@ def fetch_block_entry(self):
# Are we allowed to start a new entry? # Are we allowed to start a new entry?
if not self.allow_simple_key: if not self.allow_simple_key:
raise ScannerError(None, None, raise ScannerError(None, None,
"sequence entries are not allowed here", "sequence entries are not allowed here",
self.get_mark()) self.get_mark())
# We may need to add BLOCK-SEQUENCE-START. # We may need to add BLOCK-SEQUENCE-START.
if self.add_indent(self.column): if self.add_indent(self.column):
@ -512,15 +534,15 @@ def fetch_block_entry(self):
self.tokens.append(BlockEntryToken(start_mark, end_mark)) self.tokens.append(BlockEntryToken(start_mark, end_mark))
def fetch_key(self): def fetch_key(self):
# Block context needs additional checks. # Block context needs additional checks.
if not self.flow_level: if not self.flow_level:
# Are we allowed to start a key (not nessesary a simple)? # Are we allowed to start a key (not nessesary a simple)?
if not self.allow_simple_key: if not self.allow_simple_key:
raise ScannerError(None, None, raise ScannerError(None, None,
"mapping keys are not allowed here", "mapping keys are not allowed here",
self.get_mark()) self.get_mark())
# We may need to add BLOCK-MAPPING-START. # We may need to add BLOCK-MAPPING-START.
if self.add_indent(self.column): if self.add_indent(self.column):
@ -543,26 +565,26 @@ def fetch_value(self):
# Do we determine a simple key? # Do we determine a simple key?
if self.flow_level in self.possible_simple_keys: if self.flow_level in self.possible_simple_keys:
# Add KEY. # Add KEY.
key = self.possible_simple_keys[self.flow_level] key = self.possible_simple_keys[self.flow_level]
del self.possible_simple_keys[self.flow_level] del self.possible_simple_keys[self.flow_level]
self.tokens.insert(key.token_number-self.tokens_taken, self.tokens.insert(key.token_number-self.tokens_taken,
KeyToken(key.mark, key.mark)) KeyToken(key.mark, key.mark))
# If this key starts a new block mapping, we need to add # If this key starts a new block mapping, we need to add
# BLOCK-MAPPING-START. # BLOCK-MAPPING-START.
if not self.flow_level: if not self.flow_level:
if self.add_indent(key.column): if self.add_indent(key.column):
self.tokens.insert(key.token_number-self.tokens_taken, self.tokens.insert(
BlockMappingStartToken(key.mark, key.mark)) key.token_number-self.tokens_taken,
BlockMappingStartToken(key.mark, key.mark))
# There cannot be two simple keys one after another. # There cannot be two simple keys one after another.
self.allow_simple_key = False self.allow_simple_key = False
# It must be a part of a complex key. # It must be a part of a complex key.
else: else:
# Block context needs additional checks. # Block context needs additional checks.
# (Do we really need them? They will be catched by the parser # (Do we really need them? They will be catched by the parser
# anyway.) # anyway.)
@ -572,8 +594,8 @@ def fetch_value(self):
# we can start a simple key. # we can start a simple key.
if not self.allow_simple_key: if not self.allow_simple_key:
raise ScannerError(None, None, raise ScannerError(None, None,
"mapping values are not allowed here", "mapping values are not allowed here",
self.get_mark()) self.get_mark())
# If this value starts a new block mapping, we need to add # If this value starts a new block mapping, we need to add
# BLOCK-MAPPING-START. It will be detected as an error later by # BLOCK-MAPPING-START. It will be detected as an error later by
@ -726,7 +748,6 @@ def check_value(self):
return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029'
def check_plain(self): def check_plain(self):
# A plain scalar may start with any non-space character except: # A plain scalar may start with any non-space character except:
# '-', '?', ':', ',', '[', ']', '{', '}', # '-', '?', ':', ',', '[', ']', '{', '}',
# '#', '&', '*', '!', '|', '>', '\'', '\"', # '#', '&', '*', '!', '|', '>', '\'', '\"',
@ -740,9 +761,9 @@ def check_plain(self):
# '-' character) because we want the flow context to be space # '-' character) because we want the flow context to be space
# independent. # independent.
ch = self.peek() ch = self.peek()
return ch not in u'\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ return ch not in u'\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' or \
or (self.peek(1) not in u'\0 \t\r\n\x85\u2028\u2029' (self.peek(1) not in u'\0 \t\r\n\x85\u2028\u2029' and
and (ch == u'-' or (not self.flow_level and ch in u'?:'))) (ch == u'-' or (not self.flow_level and ch in u'?:')))
# Scanners. # Scanners.
@ -804,21 +825,23 @@ def scan_directive_name(self, start_mark):
# See the specification for details. # See the specification for details.
length = 0 length = 0
ch = self.peek(length) ch = self.peek(length)
while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \
or ch in u'-_': or ch in u'-_:.':
length += 1 length += 1
ch = self.peek(length) ch = self.peek(length)
if not length: if not length:
raise ScannerError("while scanning a directive", start_mark, raise ScannerError(
"expected alphabetic or numeric character, but found %r" "while scanning a directive", start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected alphabetic or numeric character, but found %r"
% utf8(ch), self.get_mark())
value = self.prefix(length) value = self.prefix(length)
self.forward(length) self.forward(length)
ch = self.peek() ch = self.peek()
if ch not in u'\0 \r\n\x85\u2028\u2029': if ch not in u'\0 \r\n\x85\u2028\u2029':
raise ScannerError("while scanning a directive", start_mark, raise ScannerError(
"expected alphabetic or numeric character, but found %r" "while scanning a directive", start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected alphabetic or numeric character, but found %r"
% utf8(ch), self.get_mark())
return value return value
def scan_yaml_directive_value(self, start_mark): def scan_yaml_directive_value(self, start_mark):
@ -827,26 +850,29 @@ def scan_yaml_directive_value(self, start_mark):
self.forward() self.forward()
major = self.scan_yaml_directive_number(start_mark) major = self.scan_yaml_directive_number(start_mark)
if self.peek() != '.': if self.peek() != '.':
raise ScannerError("while scanning a directive", start_mark, raise ScannerError(
"expected a digit or '.', but found %r" "while scanning a directive", start_mark,
% self.peek().encode('utf-8'), "expected a digit or '.', but found %r"
self.get_mark()) % utf8(self.peek()),
self.get_mark())
self.forward() self.forward()
minor = self.scan_yaml_directive_number(start_mark) minor = self.scan_yaml_directive_number(start_mark)
if self.peek() not in u'\0 \r\n\x85\u2028\u2029': if self.peek() not in u'\0 \r\n\x85\u2028\u2029':
raise ScannerError("while scanning a directive", start_mark, raise ScannerError(
"expected a digit or ' ', but found %r" "while scanning a directive", start_mark,
% self.peek().encode('utf-8'), "expected a digit or ' ', but found %r"
self.get_mark()) % utf8(self.peek()),
self.get_mark())
return (major, minor) return (major, minor)
def scan_yaml_directive_number(self, start_mark): def scan_yaml_directive_number(self, start_mark):
# See the specification for details. # See the specification for details.
ch = self.peek() ch = self.peek()
if not (u'0' <= ch <= u'9'): if not (u'0' <= ch <= u'9'):
raise ScannerError("while scanning a directive", start_mark, raise ScannerError(
"expected a digit, but found %r" % ch.encode('utf-8'), "while scanning a directive", start_mark,
self.get_mark()) "expected a digit, but found %r" % utf8(ch),
self.get_mark())
length = 0 length = 0
while u'0' <= self.peek(length) <= u'9': while u'0' <= self.peek(length) <= u'9':
length += 1 length += 1
@ -870,8 +896,8 @@ def scan_tag_directive_handle(self, start_mark):
ch = self.peek() ch = self.peek()
if ch != u' ': if ch != u' ':
raise ScannerError("while scanning a directive", start_mark, raise ScannerError("while scanning a directive", start_mark,
"expected ' ', but found %r" % ch.encode('utf-8'), "expected ' ', but found %r" % utf8(ch),
self.get_mark()) self.get_mark())
return value return value
def scan_tag_directive_prefix(self, start_mark): def scan_tag_directive_prefix(self, start_mark):
@ -880,8 +906,8 @@ def scan_tag_directive_prefix(self, start_mark):
ch = self.peek() ch = self.peek()
if ch not in u'\0 \r\n\x85\u2028\u2029': if ch not in u'\0 \r\n\x85\u2028\u2029':
raise ScannerError("while scanning a directive", start_mark, raise ScannerError("while scanning a directive", start_mark,
"expected ' ', but found %r" % ch.encode('utf-8'), "expected ' ', but found %r" % utf8(ch),
self.get_mark()) self.get_mark())
return value return value
def scan_directive_ignored_line(self, start_mark): def scan_directive_ignored_line(self, start_mark):
@ -893,9 +919,10 @@ def scan_directive_ignored_line(self, start_mark):
self.forward() self.forward()
ch = self.peek() ch = self.peek()
if ch not in u'\0\r\n\x85\u2028\u2029': if ch not in u'\0\r\n\x85\u2028\u2029':
raise ScannerError("while scanning a directive", start_mark, raise ScannerError(
"expected a comment or a line break, but found %r" "while scanning a directive", start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected a comment or a line break, but found %r"
% utf8(ch), self.get_mark())
self.scan_line_break() self.scan_line_break()
def scan_anchor(self, TokenClass): def scan_anchor(self, TokenClass):
@ -916,21 +943,23 @@ def scan_anchor(self, TokenClass):
self.forward() self.forward()
length = 0 length = 0
ch = self.peek(length) ch = self.peek(length)
while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \
or ch in u'-_': or ch in u'-_':
length += 1 length += 1
ch = self.peek(length) ch = self.peek(length)
if not length: if not length:
raise ScannerError("while scanning an %s" % name, start_mark, raise ScannerError(
"expected alphabetic or numeric character, but found %r" "while scanning an %s" % name, start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected alphabetic or numeric character, but found %r"
% utf8(ch), self.get_mark())
value = self.prefix(length) value = self.prefix(length)
self.forward(length) self.forward(length)
ch = self.peek() ch = self.peek()
if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`': if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`':
raise ScannerError("while scanning an %s" % name, start_mark, raise ScannerError(
"expected alphabetic or numeric character, but found %r" "while scanning an %s" % name, start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected alphabetic or numeric character, but found %r"
% utf8(ch), self.get_mark())
end_mark = self.get_mark() end_mark = self.get_mark()
return TokenClass(value, start_mark, end_mark) return TokenClass(value, start_mark, end_mark)
@ -943,9 +972,10 @@ def scan_tag(self):
self.forward(2) self.forward(2)
suffix = self.scan_tag_uri('tag', start_mark) suffix = self.scan_tag_uri('tag', start_mark)
if self.peek() != u'>': if self.peek() != u'>':
raise ScannerError("while parsing a tag", start_mark, raise ScannerError(
"expected '>', but found %r" % self.peek().encode('utf-8'), "while parsing a tag", start_mark,
self.get_mark()) "expected '>', but found %r" % utf8(self.peek()),
self.get_mark())
self.forward() self.forward()
elif ch in u'\0 \t\r\n\x85\u2028\u2029': elif ch in u'\0 \t\r\n\x85\u2028\u2029':
handle = None handle = None
@ -970,8 +1000,8 @@ def scan_tag(self):
ch = self.peek() ch = self.peek()
if ch not in u'\0 \r\n\x85\u2028\u2029': if ch not in u'\0 \r\n\x85\u2028\u2029':
raise ScannerError("while scanning a tag", start_mark, raise ScannerError("while scanning a tag", start_mark,
"expected ' ', but found %r" % ch.encode('utf-8'), "expected ' ', but found %r" % utf8(ch),
self.get_mark()) self.get_mark())
value = (handle, suffix) value = (handle, suffix)
end_mark = self.get_mark() end_mark = self.get_mark()
return TagToken(value, start_mark, end_mark) return TagToken(value, start_mark, end_mark)
@ -1020,37 +1050,49 @@ def scan_block_scalar(self, style):
# Unfortunately, folding rules are ambiguous. # Unfortunately, folding rules are ambiguous.
# #
# This is the folding according to the specification: # This is the folding according to the specification:
if folded and line_break == u'\n' \ if folded and line_break == u'\n' \
and leading_non_space and self.peek() not in u' \t': and leading_non_space and self.peek() not in u' \t':
if not breaks: if not breaks:
chunks.append(u' ') chunks.append(u' ')
else: else:
chunks.append(line_break) chunks.append(line_break)
# This is Clark Evans's interpretation (also in the spec # This is Clark Evans's interpretation (also in the spec
# examples): # examples):
# #
#if folded and line_break == u'\n': # if folded and line_break == u'\n':
# if not breaks: # if not breaks:
# if self.peek() not in ' \t': # if self.peek() not in ' \t':
# chunks.append(u' ') # chunks.append(u' ')
# else: # else:
# chunks.append(line_break) # chunks.append(line_break)
#else: # else:
# chunks.append(line_break) # chunks.append(line_break)
else: else:
break break
# Chomp the tail. # Process trailing line breaks. The 'chomping' setting determines
if chomping is not False: # whether they are included in the value.
comment = []
if chomping in [None, True]:
chunks.append(line_break) chunks.append(line_break)
if chomping is True: if chomping is True:
chunks.extend(breaks) chunks.extend(breaks)
elif chomping in [None, False]:
comment.extend(breaks)
# We are done. # We are done.
return ScalarToken(u''.join(chunks), False, start_mark, end_mark, token = ScalarToken(u''.join(chunks), False, start_mark, end_mark,
style) style)
if len(comment) > 0:
# Keep track of the trailing whitespace as a comment token, if
# isn't all included in the actual value.
comment_end_mark = self.get_mark()
comment = CommentToken(''.join(comment), end_mark,
comment_end_mark)
token.add_post_comment(comment)
return token
def scan_block_scalar_indicators(self, start_mark): def scan_block_scalar_indicators(self, start_mark):
# See the specification for details. # See the specification for details.
@ -1067,16 +1109,19 @@ def scan_block_scalar_indicators(self, start_mark):
if ch in u'0123456789': if ch in u'0123456789':
increment = int(ch) increment = int(ch)
if increment == 0: if increment == 0:
raise ScannerError("while scanning a block scalar", start_mark, raise ScannerError(
"expected indentation indicator in the range 1-9, but found 0", "while scanning a block scalar", start_mark,
self.get_mark()) "expected indentation indicator in the range 1-9, "
"but found 0", self.get_mark())
self.forward() self.forward()
elif ch in u'0123456789': elif ch in u'0123456789':
increment = int(ch) increment = int(ch)
if increment == 0: if increment == 0:
raise ScannerError("while scanning a block scalar", start_mark, raise ScannerError(
"expected indentation indicator in the range 1-9, but found 0", "while scanning a block scalar", start_mark,
self.get_mark()) "expected indentation indicator in the range 1-9, "
"but found 0",
self.get_mark())
self.forward() self.forward()
ch = self.peek() ch = self.peek()
if ch in u'+-': if ch in u'+-':
@ -1087,9 +1132,10 @@ def scan_block_scalar_indicators(self, start_mark):
self.forward() self.forward()
ch = self.peek() ch = self.peek()
if ch not in u'\0 \r\n\x85\u2028\u2029': if ch not in u'\0 \r\n\x85\u2028\u2029':
raise ScannerError("while scanning a block scalar", start_mark, raise ScannerError(
"expected chomping or indentation indicators, but found %r" "while scanning a block scalar", start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected chomping or indentation indicators, but found %r"
% utf8(ch), self.get_mark())
return chomping, increment return chomping, increment
def scan_block_scalar_ignored_line(self, start_mark): def scan_block_scalar_ignored_line(self, start_mark):
@ -1101,9 +1147,10 @@ def scan_block_scalar_ignored_line(self, start_mark):
self.forward() self.forward()
ch = self.peek() ch = self.peek()
if ch not in u'\0\r\n\x85\u2028\u2029': if ch not in u'\0\r\n\x85\u2028\u2029':
raise ScannerError("while scanning a block scalar", start_mark, raise ScannerError(
"expected a comment or a line break, but found %r" "while scanning a block scalar", start_mark,
% ch.encode('utf-8'), self.get_mark()) "expected a comment or a line break, but found %r"
% utf8(ch), self.get_mark())
self.scan_line_break() self.scan_line_break()
def scan_block_scalar_indentation(self): def scan_block_scalar_indentation(self):
@ -1156,7 +1203,7 @@ def scan_flow_scalar(self, style):
self.forward() self.forward()
end_mark = self.get_mark() end_mark = self.get_mark()
return ScalarToken(u''.join(chunks), False, start_mark, end_mark, return ScalarToken(u''.join(chunks), False, start_mark, end_mark,
style) style)
ESCAPE_REPLACEMENTS = { ESCAPE_REPLACEMENTS = {
u'0': u'\0', u'0': u'\0',
@ -1171,6 +1218,7 @@ def scan_flow_scalar(self, style):
u'e': u'\x1B', u'e': u'\x1B',
u' ': u'\x20', u' ': u'\x20',
u'\"': u'\"', u'\"': u'\"',
u'/': u'/', # as per http://www.json.org/
u'\\': u'\\', u'\\': u'\\',
u'N': u'\x85', u'N': u'\x85',
u'_': u'\xA0', u'_': u'\xA0',
@ -1212,18 +1260,24 @@ def scan_flow_scalar_non_spaces(self, double, start_mark):
self.forward() self.forward()
for k in range(length): for k in range(length):
if self.peek(k) not in u'0123456789ABCDEFabcdef': if self.peek(k) not in u'0123456789ABCDEFabcdef':
raise ScannerError("while scanning a double-quoted scalar", start_mark, raise ScannerError(
"expected escape sequence of %d hexdecimal numbers, but found %r" % "while scanning a double-quoted scalar",
(length, self.peek(k).encode('utf-8')), self.get_mark()) start_mark,
"expected escape sequence of %d hexdecimal "
"numbers, but found %r" %
(length, utf8(self.peek(k))), self.get_mark())
code = int(self.prefix(length), 16) code = int(self.prefix(length), 16)
chunks.append(unichr(code)) chunks.append(unichr(code))
self.forward(length) self.forward(length)
elif ch in u'\r\n\x85\u2028\u2029': elif ch in u'\r\n\x85\u2028\u2029':
self.scan_line_break() self.scan_line_break()
chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) chunks.extend(self.scan_flow_scalar_breaks(
double, start_mark))
else: else:
raise ScannerError("while scanning a double-quoted scalar", start_mark, raise ScannerError(
"found unknown escape character %r" % ch.encode('utf-8'), self.get_mark()) "while scanning a double-quoted scalar", start_mark,
"found unknown escape character %r" % utf8(ch),
self.get_mark())
else: else:
return chunks return chunks
@ -1237,8 +1291,9 @@ def scan_flow_scalar_spaces(self, double, start_mark):
self.forward(length) self.forward(length)
ch = self.peek() ch = self.peek()
if ch == u'\0': if ch == u'\0':
raise ScannerError("while scanning a quoted scalar", start_mark, raise ScannerError(
"found unexpected end of stream", self.get_mark()) "while scanning a quoted scalar", start_mark,
"found unexpected end of stream", self.get_mark())
elif ch in u'\r\n\x85\u2028\u2029': elif ch in u'\r\n\x85\u2028\u2029':
line_break = self.scan_line_break() line_break = self.scan_line_break()
breaks = self.scan_flow_scalar_breaks(double, start_mark) breaks = self.scan_flow_scalar_breaks(double, start_mark)
@ -1260,8 +1315,10 @@ def scan_flow_scalar_breaks(self, double, start_mark):
prefix = self.prefix(3) prefix = self.prefix(3)
if (prefix == u'---' or prefix == u'...') \ if (prefix == u'---' or prefix == u'...') \
and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
raise ScannerError("while scanning a quoted scalar", start_mark, raise ScannerError("while scanning a quoted scalar",
"found unexpected document separator", self.get_mark()) start_mark,
"found unexpected document separator",
self.get_mark())
while self.peek() in u' \t': while self.peek() in u' \t':
self.forward() self.forward()
if self.peek() in u'\r\n\x85\u2028\u2029': if self.peek() in u'\r\n\x85\u2028\u2029':
@ -1281,8 +1338,8 @@ def scan_plain(self):
indent = self.indent+1 indent = self.indent+1
# We allow zero indentation for scalars, but then we need to check for # We allow zero indentation for scalars, but then we need to check for
# document separators at the beginning of the line. # document separators at the beginning of the line.
#if indent == 0: # if indent == 0:
# indent = 1 # indent = 1
spaces = [] spaces = []
while True: while True:
length = 0 length = 0
@ -1290,19 +1347,22 @@ def scan_plain(self):
break break
while True: while True:
ch = self.peek(length) ch = self.peek(length)
if ch in u'\0 \t\r\n\x85\u2028\u2029' \ if ch in u'\0 \t\r\n\x85\u2028\u2029' \
or (not self.flow_level and ch == u':' and or (not self.flow_level and ch == u':' and
self.peek(length+1) in u'\0 \t\r\n\x85\u2028\u2029') \ self.peek(length+1) in u'\0 \t\r\n\x85\u2028\u2029') \
or (self.flow_level and ch in u',:?[]{}'): or (self.flow_level and ch in u',:?[]{}'):
break break
length += 1 length += 1
# It's not clear what we should do with ':' in the flow context. # It's not clear what we should do with ':' in the flow context.
if (self.flow_level and ch == u':' if (self.flow_level and ch == u':' and
and self.peek(length+1) not in u'\0 \t\r\n\x85\u2028\u2029,[]{}'): self.peek(length+1) not in u'\0 \t\r\n\x85\u2028\u2029,[]{}'):
self.forward(length) self.forward(length)
raise ScannerError("while scanning a plain scalar", start_mark, raise ScannerError(
"while scanning a plain scalar", start_mark,
"found unexpected ':'", self.get_mark(), "found unexpected ':'", self.get_mark(),
"Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.") "Please check "
"http://pyyaml.org/wiki/YAMLColonInFlowContext "
"for details.")
if length == 0: if length == 0:
break break
self.allow_simple_key = False self.allow_simple_key = False
@ -1314,7 +1374,13 @@ def scan_plain(self):
if not spaces or self.peek() == u'#' \ if not spaces or self.peek() == u'#' \
or (not self.flow_level and self.column < indent): or (not self.flow_level and self.column < indent):
break break
return ScalarToken(u''.join(chunks), True, start_mark, end_mark)
token = ScalarToken(u''.join(chunks), True, start_mark, end_mark)
if spaces and spaces[0] == '\n':
# Create a comment token to preserve the trailing line breaks.
comment = CommentToken(''.join(spaces) + '\n', start_mark, end_mark)
token.add_post_comment(comment)
return token
def scan_plain_spaces(self, indent, start_mark): def scan_plain_spaces(self, indent, start_mark):
# See the specification for details. # See the specification for details.
@ -1342,7 +1408,7 @@ def scan_plain_spaces(self, indent, start_mark):
breaks.append(self.scan_line_break()) breaks.append(self.scan_line_break())
prefix = self.prefix(3) prefix = self.prefix(3)
if (prefix == u'---' or prefix == u'...') \ if (prefix == u'---' or prefix == u'...') \
and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029':
return return
if line_break != u'\n': if line_break != u'\n':
chunks.append(line_break) chunks.append(line_break)
@ -1360,20 +1426,21 @@ def scan_tag_handle(self, name, start_mark):
ch = self.peek() ch = self.peek()
if ch != u'!': if ch != u'!':
raise ScannerError("while scanning a %s" % name, start_mark, raise ScannerError("while scanning a %s" % name, start_mark,
"expected '!', but found %r" % ch.encode('utf-8'), "expected '!', but found %r" % utf8(ch),
self.get_mark()) self.get_mark())
length = 1 length = 1
ch = self.peek(length) ch = self.peek(length)
if ch != u' ': if ch != u' ':
while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' \
or ch in u'-_': or u'a' <= ch <= u'z' \
or ch in u'-_':
length += 1 length += 1
ch = self.peek(length) ch = self.peek(length)
if ch != u'!': if ch != u'!':
self.forward(length) self.forward(length)
raise ScannerError("while scanning a %s" % name, start_mark, raise ScannerError("while scanning a %s" % name, start_mark,
"expected '!', but found %r" % ch.encode('utf-8'), "expected '!', but found %r" % utf8(ch),
self.get_mark()) self.get_mark())
length += 1 length += 1
value = self.prefix(length) value = self.prefix(length)
self.forward(length) self.forward(length)
@ -1401,27 +1468,36 @@ def scan_tag_uri(self, name, start_mark):
length = 0 length = 0
if not chunks: if not chunks:
raise ScannerError("while parsing a %s" % name, start_mark, raise ScannerError("while parsing a %s" % name, start_mark,
"expected URI, but found %r" % ch.encode('utf-8'), "expected URI, but found %r" % utf8(ch),
self.get_mark()) self.get_mark())
return u''.join(chunks) return u''.join(chunks)
def scan_uri_escapes(self, name, start_mark): def scan_uri_escapes(self, name, start_mark):
# See the specification for details. # See the specification for details.
bytes = [] code_bytes = []
mark = self.get_mark() mark = self.get_mark()
while self.peek() == u'%': while self.peek() == u'%':
self.forward() self.forward()
for k in range(2): for k in range(2):
if self.peek(k) not in u'0123456789ABCDEFabcdef': if self.peek(k) not in u'0123456789ABCDEFabcdef':
raise ScannerError("while scanning a %s" % name, start_mark, raise ScannerError(
"expected URI escape sequence of 2 hexdecimal numbers, but found %r" % "while scanning a %s" % name, start_mark,
(self.peek(k).encode('utf-8')), self.get_mark()) "expected URI escape sequence of 2 hexdecimal numbers,"
bytes.append(chr(int(self.prefix(2), 16))) " but found %r"
% utf8(self.peek(k)), self.get_mark())
if PY3:
code_bytes.append(int(self.prefix(2), 16))
else:
code_bytes.append(chr(int(self.prefix(2), 16)))
self.forward(2) self.forward(2)
try: try:
value = unicode(''.join(bytes), 'utf-8') if PY3:
except UnicodeDecodeError, exc: value = bytes(code_bytes).decode('utf-8')
raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) else:
value = unicode(''.join(code_bytes), 'utf-8')
except UnicodeDecodeError as exc:
raise ScannerError("while scanning a %s" % name, start_mark,
str(exc), mark)
return value return value
def scan_line_break(self): def scan_line_break(self):
@ -1445,9 +1521,141 @@ def scan_line_break(self):
return ch return ch
return u'' return u''
#try:
# import psyco
# psyco.bind(Scanner)
#except ImportError:
# pass
class RoundTripScanner(Scanner):
def check_token(self, *choices):
# Check if the next token is one of the given types.
while self.need_more_tokens():
self.fetch_more_tokens()
self._gather_comments()
if self.tokens:
if not choices:
return True
for choice in choices:
if isinstance(self.tokens[0], choice):
return True
return False
def peek_token(self):
# Return the next token, but do not delete if from the queue.
while self.need_more_tokens():
self.fetch_more_tokens()
self._gather_comments()
if self.tokens:
return self.tokens[0]
def _gather_comments(self):
"""combine multiple comment lines"""
comments = []
if not self.tokens:
return comments
if isinstance(self.tokens[0], CommentToken):
comment = self.tokens.pop(0)
self.tokens_taken += 1
# print('################ dropping', comment)
comments.append(comment)
while self.need_more_tokens():
self.fetch_more_tokens()
if not self.tokens:
return comments
if isinstance(self.tokens[0], CommentToken):
self.tokens_taken += 1
comment = self.tokens.pop(0)
# print 'dropping2', comment
comments.append(comment)
if len(comments) >= 1:
# print(' len', len(comments), comments)
# print(' com', comments[0], comments[0].start_mark.line)
# print(' tok', self.tokens[0].end_mark.line)
self.tokens[0].add_pre_comments(comments)
# pull in post comment on e.g. ':'
if not self.done and len(self.tokens) < 2:
self.fetch_more_tokens()
def get_token(self):
# Return the next token.
while self.need_more_tokens():
self.fetch_more_tokens()
self._gather_comments()
if self.tokens:
# only add post comment to single line tokens:
# scalar, value token. FlowXEndToken, otherwise
# hidden streamtokens could get them (leave them and they will be
# pre comments for the next map/seq
if len(self.tokens) > 1 and \
isinstance(self.tokens[0], (
ScalarToken,
ValueToken,
FlowSequenceEndToken,
FlowMappingEndToken,
)) and \
isinstance(self.tokens[1], CommentToken) and \
self.tokens[0].end_mark.line == self.tokens[1].start_mark.line:
self.tokens_taken += 1
self.tokens[0].add_post_comment(self.tokens.pop(1))
self.tokens_taken += 1
return self.tokens.pop(0)
def fetch_comment(self, comment): # XXXX
value, start_mark, end_mark = comment
self.tokens.append(CommentToken(value, start_mark, end_mark))
# scanner
def scan_to_next_token(self):
# We ignore spaces, line breaks and comments.
# If we find a line break in the block context, we set the flag
# `allow_simple_key` on.
# The byte order mark is stripped if it's the first character in the
# stream. We do not yet support BOM inside the stream as the
# specification requires. Any such mark will be considered as a part
# of the document.
#
# TODO: We need to make tab handling rules more sane. A good rule is
# Tabs cannot precede tokens
# BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END,
# KEY(block), VALUE(block), BLOCK-ENTRY
# So the checking code is
# if <TAB>:
# self.allow_simple_keys = False
# We also need to add the check for `allow_simple_keys == True` to
# `unwind_indent` before issuing BLOCK-END.
# Scanners for block, flow, and plain scalars need to be modified.
if self.index == 0 and self.peek() == u'\uFEFF':
self.forward()
found = False
while not found:
while self.peek() == u' ':
self.forward()
ch = self.peek()
if ch == u'#':
start_mark = self.get_mark()
comment = ch
self.forward()
while ch not in u'\0\r\n\x85\u2028\u2029':
ch = self.peek()
if ch == u'\0': # don't gobble the end-of-stream character
break
comment += ch
self.forward()
# gather any blank lines following the comment too
ch = self.scan_line_break()
while len(ch) > 0:
comment += ch
ch = self.scan_line_break()
end_mark = self.get_mark()
if not self.flow_level:
self.allow_simple_key = True
return comment, start_mark, end_mark
if self.scan_line_break():
if not self.flow_level:
self.allow_simple_key = True
else:
found = True
# try:
# import psyco
# psyco.bind(Scanner)
# except ImportError:
# pass

View file

@ -0,0 +1,178 @@
# coding: utf-8
from __future__ import absolute_import
import re
try:
from .error import YAMLError
from .compat import nprint, DBG_NODE, dbg, string_types
except (ImportError, ValueError): # for Jython
from ruamel.yaml.error import YAMLError
from ruamel.yaml.compat import nprint, DBG_NODE, dbg, string_types
from ruamel.yaml.events import (
StreamStartEvent, StreamEndEvent, MappingStartEvent, MappingEndEvent,
SequenceStartEvent, SequenceEndEvent, AliasEvent, ScalarEvent,
DocumentStartEvent, DocumentEndEvent,
)
from ruamel.yaml.nodes import (
MappingNode, ScalarNode, SequenceNode,
)
__all__ = ['Serializer', 'SerializerError']
class SerializerError(YAMLError):
pass
class Serializer(object):
# 'id' and 3+ numbers, but not 000
ANCHOR_TEMPLATE = u'id%03d'
ANCHOR_RE = re.compile(u'id(?!000$)\\d{3,}')
def __init__(self, encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
self.use_encoding = encoding
self.use_explicit_start = explicit_start
self.use_explicit_end = explicit_end
if isinstance(version, string_types):
self.use_version = tuple(map(int, version.split('.')))
else:
self.use_version = version
self.use_tags = tags
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
self.closed = None
self._templated_id = None
def open(self):
if self.closed is None:
self.emit(StreamStartEvent(encoding=self.use_encoding))
self.closed = False
elif self.closed:
raise SerializerError("serializer is closed")
else:
raise SerializerError("serializer is already opened")
def close(self):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif not self.closed:
self.emit(StreamEndEvent())
self.closed = True
# def __del__(self):
# self.close()
def serialize(self, node):
if dbg(DBG_NODE):
nprint('Serializing nodes')
node.dump()
if self.closed is None:
raise SerializerError("serializer is not opened")
elif self.closed:
raise SerializerError("serializer is closed")
self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
version=self.use_version,
tags=self.use_tags))
self.anchor_node(node)
self.serialize_node(node, None, None)
self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
def anchor_node(self, node):
if node in self.anchors:
if self.anchors[node] is None:
self.anchors[node] = self.generate_anchor(node)
else:
anchor = None
try:
if node.anchor.always_dump:
anchor = node.anchor.value
except:
pass
self.anchors[node] = anchor
if isinstance(node, SequenceNode):
for item in node.value:
self.anchor_node(item)
elif isinstance(node, MappingNode):
for key, value in node.value:
self.anchor_node(key)
self.anchor_node(value)
def generate_anchor(self, node):
try:
anchor = node.anchor.value
except:
anchor = None
if anchor is None:
self.last_anchor_id += 1
return self.ANCHOR_TEMPLATE % self.last_anchor_id
return anchor
def serialize_node(self, node, parent, index):
alias = self.anchors[node]
if node in self.serialized_nodes:
self.emit(AliasEvent(alias))
else:
self.serialized_nodes[node] = True
self.descend_resolver(parent, index)
if isinstance(node, ScalarNode):
# here check if the node.tag equals the one that would result from parsing
# if not equal quoting is necessary for strings
detected_tag = self.resolve(ScalarNode, node.value, (True, False))
default_tag = self.resolve(ScalarNode, node.value, (False, True))
implicit = (node.tag == detected_tag), (node.tag == default_tag)
self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
style=node.style, comment=node.comment))
elif isinstance(node, SequenceNode):
implicit = (node.tag == self.resolve(SequenceNode, node.value, True))
comment = node.comment
# print('comment >>>>>>>>>>>>>.', comment, node.flow_style)
end_comment = None
seq_comment = None
if node.flow_style is True:
if comment: # eol comment on flow style sequence
seq_comment = comment[0]
# comment[0] = None
if comment and len(comment) > 2:
end_comment = comment[2]
else:
end_comment = None
self.emit(SequenceStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style,
comment=node.comment))
index = 0
for item in node.value:
self.serialize_node(item, node, index)
index += 1
self.emit(SequenceEndEvent(comment=[seq_comment, end_comment]))
elif isinstance(node, MappingNode):
implicit = (node.tag == self.resolve(MappingNode, node.value, True))
comment = node.comment
end_comment = None
map_comment = None
if node.flow_style is True:
if comment: # eol comment on flow style sequence
map_comment = comment[0]
# comment[0] = None
if comment and len(comment) > 2:
end_comment = comment[2]
self.emit(MappingStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style,
comment=node.comment))
for key, value in node.value:
self.serialize_node(key, node, None)
self.serialize_node(value, node, key)
self.emit(MappingEndEvent(comment=[map_comment, end_comment]))
self.ascend_resolver()
def templated_id(s):
return Serializer.ANCHOR_RE.match(s)

View file

@ -0,0 +1,5 @@
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0

195
lib/spack/external/ruamel/yaml/tokens.py vendored Normal file
View file

@ -0,0 +1,195 @@
# # header
# coding: utf-8
class Token(object):
def __init__(self, start_mark, end_mark):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
attributes = [key for key in self.__dict__
if not key.endswith('_mark')]
attributes.sort()
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
def add_post_comment(self, comment):
if not hasattr(self, '_comment'):
self._comment = [None, None]
self._comment[0] = comment
def add_pre_comments(self, comments):
if not hasattr(self, '_comment'):
self._comment = [None, None]
assert self._comment[1] is None
self._comment[1] = comments
def get_comment(self):
return getattr(self, '_comment', None)
@property
def comment(self):
return getattr(self, '_comment', None)
def move_comment(self, target):
"""move a comment from this token to target (normally next token)
used to combine e.g. comments before a BlockEntryToken to the
ScalarToken that follows it
"""
c = self.comment
if c is None:
return
# don't push beyond last element
if isinstance(target, StreamEndToken):
return
delattr(self, '_comment')
tc = target.comment
if not tc: # target comment, just insert
target._comment = c
return self
if c[0] and tc[0] or c[1] and tc[1]:
raise NotImplementedError('overlap in comment %r %r' % c, tc)
if c[0]:
tc[0] = c[0]
if c[1]:
tc[1] = c[1]
return self
def split_comment(self):
""" split the post part of a comment, and return it
as comment to be added. Delete second part if [None, None]
abc: # this goes to sequence
# this goes to first element
- first element
"""
comment = self.comment
if comment is None or comment[0] is None:
return None # nothing to do
ret_val = [comment[0], None]
if comment[1] is None:
delattr(self, '_comment')
return ret_val
# class BOMToken(Token):
# id = '<byte order mark>'
class DirectiveToken(Token):
id = '<directive>'
def __init__(self, name, value, start_mark, end_mark):
Token.__init__(self, start_mark, end_mark)
self.name = name
self.value = value
class DocumentStartToken(Token):
id = '<document start>'
class DocumentEndToken(Token):
id = '<document end>'
class StreamStartToken(Token):
id = '<stream start>'
def __init__(self, start_mark=None, end_mark=None, encoding=None):
Token.__init__(self, start_mark, end_mark)
self.encoding = encoding
class StreamEndToken(Token):
id = '<stream end>'
class BlockSequenceStartToken(Token):
id = '<block sequence start>'
class BlockMappingStartToken(Token):
id = '<block mapping start>'
class BlockEndToken(Token):
id = '<block end>'
class FlowSequenceStartToken(Token):
id = '['
class FlowMappingStartToken(Token):
id = '{'
class FlowSequenceEndToken(Token):
id = ']'
class FlowMappingEndToken(Token):
id = '}'
class KeyToken(Token):
id = '?'
class ValueToken(Token):
id = ':'
class BlockEntryToken(Token):
id = '-'
class FlowEntryToken(Token):
id = ','
class AliasToken(Token):
id = '<alias>'
def __init__(self, value, start_mark, end_mark):
Token.__init__(self, start_mark, end_mark)
self.value = value
class AnchorToken(Token):
id = '<anchor>'
def __init__(self, value, start_mark, end_mark):
Token.__init__(self, start_mark, end_mark)
self.value = value
class TagToken(Token):
id = '<tag>'
def __init__(self, value, start_mark, end_mark):
Token.__init__(self, start_mark, end_mark)
self.value = value
class ScalarToken(Token):
id = '<scalar>'
def __init__(self, value, plain, start_mark, end_mark, style=None):
Token.__init__(self, start_mark, end_mark)
self.value = value
self.plain = plain
self.style = style
class CommentToken(Token):
id = '<comment>'
def __init__(self, value, start_mark, end_mark):
Token.__init__(self, start_mark, end_mark)
self.value = value
def reset(self):
if hasattr(self, 'pre_done'):
delattr(self, 'pre_done')

139
lib/spack/external/ruamel/yaml/util.py vendored Normal file
View file

@ -0,0 +1,139 @@
# coding: utf-8
"""
some helper functions that might be generally useful
"""
from __future__ import print_function
from __future__ import absolute_import
from .compat import text_type, binary_type
from .main import round_trip_load
# originally as comment
# https://github.com/pre-commit/pre-commit/pull/211#issuecomment-186466605
# if you use this in your code, I suggest adding a test in your test suite
# that check this routines output against a known piece of your YAML
# before upgrades to this code break your round-tripped YAML
def load_yaml_guess_indent(stream, **kw):
"""guess the indent and block sequence indent of yaml stream/string
returns round_trip_loaded stream, indent level, block sequence indent
- block sequence indent is the number of spaces before a dash relative to previous indent
- if there are no block sequences, indent is taken from nested mappings, block sequence
indent is unset (None) in that case
"""
# load a yaml file guess the indentation, if you use TABs ...
def leading_spaces(l):
idx = 0
while idx < len(l) and l[idx] == ' ':
idx += 1
return idx
if isinstance(stream, text_type):
yaml_str = stream
elif isinstance(stream, binary_type):
yaml_str = stream.decode('utf-8') # most likely, but the Reader checks BOM for this
else:
yaml_str = stream.read()
map_indent = None
indent = None # default if not found for some reason
block_seq_indent = None
prev_line_key_only = None
key_indent = 0
for line in yaml_str.splitlines():
rline = line.rstrip()
lline = rline.lstrip()
if lline.startswith('- '):
l_s = leading_spaces(line)
block_seq_indent = l_s - key_indent
idx = l_s + 1
while line[idx] == ' ': # this will end as we rstripped
idx += 1
if line[idx] == '#': # comment after -
continue
indent = idx - key_indent
break
if map_indent is None and prev_line_key_only is not None and rline:
idx = 0
while line[idx] in ' -':
idx += 1
if idx > prev_line_key_only:
map_indent = idx - prev_line_key_only
if rline.endswith(':'):
key_indent = leading_spaces(line)
idx = 0
while line[idx] == ' ': # this will end on ':'
idx += 1
prev_line_key_only = idx
continue
prev_line_key_only = None
if indent is None and map_indent is not None:
indent = map_indent
return round_trip_load(yaml_str, **kw), indent, block_seq_indent
def configobj_walker(cfg):
"""
walks over a ConfigObj (INI file with comments) generating
corresponding YAML output (including comments
"""
from configobj import ConfigObj
assert isinstance(cfg, ConfigObj)
for c in cfg.initial_comment:
if c.strip():
yield c
for s in _walk_section(cfg):
if s.strip():
yield s
for c in cfg.final_comment:
if c.strip():
yield c
def _walk_section(s, level=0):
from configobj import Section
assert isinstance(s, Section)
indent = u' ' * level
for name in s.scalars:
for c in s.comments[name]:
yield indent + c.strip()
x = s[name]
if u'\n' in x:
i = indent + u' '
x = u'|\n' + i + x.strip().replace(u'\n', u'\n' + i)
elif ':' in x:
x = u"'" + x.replace(u"'", u"''") + u"'"
line = u'{0}{1}: {2}'.format(indent, name, x)
c = s.inline_comments[name]
if c:
line += u' ' + c
yield line
for name in s.sections:
for c in s.comments[name]:
yield indent + c.strip()
line = u'{0}{1}:'.format(indent, name)
c = s.inline_comments[name]
if c:
line += u' ' + c
yield line
for val in _walk_section(s[name], level=level+1):
yield val
# def config_obj_2_rt_yaml(cfg):
# from .comments import CommentedMap, CommentedSeq
# from configobj import ConfigObj
# assert isinstance(cfg, ConfigObj)
# #for c in cfg.initial_comment:
# # if c.strip():
# # pass
# cm = CommentedMap()
# for name in s.sections:
# cm[name] = d = CommentedMap()
#
#
# #for c in cfg.final_comment:
# # if c.strip():
# # yield c
# return cm

View file

@ -1,19 +0,0 @@
Copyright (c) 2006 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,35 +0,0 @@
PyYAML - The next generation YAML parser and emitter for Python.
To install, type 'python setup.py install'.
By default, the setup.py script checks whether LibYAML is installed
and if so, builds and installs LibYAML bindings. To skip the check
and force installation of LibYAML bindings, use the option '--with-libyaml':
'python setup.py --with-libyaml install'. To disable the check and
skip building and installing LibYAML bindings, use '--without-libyaml':
'python setup.py --without-libyaml install'.
When LibYAML bindings are installed, you may use fast LibYAML-based
parser and emitter as follows:
>>> yaml.load(stream, Loader=yaml.CLoader)
>>> yaml.dump(data, Dumper=yaml.CDumper)
PyYAML includes a comprehensive test suite. To run the tests,
type 'python setup.py test'.
For more information, check the PyYAML homepage:
'http://pyyaml.org/wiki/PyYAML'.
For PyYAML tutorial and reference, see:
'http://pyyaml.org/wiki/PyYAMLDocumentation'.
Post your questions and opinions to the YAML-Core mailing list:
'http://lists.sourceforge.net/lists/listinfo/yaml-core'.
Submit bug reports and feature requests to the PyYAML bug tracker:
'https://bitbucket.org/xi/pyyaml/issues/new'.
PyYAML is written by Kirill Simonov <xi@resolvent.net>. It is released
under the MIT license. See the file LICENSE for more details.

View file

@ -1,315 +0,0 @@
from error import *
from tokens import *
from events import *
from nodes import *
from loader import *
from dumper import *
__version__ = '3.12'
try:
from cyaml import *
__with_libyaml__ = True
except ImportError:
__with_libyaml__ = False
def scan(stream, Loader=Loader):
"""
Scan a YAML stream and produce scanning tokens.
"""
loader = Loader(stream)
try:
while loader.check_token():
yield loader.get_token()
finally:
loader.dispose()
def parse(stream, Loader=Loader):
"""
Parse a YAML stream and produce parsing events.
"""
loader = Loader(stream)
try:
while loader.check_event():
yield loader.get_event()
finally:
loader.dispose()
def compose(stream, Loader=Loader):
"""
Parse the first YAML document in a stream
and produce the corresponding representation tree.
"""
loader = Loader(stream)
try:
return loader.get_single_node()
finally:
loader.dispose()
def compose_all(stream, Loader=Loader):
"""
Parse all YAML documents in a stream
and produce corresponding representation trees.
"""
loader = Loader(stream)
try:
while loader.check_node():
yield loader.get_node()
finally:
loader.dispose()
def load(stream, Loader=Loader):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
"""
loader = Loader(stream)
try:
return loader.get_single_data()
finally:
loader.dispose()
def load_all(stream, Loader=Loader):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
"""
loader = Loader(stream)
try:
while loader.check_data():
yield loader.get_data()
finally:
loader.dispose()
def safe_load(stream):
"""
Parse the first YAML document in a stream
and produce the corresponding Python object.
Resolve only basic YAML tags.
"""
return load(stream, SafeLoader)
def safe_load_all(stream):
"""
Parse all YAML documents in a stream
and produce corresponding Python objects.
Resolve only basic YAML tags.
"""
return load_all(stream, SafeLoader)
def emit(events, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None):
"""
Emit YAML parsing events into a stream.
If stream is None, return the produced string instead.
"""
getvalue = None
if stream is None:
from StringIO import StringIO
stream = StringIO()
getvalue = stream.getvalue
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
try:
for event in events:
dumper.emit(event)
finally:
dumper.dispose()
if getvalue:
return getvalue()
def serialize_all(nodes, stream=None, Dumper=Dumper,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding='utf-8', explicit_start=None, explicit_end=None,
version=None, tags=None):
"""
Serialize a sequence of representation trees into a YAML stream.
If stream is None, return the produced string instead.
"""
getvalue = None
if stream is None:
if encoding is None:
from StringIO import StringIO
else:
from cStringIO import StringIO
stream = StringIO()
getvalue = stream.getvalue
dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
encoding=encoding, version=version, tags=tags,
explicit_start=explicit_start, explicit_end=explicit_end)
try:
dumper.open()
for node in nodes:
dumper.serialize(node)
dumper.close()
finally:
dumper.dispose()
if getvalue:
return getvalue()
def serialize(node, stream=None, Dumper=Dumper, **kwds):
"""
Serialize a representation tree into a YAML stream.
If stream is None, return the produced string instead.
"""
return serialize_all([node], stream, Dumper=Dumper, **kwds)
def dump_all(documents, stream=None, Dumper=Dumper,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding='utf-8', explicit_start=None, explicit_end=None,
version=None, tags=None):
"""
Serialize a sequence of Python objects into a YAML stream.
If stream is None, return the produced string instead.
"""
getvalue = None
if stream is None:
if encoding is None:
from StringIO import StringIO
else:
from cStringIO import StringIO
stream = StringIO()
getvalue = stream.getvalue
dumper = Dumper(stream, default_style=default_style,
default_flow_style=default_flow_style,
canonical=canonical, indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break,
encoding=encoding, version=version, tags=tags,
explicit_start=explicit_start, explicit_end=explicit_end)
try:
dumper.open()
for data in documents:
dumper.represent(data)
dumper.close()
finally:
dumper.dispose()
if getvalue:
return getvalue()
def dump(data, stream=None, Dumper=Dumper, **kwds):
"""
Serialize a Python object into a YAML stream.
If stream is None, return the produced string instead.
"""
return dump_all([data], stream, Dumper=Dumper, **kwds)
def safe_dump_all(documents, stream=None, **kwds):
"""
Serialize a sequence of Python objects into a YAML stream.
Produce only basic YAML tags.
If stream is None, return the produced string instead.
"""
return dump_all(documents, stream, Dumper=SafeDumper, **kwds)
def safe_dump(data, stream=None, **kwds):
"""
Serialize a Python object into a YAML stream.
Produce only basic YAML tags.
If stream is None, return the produced string instead.
"""
return dump_all([data], stream, Dumper=SafeDumper, **kwds)
def add_implicit_resolver(tag, regexp, first=None,
Loader=Loader, Dumper=Dumper):
"""
Add an implicit scalar detector.
If an implicit scalar value matches the given regexp,
the corresponding tag is assigned to the scalar.
first is a sequence of possible initial characters or None.
"""
Loader.add_implicit_resolver(tag, regexp, first)
Dumper.add_implicit_resolver(tag, regexp, first)
def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper):
"""
Add a path based resolver for the given tag.
A path is a list of keys that forms a path
to a node in the representation tree.
Keys can be string values, integers, or None.
"""
Loader.add_path_resolver(tag, path, kind)
Dumper.add_path_resolver(tag, path, kind)
def add_constructor(tag, constructor, Loader=Loader):
"""
Add a constructor for the given tag.
Constructor is a function that accepts a Loader instance
and a node object and produces the corresponding Python object.
"""
Loader.add_constructor(tag, constructor)
def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader):
"""
Add a multi-constructor for the given tag prefix.
Multi-constructor is called for a node if its tag starts with tag_prefix.
Multi-constructor accepts a Loader instance, a tag suffix,
and a node object and produces the corresponding Python object.
"""
Loader.add_multi_constructor(tag_prefix, multi_constructor)
def add_representer(data_type, representer, Dumper=Dumper):
"""
Add a representer for the given type.
Representer is a function accepting a Dumper instance
and an instance of the given data type
and producing the corresponding representation node.
"""
Dumper.add_representer(data_type, representer)
def add_multi_representer(data_type, multi_representer, Dumper=Dumper):
"""
Add a representer for the given type.
Multi-representer is a function accepting a Dumper instance
and an instance of the given data type or subtype
and producing the corresponding representation node.
"""
Dumper.add_multi_representer(data_type, multi_representer)
class YAMLObjectMetaclass(type):
"""
The metaclass for YAMLObject.
"""
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
cls.yaml_dumper.add_representer(cls, cls.to_yaml)
class YAMLObject(object):
"""
An object that can dump itself to a YAML stream
and load itself from a YAML stream.
"""
__metaclass__ = YAMLObjectMetaclass
__slots__ = () # no direct instantiation, so allow immutable subclasses
yaml_loader = Loader
yaml_dumper = Dumper
yaml_tag = None
yaml_flow_style = None
def from_yaml(cls, loader, node):
"""
Convert a representation node to a Python object.
"""
return loader.construct_yaml_object(node, cls)
from_yaml = classmethod(from_yaml)
def to_yaml(cls, dumper, data):
"""
Convert a Python object to a representation node.
"""
return dumper.represent_yaml_object(cls.yaml_tag, data, cls,
flow_style=cls.yaml_flow_style)
to_yaml = classmethod(to_yaml)

View file

@ -1,675 +0,0 @@
__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
'ConstructorError']
from error import *
from nodes import *
import datetime
import binascii, re, sys, types
class ConstructorError(MarkedYAMLError):
pass
class BaseConstructor(object):
yaml_constructors = {}
yaml_multi_constructors = {}
def __init__(self):
self.constructed_objects = {}
self.recursive_objects = {}
self.state_generators = []
self.deep_construct = False
def check_data(self):
# If there are more documents available?
return self.check_node()
def get_data(self):
# Construct and return the next document.
if self.check_node():
return self.construct_document(self.get_node())
def get_single_data(self):
# Ensure that the stream contains a single document and construct it.
node = self.get_single_node()
if node is not None:
return self.construct_document(node)
return None
def construct_document(self, node):
data = self.construct_object(node)
while self.state_generators:
state_generators = self.state_generators
self.state_generators = []
for generator in state_generators:
for dummy in generator:
pass
self.constructed_objects = {}
self.recursive_objects = {}
self.deep_construct = False
return data
def construct_object(self, node, deep=False):
if node in self.constructed_objects:
return self.constructed_objects[node]
if deep:
old_deep = self.deep_construct
self.deep_construct = True
if node in self.recursive_objects:
raise ConstructorError(None, None,
"found unconstructable recursive node", node.start_mark)
self.recursive_objects[node] = None
constructor = None
tag_suffix = None
if node.tag in self.yaml_constructors:
constructor = self.yaml_constructors[node.tag]
else:
for tag_prefix in self.yaml_multi_constructors:
if node.tag.startswith(tag_prefix):
tag_suffix = node.tag[len(tag_prefix):]
constructor = self.yaml_multi_constructors[tag_prefix]
break
else:
if None in self.yaml_multi_constructors:
tag_suffix = node.tag
constructor = self.yaml_multi_constructors[None]
elif None in self.yaml_constructors:
constructor = self.yaml_constructors[None]
elif isinstance(node, ScalarNode):
constructor = self.__class__.construct_scalar
elif isinstance(node, SequenceNode):
constructor = self.__class__.construct_sequence
elif isinstance(node, MappingNode):
constructor = self.__class__.construct_mapping
if tag_suffix is None:
data = constructor(self, node)
else:
data = constructor(self, tag_suffix, node)
if isinstance(data, types.GeneratorType):
generator = data
data = generator.next()
if self.deep_construct:
for dummy in generator:
pass
else:
self.state_generators.append(generator)
self.constructed_objects[node] = data
del self.recursive_objects[node]
if deep:
self.deep_construct = old_deep
return data
def construct_scalar(self, node):
if not isinstance(node, ScalarNode):
raise ConstructorError(None, None,
"expected a scalar node, but found %s" % node.id,
node.start_mark)
return node.value
def construct_sequence(self, node, deep=False):
if not isinstance(node, SequenceNode):
raise ConstructorError(None, None,
"expected a sequence node, but found %s" % node.id,
node.start_mark)
return [self.construct_object(child, deep=deep)
for child in node.value]
def construct_mapping(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
try:
hash(key)
except TypeError, exc:
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unacceptable key (%s)" % exc, key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
def construct_pairs(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
pairs = []
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
value = self.construct_object(value_node, deep=deep)
pairs.append((key, value))
return pairs
def add_constructor(cls, tag, constructor):
if not 'yaml_constructors' in cls.__dict__:
cls.yaml_constructors = cls.yaml_constructors.copy()
cls.yaml_constructors[tag] = constructor
add_constructor = classmethod(add_constructor)
def add_multi_constructor(cls, tag_prefix, multi_constructor):
if not 'yaml_multi_constructors' in cls.__dict__:
cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
cls.yaml_multi_constructors[tag_prefix] = multi_constructor
add_multi_constructor = classmethod(add_multi_constructor)
class SafeConstructor(BaseConstructor):
def construct_scalar(self, node):
if isinstance(node, MappingNode):
for key_node, value_node in node.value:
if key_node.tag == u'tag:yaml.org,2002:value':
return self.construct_scalar(value_node)
return BaseConstructor.construct_scalar(self, node)
def flatten_mapping(self, node):
merge = []
index = 0
while index < len(node.value):
key_node, value_node = node.value[index]
if key_node.tag == u'tag:yaml.org,2002:merge':
del node.value[index]
if isinstance(value_node, MappingNode):
self.flatten_mapping(value_node)
merge.extend(value_node.value)
elif isinstance(value_node, SequenceNode):
submerge = []
for subnode in value_node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing a mapping",
node.start_mark,
"expected a mapping for merging, but found %s"
% subnode.id, subnode.start_mark)
self.flatten_mapping(subnode)
submerge.append(subnode.value)
submerge.reverse()
for value in submerge:
merge.extend(value)
else:
raise ConstructorError("while constructing a mapping", node.start_mark,
"expected a mapping or list of mappings for merging, but found %s"
% value_node.id, value_node.start_mark)
elif key_node.tag == u'tag:yaml.org,2002:value':
key_node.tag = u'tag:yaml.org,2002:str'
index += 1
else:
index += 1
if merge:
node.value = merge + node.value
def construct_mapping(self, node, deep=False):
if isinstance(node, MappingNode):
self.flatten_mapping(node)
return BaseConstructor.construct_mapping(self, node, deep=deep)
def construct_yaml_null(self, node):
self.construct_scalar(node)
return None
bool_values = {
u'yes': True,
u'no': False,
u'true': True,
u'false': False,
u'on': True,
u'off': False,
}
def construct_yaml_bool(self, node):
value = self.construct_scalar(node)
return self.bool_values[value.lower()]
def construct_yaml_int(self, node):
value = str(self.construct_scalar(node))
value = value.replace('_', '')
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '0':
return 0
elif value.startswith('0b'):
return sign*int(value[2:], 2)
elif value.startswith('0x'):
return sign*int(value[2:], 16)
elif value[0] == '0':
return sign*int(value, 8)
elif ':' in value:
digits = [int(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0
for digit in digits:
value += digit*base
base *= 60
return sign*value
else:
return sign*int(value)
inf_value = 1e300
while inf_value != inf_value*inf_value:
inf_value *= inf_value
nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99).
def construct_yaml_float(self, node):
value = str(self.construct_scalar(node))
value = value.replace('_', '').lower()
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '.inf':
return sign*self.inf_value
elif value == '.nan':
return self.nan_value
elif ':' in value:
digits = [float(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0.0
for digit in digits:
value += digit*base
base *= 60
return sign*value
else:
return sign*float(value)
def construct_yaml_binary(self, node):
value = self.construct_scalar(node)
try:
return str(value).decode('base64')
except (binascii.Error, UnicodeEncodeError), exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
timestamp_regexp = re.compile(
ur'''^(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
def construct_yaml_timestamp(self, node):
value = self.construct_scalar(node)
match = self.timestamp_regexp.match(node.value)
values = match.groupdict()
year = int(values['year'])
month = int(values['month'])
day = int(values['day'])
if not values['hour']:
return datetime.date(year, month, day)
hour = int(values['hour'])
minute = int(values['minute'])
second = int(values['second'])
fraction = 0
if values['fraction']:
fraction = values['fraction'][:6]
while len(fraction) < 6:
fraction += '0'
fraction = int(fraction)
delta = None
if values['tz_sign']:
tz_hour = int(values['tz_hour'])
tz_minute = int(values['tz_minute'] or 0)
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
if values['tz_sign'] == '-':
delta = -delta
data = datetime.datetime(year, month, day, hour, minute, second, fraction)
if delta:
data -= delta
return data
def construct_yaml_omap(self, node):
# Note: we do not check for duplicate keys, because it's too
# CPU-expensive.
omap = []
yield omap
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
omap.append((key, value))
def construct_yaml_pairs(self, node):
# Note: the same code as `construct_yaml_omap`.
pairs = []
yield pairs
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
pairs.append((key, value))
def construct_yaml_set(self, node):
data = set()
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_yaml_str(self, node):
value = self.construct_scalar(node)
try:
return value.encode('ascii')
except UnicodeEncodeError:
return value
def construct_yaml_seq(self, node):
data = []
yield data
data.extend(self.construct_sequence(node))
def construct_yaml_map(self, node):
data = {}
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_yaml_object(self, node, cls):
data = cls.__new__(cls)
yield data
if hasattr(data, '__setstate__'):
state = self.construct_mapping(node, deep=True)
data.__setstate__(state)
else:
state = self.construct_mapping(node)
data.__dict__.update(state)
def construct_undefined(self, node):
raise ConstructorError(None, None,
"could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
node.start_mark)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:null',
SafeConstructor.construct_yaml_null)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:bool',
SafeConstructor.construct_yaml_bool)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:int',
SafeConstructor.construct_yaml_int)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:float',
SafeConstructor.construct_yaml_float)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:binary',
SafeConstructor.construct_yaml_binary)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:timestamp',
SafeConstructor.construct_yaml_timestamp)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:omap',
SafeConstructor.construct_yaml_omap)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:pairs',
SafeConstructor.construct_yaml_pairs)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:set',
SafeConstructor.construct_yaml_set)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:str',
SafeConstructor.construct_yaml_str)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:seq',
SafeConstructor.construct_yaml_seq)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:map',
SafeConstructor.construct_yaml_map)
SafeConstructor.add_constructor(None,
SafeConstructor.construct_undefined)
class Constructor(SafeConstructor):
def construct_python_str(self, node):
return self.construct_scalar(node).encode('utf-8')
def construct_python_unicode(self, node):
return self.construct_scalar(node)
def construct_python_long(self, node):
return long(self.construct_yaml_int(node))
def construct_python_complex(self, node):
return complex(self.construct_scalar(node))
def construct_python_tuple(self, node):
return tuple(self.construct_sequence(node))
def find_python_module(self, name, mark):
if not name:
raise ConstructorError("while constructing a Python module", mark,
"expected non-empty name appended to the tag", mark)
try:
__import__(name)
except ImportError, exc:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
return sys.modules[name]
def find_python_name(self, name, mark):
if not name:
raise ConstructorError("while constructing a Python object", mark,
"expected non-empty name appended to the tag", mark)
if u'.' in name:
module_name, object_name = name.rsplit('.', 1)
else:
module_name = '__builtin__'
object_name = name
try:
__import__(module_name)
except ImportError, exc:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
module = sys.modules[module_name]
if not hasattr(module, object_name):
raise ConstructorError("while constructing a Python object", mark,
"cannot find %r in the module %r" % (object_name.encode('utf-8'),
module.__name__), mark)
return getattr(module, object_name)
def construct_python_name(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python name", node.start_mark,
"expected the empty value, but found %r" % value.encode('utf-8'),
node.start_mark)
return self.find_python_name(suffix, node.start_mark)
def construct_python_module(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python module", node.start_mark,
"expected the empty value, but found %r" % value.encode('utf-8'),
node.start_mark)
return self.find_python_module(suffix, node.start_mark)
class classobj: pass
def make_python_instance(self, suffix, node,
args=None, kwds=None, newobj=False):
if not args:
args = []
if not kwds:
kwds = {}
cls = self.find_python_name(suffix, node.start_mark)
if newobj and isinstance(cls, type(self.classobj)) \
and not args and not kwds:
instance = self.classobj()
instance.__class__ = cls
return instance
elif newobj and isinstance(cls, type):
return cls.__new__(cls, *args, **kwds)
else:
return cls(*args, **kwds)
def set_python_instance_state(self, instance, state):
if hasattr(instance, '__setstate__'):
instance.__setstate__(state)
else:
slotstate = {}
if isinstance(state, tuple) and len(state) == 2:
state, slotstate = state
if hasattr(instance, '__dict__'):
instance.__dict__.update(state)
elif state:
slotstate.update(state)
for key, value in slotstate.items():
setattr(object, key, value)
def construct_python_object(self, suffix, node):
# Format:
# !!python/object:module.name { ... state ... }
instance = self.make_python_instance(suffix, node, newobj=True)
yield instance
deep = hasattr(instance, '__setstate__')
state = self.construct_mapping(node, deep=deep)
self.set_python_instance_state(instance, state)
def construct_python_object_apply(self, suffix, node, newobj=False):
# Format:
# !!python/object/apply # (or !!python/object/new)
# args: [ ... arguments ... ]
# kwds: { ... keywords ... }
# state: ... state ...
# listitems: [ ... listitems ... ]
# dictitems: { ... dictitems ... }
# or short format:
# !!python/object/apply [ ... arguments ... ]
# The difference between !!python/object/apply and !!python/object/new
# is how an object is created, check make_python_instance for details.
if isinstance(node, SequenceNode):
args = self.construct_sequence(node, deep=True)
kwds = {}
state = {}
listitems = []
dictitems = {}
else:
value = self.construct_mapping(node, deep=True)
args = value.get('args', [])
kwds = value.get('kwds', {})
state = value.get('state', {})
listitems = value.get('listitems', [])
dictitems = value.get('dictitems', {})
instance = self.make_python_instance(suffix, node, args, kwds, newobj)
if state:
self.set_python_instance_state(instance, state)
if listitems:
instance.extend(listitems)
if dictitems:
for key in dictitems:
instance[key] = dictitems[key]
return instance
def construct_python_object_new(self, suffix, node):
return self.construct_python_object_apply(suffix, node, newobj=True)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/none',
Constructor.construct_yaml_null)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/bool',
Constructor.construct_yaml_bool)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/str',
Constructor.construct_python_str)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/unicode',
Constructor.construct_python_unicode)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/int',
Constructor.construct_yaml_int)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/long',
Constructor.construct_python_long)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/float',
Constructor.construct_yaml_float)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/complex',
Constructor.construct_python_complex)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/list',
Constructor.construct_yaml_seq)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/tuple',
Constructor.construct_python_tuple)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/dict',
Constructor.construct_yaml_map)
Constructor.add_multi_constructor(
u'tag:yaml.org,2002:python/name:',
Constructor.construct_python_name)
Constructor.add_multi_constructor(
u'tag:yaml.org,2002:python/module:',
Constructor.construct_python_module)
Constructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object:',
Constructor.construct_python_object)
Constructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object/apply:',
Constructor.construct_python_object_apply)
Constructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object/new:',
Constructor.construct_python_object_new)

View file

@ -1,85 +0,0 @@
__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader',
'CBaseDumper', 'CSafeDumper', 'CDumper']
from _yaml import CParser, CEmitter
from constructor import *
from serializer import *
from representer import *
from resolver import *
class CBaseLoader(CParser, BaseConstructor, BaseResolver):
def __init__(self, stream):
CParser.__init__(self, stream)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class CSafeLoader(CParser, SafeConstructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class CLoader(CParser, Constructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
Constructor.__init__(self)
Resolver.__init__(self)
class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class CSafeDumper(CEmitter, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class CDumper(CEmitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)

View file

@ -1,62 +0,0 @@
__all__ = ['BaseDumper', 'SafeDumper', 'Dumper']
from emitter import *
from serializer import *
from representer import *
from resolver import *
class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class Dumper(Emitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)

View file

@ -1,40 +0,0 @@
__all__ = ['BaseLoader', 'SafeLoader', 'Loader']
from reader import *
from scanner import *
from parser import *
from composer import *
from constructor import *
from resolver import *
class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
Constructor.__init__(self)
Resolver.__init__(self)

View file

@ -1,49 +0,0 @@
class Node(object):
def __init__(self, tag, value, start_mark, end_mark):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
value = self.value
#if isinstance(value, list):
# if len(value) == 0:
# value = '<empty>'
# elif len(value) == 1:
# value = '<1 item>'
# else:
# value = '<%d items>' % len(value)
#else:
# if len(value) > 75:
# value = repr(value[:70]+u' ... ')
# else:
# value = repr(value)
value = repr(value)
return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value)
class ScalarNode(Node):
id = 'scalar'
def __init__(self, tag, value,
start_mark=None, end_mark=None, style=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style
class CollectionNode(Node):
def __init__(self, tag, value,
start_mark=None, end_mark=None, flow_style=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.flow_style = flow_style
class SequenceNode(CollectionNode):
id = 'sequence'
class MappingNode(CollectionNode):
id = 'mapping'

View file

@ -1,190 +0,0 @@
# This module contains abstractions for the input stream. You don't have to
# looks further, there are no pretty code.
#
# We define two classes here.
#
# Mark(source, line, column)
# It's just a record and its only use is producing nice error messages.
# Parser does not use it for any other purposes.
#
# Reader(source, data)
# Reader determines the encoding of `data` and converts it to unicode.
# Reader provides the following methods and attributes:
# reader.peek(length=1) - return the next `length` characters
# reader.forward(length=1) - move the current position to `length` characters.
# reader.index - the number of the current character.
# reader.line, stream.column - the line and the column of the current character.
__all__ = ['Reader', 'ReaderError']
from error import YAMLError, Mark
import codecs, re
class ReaderError(YAMLError):
def __init__(self, name, position, character, encoding, reason):
self.name = name
self.character = character
self.position = position
self.encoding = encoding
self.reason = reason
def __str__(self):
if isinstance(self.character, str):
return "'%s' codec can't decode byte #x%02x: %s\n" \
" in \"%s\", position %d" \
% (self.encoding, ord(self.character), self.reason,
self.name, self.position)
else:
return "unacceptable character #x%04x: %s\n" \
" in \"%s\", position %d" \
% (self.character, self.reason,
self.name, self.position)
class Reader(object):
# Reader:
# - determines the data encoding and converts it to unicode,
# - checks if characters are in allowed range,
# - adds '\0' to the end.
# Reader accepts
# - a `str` object,
# - a `unicode` object,
# - a file-like object with its `read` method returning `str`,
# - a file-like object with its `read` method returning `unicode`.
# Yeah, it's ugly and slow.
def __init__(self, stream):
self.name = None
self.stream = None
self.stream_pointer = 0
self.eof = True
self.buffer = u''
self.pointer = 0
self.raw_buffer = None
self.raw_decode = None
self.encoding = None
self.index = 0
self.line = 0
self.column = 0
if isinstance(stream, unicode):
self.name = "<unicode string>"
self.check_printable(stream)
self.buffer = stream+u'\0'
elif isinstance(stream, str):
self.name = "<string>"
self.raw_buffer = stream
self.determine_encoding()
else:
self.stream = stream
self.name = getattr(stream, 'name', "<file>")
self.eof = False
self.raw_buffer = ''
self.determine_encoding()
def peek(self, index=0):
try:
return self.buffer[self.pointer+index]
except IndexError:
self.update(index+1)
return self.buffer[self.pointer+index]
def prefix(self, length=1):
if self.pointer+length >= len(self.buffer):
self.update(length)
return self.buffer[self.pointer:self.pointer+length]
def forward(self, length=1):
if self.pointer+length+1 >= len(self.buffer):
self.update(length+1)
while length:
ch = self.buffer[self.pointer]
self.pointer += 1
self.index += 1
if ch in u'\n\x85\u2028\u2029' \
or (ch == u'\r' and self.buffer[self.pointer] != u'\n'):
self.line += 1
self.column = 0
elif ch != u'\uFEFF':
self.column += 1
length -= 1
def get_mark(self):
if self.stream is None:
return Mark(self.name, self.index, self.line, self.column,
self.buffer, self.pointer)
else:
return Mark(self.name, self.index, self.line, self.column,
None, None)
def determine_encoding(self):
while not self.eof and len(self.raw_buffer) < 2:
self.update_raw()
if not isinstance(self.raw_buffer, unicode):
if self.raw_buffer.startswith(codecs.BOM_UTF16_LE):
self.raw_decode = codecs.utf_16_le_decode
self.encoding = 'utf-16-le'
elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE):
self.raw_decode = codecs.utf_16_be_decode
self.encoding = 'utf-16-be'
else:
self.raw_decode = codecs.utf_8_decode
self.encoding = 'utf-8'
self.update(1)
NON_PRINTABLE = re.compile(u'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]')
def check_printable(self, data):
match = self.NON_PRINTABLE.search(data)
if match:
character = match.group()
position = self.index+(len(self.buffer)-self.pointer)+match.start()
raise ReaderError(self.name, position, ord(character),
'unicode', "special characters are not allowed")
def update(self, length):
if self.raw_buffer is None:
return
self.buffer = self.buffer[self.pointer:]
self.pointer = 0
while len(self.buffer) < length:
if not self.eof:
self.update_raw()
if self.raw_decode is not None:
try:
data, converted = self.raw_decode(self.raw_buffer,
'strict', self.eof)
except UnicodeDecodeError, exc:
character = exc.object[exc.start]
if self.stream is not None:
position = self.stream_pointer-len(self.raw_buffer)+exc.start
else:
position = exc.start
raise ReaderError(self.name, position, character,
exc.encoding, exc.reason)
else:
data = self.raw_buffer
converted = len(data)
self.check_printable(data)
self.buffer += data
self.raw_buffer = self.raw_buffer[converted:]
if self.eof:
self.buffer += u'\0'
self.raw_buffer = None
break
def update_raw(self, size=1024):
data = self.stream.read(size)
if data:
self.raw_buffer += data
self.stream_pointer += len(data)
else:
self.eof = True
#try:
# import psyco
# psyco.bind(Reader)
#except ImportError:
# pass

View file

@ -1,486 +0,0 @@
__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
'RepresenterError']
from error import *
from nodes import *
import datetime
import sys, copy_reg, types
class RepresenterError(YAMLError):
pass
class BaseRepresenter(object):
yaml_representers = {}
yaml_multi_representers = {}
def __init__(self, default_style=None, default_flow_style=None):
self.default_style = default_style
self.default_flow_style = default_flow_style
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def represent(self, data):
node = self.represent_data(data)
self.serialize(node)
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def get_classobj_bases(self, cls):
bases = [cls]
for base in cls.__bases__:
bases.extend(self.get_classobj_bases(base))
return bases
def represent_data(self, data):
if self.ignore_aliases(data):
self.alias_key = None
else:
self.alias_key = id(data)
if self.alias_key is not None:
if self.alias_key in self.represented_objects:
node = self.represented_objects[self.alias_key]
#if node is None:
# raise RepresenterError("recursive objects are not allowed: %r" % data)
return node
#self.represented_objects[alias_key] = None
self.object_keeper.append(data)
data_types = type(data).__mro__
if type(data) is types.InstanceType:
data_types = self.get_classobj_bases(data.__class__)+list(data_types)
if data_types[0] in self.yaml_representers:
node = self.yaml_representers[data_types[0]](self, data)
else:
for data_type in data_types:
if data_type in self.yaml_multi_representers:
node = self.yaml_multi_representers[data_type](self, data)
break
else:
if None in self.yaml_multi_representers:
node = self.yaml_multi_representers[None](self, data)
elif None in self.yaml_representers:
node = self.yaml_representers[None](self, data)
else:
node = ScalarNode(None, unicode(data))
#if alias_key is not None:
# self.represented_objects[alias_key] = node
return node
def add_representer(cls, data_type, representer):
if not 'yaml_representers' in cls.__dict__:
cls.yaml_representers = cls.yaml_representers.copy()
cls.yaml_representers[data_type] = representer
add_representer = classmethod(add_representer)
def add_multi_representer(cls, data_type, representer):
if not 'yaml_multi_representers' in cls.__dict__:
cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
cls.yaml_multi_representers[data_type] = representer
add_multi_representer = classmethod(add_multi_representer)
def represent_scalar(self, tag, value, style=None):
if style is None:
style = self.default_style
node = ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
def represent_sequence(self, tag, sequence, flow_style=None):
value = []
node = SequenceNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
for item in sequence:
node_item = self.represent_data(item)
if not (isinstance(node_item, ScalarNode) and not node_item.style):
best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
node = MappingNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
if hasattr(mapping, 'items'):
mapping = mapping.items()
mapping.sort()
for item_key, item_value in mapping:
node_key = self.represent_data(item_key)
node_value = self.represent_data(item_value)
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def ignore_aliases(self, data):
return False
class SafeRepresenter(BaseRepresenter):
def ignore_aliases(self, data):
if data is None:
return True
if isinstance(data, tuple) and data == ():
return True
if isinstance(data, (str, unicode, bool, int, float)):
return True
def represent_none(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:null',
u'null')
def represent_str(self, data):
tag = None
style = None
try:
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
except UnicodeDecodeError:
try:
data = unicode(data, 'utf-8')
tag = u'tag:yaml.org,2002:str'
except UnicodeDecodeError:
data = data.encode('base64')
tag = u'tag:yaml.org,2002:binary'
style = '|'
return self.represent_scalar(tag, data, style=style)
def represent_unicode(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:str', data)
def represent_bool(self, data):
if data:
value = u'true'
else:
value = u'false'
return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
def represent_int(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
def represent_long(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
inf_value = 1e300
while repr(inf_value) != repr(inf_value*inf_value):
inf_value *= inf_value
def represent_float(self, data):
if data != data or (data == 0.0 and data == 1.0):
value = u'.nan'
elif data == self.inf_value:
value = u'.inf'
elif data == -self.inf_value:
value = u'-.inf'
else:
value = unicode(repr(data)).lower()
# Note that in some cases `repr(data)` represents a float number
# without the decimal parts. For instance:
# >>> repr(1e17)
# '1e17'
# Unfortunately, this is not a valid float representation according
# to the definition of the `!!float` tag. We fix this by adding
# '.0' before the 'e' symbol.
if u'.' not in value and u'e' in value:
value = value.replace(u'e', u'.0e', 1)
return self.represent_scalar(u'tag:yaml.org,2002:float', value)
def represent_list(self, data):
#pairs = (len(data) > 0 and isinstance(data, list))
#if pairs:
# for item in data:
# if not isinstance(item, tuple) or len(item) != 2:
# pairs = False
# break
#if not pairs:
return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
#value = []
#for item_key, item_value in data:
# value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
# [(item_key, item_value)]))
#return SequenceNode(u'tag:yaml.org,2002:pairs', value)
def represent_dict(self, data):
return self.represent_mapping(u'tag:yaml.org,2002:map', data)
def represent_set(self, data):
value = {}
for key in data:
value[key] = None
return self.represent_mapping(u'tag:yaml.org,2002:set', value)
def represent_date(self, data):
value = unicode(data.isoformat())
return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
def represent_datetime(self, data):
value = unicode(data.isoformat(' '))
return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
def represent_yaml_object(self, tag, data, cls, flow_style=None):
if hasattr(data, '__getstate__'):
state = data.__getstate__()
else:
state = data.__dict__.copy()
return self.represent_mapping(tag, state, flow_style=flow_style)
def represent_undefined(self, data):
raise RepresenterError("cannot represent an object: %s" % data)
SafeRepresenter.add_representer(type(None),
SafeRepresenter.represent_none)
SafeRepresenter.add_representer(str,
SafeRepresenter.represent_str)
SafeRepresenter.add_representer(unicode,
SafeRepresenter.represent_unicode)
SafeRepresenter.add_representer(bool,
SafeRepresenter.represent_bool)
SafeRepresenter.add_representer(int,
SafeRepresenter.represent_int)
SafeRepresenter.add_representer(long,
SafeRepresenter.represent_long)
SafeRepresenter.add_representer(float,
SafeRepresenter.represent_float)
SafeRepresenter.add_representer(list,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(tuple,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(dict,
SafeRepresenter.represent_dict)
SafeRepresenter.add_representer(set,
SafeRepresenter.represent_set)
SafeRepresenter.add_representer(datetime.date,
SafeRepresenter.represent_date)
SafeRepresenter.add_representer(datetime.datetime,
SafeRepresenter.represent_datetime)
SafeRepresenter.add_representer(None,
SafeRepresenter.represent_undefined)
class Representer(SafeRepresenter):
def represent_str(self, data):
tag = None
style = None
try:
data = unicode(data, 'ascii')
tag = u'tag:yaml.org,2002:str'
except UnicodeDecodeError:
try:
data = unicode(data, 'utf-8')
tag = u'tag:yaml.org,2002:python/str'
except UnicodeDecodeError:
data = data.encode('base64')
tag = u'tag:yaml.org,2002:binary'
style = '|'
return self.represent_scalar(tag, data, style=style)
def represent_unicode(self, data):
tag = None
try:
data.encode('ascii')
tag = u'tag:yaml.org,2002:python/unicode'
except UnicodeEncodeError:
tag = u'tag:yaml.org,2002:str'
return self.represent_scalar(tag, data)
def represent_long(self, data):
tag = u'tag:yaml.org,2002:int'
if int(data) is not data:
tag = u'tag:yaml.org,2002:python/long'
return self.represent_scalar(tag, unicode(data))
def represent_complex(self, data):
if data.imag == 0.0:
data = u'%r' % data.real
elif data.real == 0.0:
data = u'%rj' % data.imag
elif data.imag > 0:
data = u'%r+%rj' % (data.real, data.imag)
else:
data = u'%r%rj' % (data.real, data.imag)
return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
def represent_tuple(self, data):
return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
def represent_name(self, data):
name = u'%s.%s' % (data.__module__, data.__name__)
return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'')
def represent_module(self, data):
return self.represent_scalar(
u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
def represent_instance(self, data):
# For instances of classic classes, we use __getinitargs__ and
# __getstate__ to serialize the data.
# If data.__getinitargs__ exists, the object must be reconstructed by
# calling cls(**args), where args is a tuple returned by
# __getinitargs__. Otherwise, the cls.__init__ method should never be
# called and the class instance is created by instantiating a trivial
# class and assigning to the instance's __class__ variable.
# If data.__getstate__ exists, it returns the state of the object.
# Otherwise, the state of the object is data.__dict__.
# We produce either a !!python/object or !!python/object/new node.
# If data.__getinitargs__ does not exist and state is a dictionary, we
# produce a !!python/object node . Otherwise we produce a
# !!python/object/new node.
cls = data.__class__
class_name = u'%s.%s' % (cls.__module__, cls.__name__)
args = None
state = None
if hasattr(data, '__getinitargs__'):
args = list(data.__getinitargs__())
if hasattr(data, '__getstate__'):
state = data.__getstate__()
else:
state = data.__dict__
if args is None and isinstance(state, dict):
return self.represent_mapping(
u'tag:yaml.org,2002:python/object:'+class_name, state)
if isinstance(state, dict) and not state:
return self.represent_sequence(
u'tag:yaml.org,2002:python/object/new:'+class_name, args)
value = {}
if args:
value['args'] = args
value['state'] = state
return self.represent_mapping(
u'tag:yaml.org,2002:python/object/new:'+class_name, value)
def represent_object(self, data):
# We use __reduce__ API to save the data. data.__reduce__ returns
# a tuple of length 2-5:
# (function, args, state, listitems, dictitems)
# For reconstructing, we calls function(*args), then set its state,
# listitems, and dictitems if they are not None.
# A special case is when function.__name__ == '__newobj__'. In this
# case we create the object with args[0].__new__(*args).
# Another special case is when __reduce__ returns a string - we don't
# support it.
# We produce a !!python/object, !!python/object/new or
# !!python/object/apply node.
cls = type(data)
if cls in copy_reg.dispatch_table:
reduce = copy_reg.dispatch_table[cls](data)
elif hasattr(data, '__reduce_ex__'):
reduce = data.__reduce_ex__(2)
elif hasattr(data, '__reduce__'):
reduce = data.__reduce__()
else:
raise RepresenterError("cannot represent object: %r" % data)
reduce = (list(reduce)+[None]*5)[:5]
function, args, state, listitems, dictitems = reduce
args = list(args)
if state is None:
state = {}
if listitems is not None:
listitems = list(listitems)
if dictitems is not None:
dictitems = dict(dictitems)
if function.__name__ == '__newobj__':
function = args[0]
args = args[1:]
tag = u'tag:yaml.org,2002:python/object/new:'
newobj = True
else:
tag = u'tag:yaml.org,2002:python/object/apply:'
newobj = False
function_name = u'%s.%s' % (function.__module__, function.__name__)
if not args and not listitems and not dictitems \
and isinstance(state, dict) and newobj:
return self.represent_mapping(
u'tag:yaml.org,2002:python/object:'+function_name, state)
if not listitems and not dictitems \
and isinstance(state, dict) and not state:
return self.represent_sequence(tag+function_name, args)
value = {}
if args:
value['args'] = args
if state or not isinstance(state, dict):
value['state'] = state
if listitems:
value['listitems'] = listitems
if dictitems:
value['dictitems'] = dictitems
return self.represent_mapping(tag+function_name, value)
Representer.add_representer(str,
Representer.represent_str)
Representer.add_representer(unicode,
Representer.represent_unicode)
Representer.add_representer(long,
Representer.represent_long)
Representer.add_representer(complex,
Representer.represent_complex)
Representer.add_representer(tuple,
Representer.represent_tuple)
Representer.add_representer(type,
Representer.represent_name)
Representer.add_representer(types.ClassType,
Representer.represent_name)
Representer.add_representer(types.FunctionType,
Representer.represent_name)
Representer.add_representer(types.BuiltinFunctionType,
Representer.represent_name)
Representer.add_representer(types.ModuleType,
Representer.represent_module)
Representer.add_multi_representer(types.InstanceType,
Representer.represent_instance)
Representer.add_multi_representer(object,
Representer.represent_object)

View file

@ -1,227 +0,0 @@
__all__ = ['BaseResolver', 'Resolver']
from error import *
from nodes import *
import re
class ResolverError(YAMLError):
pass
class BaseResolver(object):
DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'
yaml_implicit_resolvers = {}
yaml_path_resolvers = {}
def __init__(self):
self.resolver_exact_paths = []
self.resolver_prefix_paths = []
def add_implicit_resolver(cls, tag, regexp, first):
if not 'yaml_implicit_resolvers' in cls.__dict__:
implicit_resolvers = {}
for key in cls.yaml_implicit_resolvers:
implicit_resolvers[key] = cls.yaml_implicit_resolvers[key][:]
cls.yaml_implicit_resolvers = implicit_resolvers
if first is None:
first = [None]
for ch in first:
cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
add_implicit_resolver = classmethod(add_implicit_resolver)
def add_path_resolver(cls, tag, path, kind=None):
# Note: `add_path_resolver` is experimental. The API could be changed.
# `new_path` is a pattern that is matched against the path from the
# root to the node that is being considered. `node_path` elements are
# tuples `(node_check, index_check)`. `node_check` is a node class:
# `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None`
# matches any kind of a node. `index_check` could be `None`, a boolean
# value, a string value, or a number. `None` and `False` match against
# any _value_ of sequence and mapping nodes. `True` matches against
# any _key_ of a mapping node. A string `index_check` matches against
# a mapping value that corresponds to a scalar key which content is
# equal to the `index_check` value. An integer `index_check` matches
# against a sequence value with the index equal to `index_check`.
if not 'yaml_path_resolvers' in cls.__dict__:
cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
new_path = []
for element in path:
if isinstance(element, (list, tuple)):
if len(element) == 2:
node_check, index_check = element
elif len(element) == 1:
node_check = element[0]
index_check = True
else:
raise ResolverError("Invalid path element: %s" % element)
else:
node_check = None
index_check = element
if node_check is str:
node_check = ScalarNode
elif node_check is list:
node_check = SequenceNode
elif node_check is dict:
node_check = MappingNode
elif node_check not in [ScalarNode, SequenceNode, MappingNode] \
and not isinstance(node_check, basestring) \
and node_check is not None:
raise ResolverError("Invalid node checker: %s" % node_check)
if not isinstance(index_check, (basestring, int)) \
and index_check is not None:
raise ResolverError("Invalid index checker: %s" % index_check)
new_path.append((node_check, index_check))
if kind is str:
kind = ScalarNode
elif kind is list:
kind = SequenceNode
elif kind is dict:
kind = MappingNode
elif kind not in [ScalarNode, SequenceNode, MappingNode] \
and kind is not None:
raise ResolverError("Invalid node kind: %s" % kind)
cls.yaml_path_resolvers[tuple(new_path), kind] = tag
add_path_resolver = classmethod(add_path_resolver)
def descend_resolver(self, current_node, current_index):
if not self.yaml_path_resolvers:
return
exact_paths = {}
prefix_paths = []
if current_node:
depth = len(self.resolver_prefix_paths)
for path, kind in self.resolver_prefix_paths[-1]:
if self.check_resolver_prefix(depth, path, kind,
current_node, current_index):
if len(path) > depth:
prefix_paths.append((path, kind))
else:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
for path, kind in self.yaml_path_resolvers:
if not path:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
prefix_paths.append((path, kind))
self.resolver_exact_paths.append(exact_paths)
self.resolver_prefix_paths.append(prefix_paths)
def ascend_resolver(self):
if not self.yaml_path_resolvers:
return
self.resolver_exact_paths.pop()
self.resolver_prefix_paths.pop()
def check_resolver_prefix(self, depth, path, kind,
current_node, current_index):
node_check, index_check = path[depth-1]
if isinstance(node_check, basestring):
if current_node.tag != node_check:
return
elif node_check is not None:
if not isinstance(current_node, node_check):
return
if index_check is True and current_index is not None:
return
if (index_check is False or index_check is None) \
and current_index is None:
return
if isinstance(index_check, basestring):
if not (isinstance(current_index, ScalarNode)
and index_check == current_index.value):
return
elif isinstance(index_check, int) and not isinstance(index_check, bool):
if index_check != current_index:
return
return True
def resolve(self, kind, value, implicit):
if kind is ScalarNode and implicit[0]:
if value == u'':
resolvers = self.yaml_implicit_resolvers.get(u'', [])
else:
resolvers = self.yaml_implicit_resolvers.get(value[0], [])
resolvers += self.yaml_implicit_resolvers.get(None, [])
for tag, regexp in resolvers:
if regexp.match(value):
return tag
implicit = implicit[1]
if self.yaml_path_resolvers:
exact_paths = self.resolver_exact_paths[-1]
if kind in exact_paths:
return exact_paths[kind]
if None in exact_paths:
return exact_paths[None]
if kind is ScalarNode:
return self.DEFAULT_SCALAR_TAG
elif kind is SequenceNode:
return self.DEFAULT_SEQUENCE_TAG
elif kind is MappingNode:
return self.DEFAULT_MAPPING_TAG
class Resolver(BaseResolver):
pass
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:bool',
re.compile(ur'''^(?:yes|Yes|YES|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF)$''', re.X),
list(u'yYnNtTfFoO'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:float',
re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)?
|\.[0-9_]+(?:[eE][-+][0-9]+)?
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
|[-+]?\.(?:inf|Inf|INF)
|\.(?:nan|NaN|NAN))$''', re.X),
list(u'-+0123456789.'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:int',
re.compile(ur'''^(?:[-+]?0b[0-1_]+
|[-+]?0[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list(u'-+0123456789'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:merge',
re.compile(ur'^(?:<<)$'),
[u'<'])
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:null',
re.compile(ur'''^(?: ~
|null|Null|NULL
| )$''', re.X),
[u'~', u'n', u'N', u''])
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:timestamp',
re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
(?:[Tt]|[ \t]+)[0-9][0-9]?
:[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
list(u'0123456789'))
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:value',
re.compile(ur'^(?:=)$'),
[u'='])
# The following resolver is only for documentation purposes. It cannot work
# because plain scalars cannot start with '!', '&', or '*'.
Resolver.add_implicit_resolver(
u'tag:yaml.org,2002:yaml',
re.compile(ur'^(?:!|&|\*)$'),
list(u'!&*'))

View file

@ -1,111 +0,0 @@
__all__ = ['Serializer', 'SerializerError']
from error import YAMLError
from events import *
from nodes import *
class SerializerError(YAMLError):
pass
class Serializer(object):
ANCHOR_TEMPLATE = u'id%03d'
def __init__(self, encoding=None,
explicit_start=None, explicit_end=None, version=None, tags=None):
self.use_encoding = encoding
self.use_explicit_start = explicit_start
self.use_explicit_end = explicit_end
self.use_version = version
self.use_tags = tags
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
self.closed = None
def open(self):
if self.closed is None:
self.emit(StreamStartEvent(encoding=self.use_encoding))
self.closed = False
elif self.closed:
raise SerializerError("serializer is closed")
else:
raise SerializerError("serializer is already opened")
def close(self):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif not self.closed:
self.emit(StreamEndEvent())
self.closed = True
#def __del__(self):
# self.close()
def serialize(self, node):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif self.closed:
raise SerializerError("serializer is closed")
self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
version=self.use_version, tags=self.use_tags))
self.anchor_node(node)
self.serialize_node(node, None, None)
self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
def anchor_node(self, node):
if node in self.anchors:
if self.anchors[node] is None:
self.anchors[node] = self.generate_anchor(node)
else:
self.anchors[node] = None
if isinstance(node, SequenceNode):
for item in node.value:
self.anchor_node(item)
elif isinstance(node, MappingNode):
for key, value in node.value:
self.anchor_node(key)
self.anchor_node(value)
def generate_anchor(self, node):
self.last_anchor_id += 1
return self.ANCHOR_TEMPLATE % self.last_anchor_id
def serialize_node(self, node, parent, index):
alias = self.anchors[node]
if node in self.serialized_nodes:
self.emit(AliasEvent(alias))
else:
self.serialized_nodes[node] = True
self.descend_resolver(parent, index)
if isinstance(node, ScalarNode):
detected_tag = self.resolve(ScalarNode, node.value, (True, False))
default_tag = self.resolve(ScalarNode, node.value, (False, True))
implicit = (node.tag == detected_tag), (node.tag == default_tag)
self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
style=node.style))
elif isinstance(node, SequenceNode):
implicit = (node.tag
== self.resolve(SequenceNode, node.value, True))
self.emit(SequenceStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style))
index = 0
for item in node.value:
self.serialize_node(item, node, index)
index += 1
self.emit(SequenceEndEvent())
elif isinstance(node, MappingNode):
implicit = (node.tag
== self.resolve(MappingNode, node.value, True))
self.emit(MappingStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style))
for key, value in node.value:
self.serialize_node(key, node, None)
self.serialize_node(value, node, key)
self.emit(MappingEndEvent())
self.ascend_resolver()

View file

@ -1,104 +0,0 @@
class Token(object):
def __init__(self, start_mark, end_mark):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
attributes = [key for key in self.__dict__
if not key.endswith('_mark')]
attributes.sort()
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
#class BOMToken(Token):
# id = '<byte order mark>'
class DirectiveToken(Token):
id = '<directive>'
def __init__(self, name, value, start_mark, end_mark):
self.name = name
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class DocumentStartToken(Token):
id = '<document start>'
class DocumentEndToken(Token):
id = '<document end>'
class StreamStartToken(Token):
id = '<stream start>'
def __init__(self, start_mark=None, end_mark=None,
encoding=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.encoding = encoding
class StreamEndToken(Token):
id = '<stream end>'
class BlockSequenceStartToken(Token):
id = '<block sequence start>'
class BlockMappingStartToken(Token):
id = '<block mapping start>'
class BlockEndToken(Token):
id = '<block end>'
class FlowSequenceStartToken(Token):
id = '['
class FlowMappingStartToken(Token):
id = '{'
class FlowSequenceEndToken(Token):
id = ']'
class FlowMappingEndToken(Token):
id = '}'
class KeyToken(Token):
id = '?'
class ValueToken(Token):
id = ':'
class BlockEntryToken(Token):
id = '-'
class FlowEntryToken(Token):
id = ','
class AliasToken(Token):
id = '<alias>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class AnchorToken(Token):
id = '<anchor>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class TagToken(Token):
id = '<tag>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class ScalarToken(Token):
id = '<scalar>'
def __init__(self, value, plain, start_mark, end_mark, style=None):
self.value = value
self.plain = plain
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style

View file

@ -1,139 +0,0 @@
__all__ = ['Composer', 'ComposerError']
from .error import MarkedYAMLError
from .events import *
from .nodes import *
class ComposerError(MarkedYAMLError):
pass
class Composer:
def __init__(self):
self.anchors = {}
def check_node(self):
# Drop the STREAM-START event.
if self.check_event(StreamStartEvent):
self.get_event()
# If there are more documents available?
return not self.check_event(StreamEndEvent)
def get_node(self):
# Get the root node of the next document.
if not self.check_event(StreamEndEvent):
return self.compose_document()
def get_single_node(self):
# Drop the STREAM-START event.
self.get_event()
# Compose a document if the stream is not empty.
document = None
if not self.check_event(StreamEndEvent):
document = self.compose_document()
# Ensure that the stream contains no more documents.
if not self.check_event(StreamEndEvent):
event = self.get_event()
raise ComposerError("expected a single document in the stream",
document.start_mark, "but found another document",
event.start_mark)
# Drop the STREAM-END event.
self.get_event()
return document
def compose_document(self):
# Drop the DOCUMENT-START event.
self.get_event()
# Compose the root node.
node = self.compose_node(None, None)
# Drop the DOCUMENT-END event.
self.get_event()
self.anchors = {}
return node
def compose_node(self, parent, index):
if self.check_event(AliasEvent):
event = self.get_event()
anchor = event.anchor
if anchor not in self.anchors:
raise ComposerError(None, None, "found undefined alias %r"
% anchor, event.start_mark)
return self.anchors[anchor]
event = self.peek_event()
anchor = event.anchor
if anchor is not None:
if anchor in self.anchors:
raise ComposerError("found duplicate anchor %r; first occurence"
% anchor, self.anchors[anchor].start_mark,
"second occurence", event.start_mark)
self.descend_resolver(parent, index)
if self.check_event(ScalarEvent):
node = self.compose_scalar_node(anchor)
elif self.check_event(SequenceStartEvent):
node = self.compose_sequence_node(anchor)
elif self.check_event(MappingStartEvent):
node = self.compose_mapping_node(anchor)
self.ascend_resolver()
return node
def compose_scalar_node(self, anchor):
event = self.get_event()
tag = event.tag
if tag is None or tag == '!':
tag = self.resolve(ScalarNode, event.value, event.implicit)
node = ScalarNode(tag, event.value,
event.start_mark, event.end_mark, style=event.style)
if anchor is not None:
self.anchors[anchor] = node
return node
def compose_sequence_node(self, anchor):
start_event = self.get_event()
tag = start_event.tag
if tag is None or tag == '!':
tag = self.resolve(SequenceNode, None, start_event.implicit)
node = SequenceNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
index = 0
while not self.check_event(SequenceEndEvent):
node.value.append(self.compose_node(node, index))
index += 1
end_event = self.get_event()
node.end_mark = end_event.end_mark
return node
def compose_mapping_node(self, anchor):
start_event = self.get_event()
tag = start_event.tag
if tag is None or tag == '!':
tag = self.resolve(MappingNode, None, start_event.implicit)
node = MappingNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
while not self.check_event(MappingEndEvent):
#key_event = self.peek_event()
item_key = self.compose_node(node, None)
#if item_key in node.value:
# raise ComposerError("while composing a mapping", start_event.start_mark,
# "found duplicate key", key_event.start_mark)
item_value = self.compose_node(node, item_key)
#node.value[item_key] = item_value
node.value.append((item_key, item_value))
end_event = self.get_event()
node.end_mark = end_event.end_mark
return node

View file

@ -1,686 +0,0 @@
__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
'ConstructorError']
from .error import *
from .nodes import *
import collections, datetime, base64, binascii, re, sys, types
class ConstructorError(MarkedYAMLError):
pass
class BaseConstructor:
yaml_constructors = {}
yaml_multi_constructors = {}
def __init__(self):
self.constructed_objects = {}
self.recursive_objects = {}
self.state_generators = []
self.deep_construct = False
def check_data(self):
# If there are more documents available?
return self.check_node()
def get_data(self):
# Construct and return the next document.
if self.check_node():
return self.construct_document(self.get_node())
def get_single_data(self):
# Ensure that the stream contains a single document and construct it.
node = self.get_single_node()
if node is not None:
return self.construct_document(node)
return None
def construct_document(self, node):
data = self.construct_object(node)
while self.state_generators:
state_generators = self.state_generators
self.state_generators = []
for generator in state_generators:
for dummy in generator:
pass
self.constructed_objects = {}
self.recursive_objects = {}
self.deep_construct = False
return data
def construct_object(self, node, deep=False):
if node in self.constructed_objects:
return self.constructed_objects[node]
if deep:
old_deep = self.deep_construct
self.deep_construct = True
if node in self.recursive_objects:
raise ConstructorError(None, None,
"found unconstructable recursive node", node.start_mark)
self.recursive_objects[node] = None
constructor = None
tag_suffix = None
if node.tag in self.yaml_constructors:
constructor = self.yaml_constructors[node.tag]
else:
for tag_prefix in self.yaml_multi_constructors:
if node.tag.startswith(tag_prefix):
tag_suffix = node.tag[len(tag_prefix):]
constructor = self.yaml_multi_constructors[tag_prefix]
break
else:
if None in self.yaml_multi_constructors:
tag_suffix = node.tag
constructor = self.yaml_multi_constructors[None]
elif None in self.yaml_constructors:
constructor = self.yaml_constructors[None]
elif isinstance(node, ScalarNode):
constructor = self.__class__.construct_scalar
elif isinstance(node, SequenceNode):
constructor = self.__class__.construct_sequence
elif isinstance(node, MappingNode):
constructor = self.__class__.construct_mapping
if tag_suffix is None:
data = constructor(self, node)
else:
data = constructor(self, tag_suffix, node)
if isinstance(data, types.GeneratorType):
generator = data
data = next(generator)
if self.deep_construct:
for dummy in generator:
pass
else:
self.state_generators.append(generator)
self.constructed_objects[node] = data
del self.recursive_objects[node]
if deep:
self.deep_construct = old_deep
return data
def construct_scalar(self, node):
if not isinstance(node, ScalarNode):
raise ConstructorError(None, None,
"expected a scalar node, but found %s" % node.id,
node.start_mark)
return node.value
def construct_sequence(self, node, deep=False):
if not isinstance(node, SequenceNode):
raise ConstructorError(None, None,
"expected a sequence node, but found %s" % node.id,
node.start_mark)
return [self.construct_object(child, deep=deep)
for child in node.value]
def construct_mapping(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.Hashable):
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unhashable key", key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
def construct_pairs(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
pairs = []
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
value = self.construct_object(value_node, deep=deep)
pairs.append((key, value))
return pairs
@classmethod
def add_constructor(cls, tag, constructor):
if not 'yaml_constructors' in cls.__dict__:
cls.yaml_constructors = cls.yaml_constructors.copy()
cls.yaml_constructors[tag] = constructor
@classmethod
def add_multi_constructor(cls, tag_prefix, multi_constructor):
if not 'yaml_multi_constructors' in cls.__dict__:
cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
cls.yaml_multi_constructors[tag_prefix] = multi_constructor
class SafeConstructor(BaseConstructor):
def construct_scalar(self, node):
if isinstance(node, MappingNode):
for key_node, value_node in node.value:
if key_node.tag == 'tag:yaml.org,2002:value':
return self.construct_scalar(value_node)
return super().construct_scalar(node)
def flatten_mapping(self, node):
merge = []
index = 0
while index < len(node.value):
key_node, value_node = node.value[index]
if key_node.tag == 'tag:yaml.org,2002:merge':
del node.value[index]
if isinstance(value_node, MappingNode):
self.flatten_mapping(value_node)
merge.extend(value_node.value)
elif isinstance(value_node, SequenceNode):
submerge = []
for subnode in value_node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing a mapping",
node.start_mark,
"expected a mapping for merging, but found %s"
% subnode.id, subnode.start_mark)
self.flatten_mapping(subnode)
submerge.append(subnode.value)
submerge.reverse()
for value in submerge:
merge.extend(value)
else:
raise ConstructorError("while constructing a mapping", node.start_mark,
"expected a mapping or list of mappings for merging, but found %s"
% value_node.id, value_node.start_mark)
elif key_node.tag == 'tag:yaml.org,2002:value':
key_node.tag = 'tag:yaml.org,2002:str'
index += 1
else:
index += 1
if merge:
node.value = merge + node.value
def construct_mapping(self, node, deep=False):
if isinstance(node, MappingNode):
self.flatten_mapping(node)
return super().construct_mapping(node, deep=deep)
def construct_yaml_null(self, node):
self.construct_scalar(node)
return None
bool_values = {
'yes': True,
'no': False,
'true': True,
'false': False,
'on': True,
'off': False,
}
def construct_yaml_bool(self, node):
value = self.construct_scalar(node)
return self.bool_values[value.lower()]
def construct_yaml_int(self, node):
value = self.construct_scalar(node)
value = value.replace('_', '')
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '0':
return 0
elif value.startswith('0b'):
return sign*int(value[2:], 2)
elif value.startswith('0x'):
return sign*int(value[2:], 16)
elif value[0] == '0':
return sign*int(value, 8)
elif ':' in value:
digits = [int(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0
for digit in digits:
value += digit*base
base *= 60
return sign*value
else:
return sign*int(value)
inf_value = 1e300
while inf_value != inf_value*inf_value:
inf_value *= inf_value
nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99).
def construct_yaml_float(self, node):
value = self.construct_scalar(node)
value = value.replace('_', '').lower()
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '.inf':
return sign*self.inf_value
elif value == '.nan':
return self.nan_value
elif ':' in value:
digits = [float(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0.0
for digit in digits:
value += digit*base
base *= 60
return sign*value
else:
return sign*float(value)
def construct_yaml_binary(self, node):
try:
value = self.construct_scalar(node).encode('ascii')
except UnicodeEncodeError as exc:
raise ConstructorError(None, None,
"failed to convert base64 data into ascii: %s" % exc,
node.start_mark)
try:
if hasattr(base64, 'decodebytes'):
return base64.decodebytes(value)
else:
return base64.decodestring(value)
except binascii.Error as exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
timestamp_regexp = re.compile(
r'''^(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
def construct_yaml_timestamp(self, node):
value = self.construct_scalar(node)
match = self.timestamp_regexp.match(node.value)
values = match.groupdict()
year = int(values['year'])
month = int(values['month'])
day = int(values['day'])
if not values['hour']:
return datetime.date(year, month, day)
hour = int(values['hour'])
minute = int(values['minute'])
second = int(values['second'])
fraction = 0
if values['fraction']:
fraction = values['fraction'][:6]
while len(fraction) < 6:
fraction += '0'
fraction = int(fraction)
delta = None
if values['tz_sign']:
tz_hour = int(values['tz_hour'])
tz_minute = int(values['tz_minute'] or 0)
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
if values['tz_sign'] == '-':
delta = -delta
data = datetime.datetime(year, month, day, hour, minute, second, fraction)
if delta:
data -= delta
return data
def construct_yaml_omap(self, node):
# Note: we do not check for duplicate keys, because it's too
# CPU-expensive.
omap = []
yield omap
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
omap.append((key, value))
def construct_yaml_pairs(self, node):
# Note: the same code as `construct_yaml_omap`.
pairs = []
yield pairs
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
pairs.append((key, value))
def construct_yaml_set(self, node):
data = set()
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_yaml_str(self, node):
return self.construct_scalar(node)
def construct_yaml_seq(self, node):
data = []
yield data
data.extend(self.construct_sequence(node))
def construct_yaml_map(self, node):
data = {}
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_yaml_object(self, node, cls):
data = cls.__new__(cls)
yield data
if hasattr(data, '__setstate__'):
state = self.construct_mapping(node, deep=True)
data.__setstate__(state)
else:
state = self.construct_mapping(node)
data.__dict__.update(state)
def construct_undefined(self, node):
raise ConstructorError(None, None,
"could not determine a constructor for the tag %r" % node.tag,
node.start_mark)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:null',
SafeConstructor.construct_yaml_null)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:bool',
SafeConstructor.construct_yaml_bool)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:int',
SafeConstructor.construct_yaml_int)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:float',
SafeConstructor.construct_yaml_float)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:binary',
SafeConstructor.construct_yaml_binary)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:timestamp',
SafeConstructor.construct_yaml_timestamp)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:omap',
SafeConstructor.construct_yaml_omap)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:pairs',
SafeConstructor.construct_yaml_pairs)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:set',
SafeConstructor.construct_yaml_set)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:str',
SafeConstructor.construct_yaml_str)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:seq',
SafeConstructor.construct_yaml_seq)
SafeConstructor.add_constructor(
'tag:yaml.org,2002:map',
SafeConstructor.construct_yaml_map)
SafeConstructor.add_constructor(None,
SafeConstructor.construct_undefined)
class Constructor(SafeConstructor):
def construct_python_str(self, node):
return self.construct_scalar(node)
def construct_python_unicode(self, node):
return self.construct_scalar(node)
def construct_python_bytes(self, node):
try:
value = self.construct_scalar(node).encode('ascii')
except UnicodeEncodeError as exc:
raise ConstructorError(None, None,
"failed to convert base64 data into ascii: %s" % exc,
node.start_mark)
try:
if hasattr(base64, 'decodebytes'):
return base64.decodebytes(value)
else:
return base64.decodestring(value)
except binascii.Error as exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
def construct_python_long(self, node):
return self.construct_yaml_int(node)
def construct_python_complex(self, node):
return complex(self.construct_scalar(node))
def construct_python_tuple(self, node):
return tuple(self.construct_sequence(node))
def find_python_module(self, name, mark):
if not name:
raise ConstructorError("while constructing a Python module", mark,
"expected non-empty name appended to the tag", mark)
try:
__import__(name)
except ImportError as exc:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name, exc), mark)
return sys.modules[name]
def find_python_name(self, name, mark):
if not name:
raise ConstructorError("while constructing a Python object", mark,
"expected non-empty name appended to the tag", mark)
if '.' in name:
module_name, object_name = name.rsplit('.', 1)
else:
module_name = 'builtins'
object_name = name
try:
__import__(module_name)
except ImportError as exc:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name, exc), mark)
module = sys.modules[module_name]
if not hasattr(module, object_name):
raise ConstructorError("while constructing a Python object", mark,
"cannot find %r in the module %r"
% (object_name, module.__name__), mark)
return getattr(module, object_name)
def construct_python_name(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python name", node.start_mark,
"expected the empty value, but found %r" % value, node.start_mark)
return self.find_python_name(suffix, node.start_mark)
def construct_python_module(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python module", node.start_mark,
"expected the empty value, but found %r" % value, node.start_mark)
return self.find_python_module(suffix, node.start_mark)
def make_python_instance(self, suffix, node,
args=None, kwds=None, newobj=False):
if not args:
args = []
if not kwds:
kwds = {}
cls = self.find_python_name(suffix, node.start_mark)
if newobj and isinstance(cls, type):
return cls.__new__(cls, *args, **kwds)
else:
return cls(*args, **kwds)
def set_python_instance_state(self, instance, state):
if hasattr(instance, '__setstate__'):
instance.__setstate__(state)
else:
slotstate = {}
if isinstance(state, tuple) and len(state) == 2:
state, slotstate = state
if hasattr(instance, '__dict__'):
instance.__dict__.update(state)
elif state:
slotstate.update(state)
for key, value in slotstate.items():
setattr(object, key, value)
def construct_python_object(self, suffix, node):
# Format:
# !!python/object:module.name { ... state ... }
instance = self.make_python_instance(suffix, node, newobj=True)
yield instance
deep = hasattr(instance, '__setstate__')
state = self.construct_mapping(node, deep=deep)
self.set_python_instance_state(instance, state)
def construct_python_object_apply(self, suffix, node, newobj=False):
# Format:
# !!python/object/apply # (or !!python/object/new)
# args: [ ... arguments ... ]
# kwds: { ... keywords ... }
# state: ... state ...
# listitems: [ ... listitems ... ]
# dictitems: { ... dictitems ... }
# or short format:
# !!python/object/apply [ ... arguments ... ]
# The difference between !!python/object/apply and !!python/object/new
# is how an object is created, check make_python_instance for details.
if isinstance(node, SequenceNode):
args = self.construct_sequence(node, deep=True)
kwds = {}
state = {}
listitems = []
dictitems = {}
else:
value = self.construct_mapping(node, deep=True)
args = value.get('args', [])
kwds = value.get('kwds', {})
state = value.get('state', {})
listitems = value.get('listitems', [])
dictitems = value.get('dictitems', {})
instance = self.make_python_instance(suffix, node, args, kwds, newobj)
if state:
self.set_python_instance_state(instance, state)
if listitems:
instance.extend(listitems)
if dictitems:
for key in dictitems:
instance[key] = dictitems[key]
return instance
def construct_python_object_new(self, suffix, node):
return self.construct_python_object_apply(suffix, node, newobj=True)
Constructor.add_constructor(
'tag:yaml.org,2002:python/none',
Constructor.construct_yaml_null)
Constructor.add_constructor(
'tag:yaml.org,2002:python/bool',
Constructor.construct_yaml_bool)
Constructor.add_constructor(
'tag:yaml.org,2002:python/str',
Constructor.construct_python_str)
Constructor.add_constructor(
'tag:yaml.org,2002:python/unicode',
Constructor.construct_python_unicode)
Constructor.add_constructor(
'tag:yaml.org,2002:python/bytes',
Constructor.construct_python_bytes)
Constructor.add_constructor(
'tag:yaml.org,2002:python/int',
Constructor.construct_yaml_int)
Constructor.add_constructor(
'tag:yaml.org,2002:python/long',
Constructor.construct_python_long)
Constructor.add_constructor(
'tag:yaml.org,2002:python/float',
Constructor.construct_yaml_float)
Constructor.add_constructor(
'tag:yaml.org,2002:python/complex',
Constructor.construct_python_complex)
Constructor.add_constructor(
'tag:yaml.org,2002:python/list',
Constructor.construct_yaml_seq)
Constructor.add_constructor(
'tag:yaml.org,2002:python/tuple',
Constructor.construct_python_tuple)
Constructor.add_constructor(
'tag:yaml.org,2002:python/dict',
Constructor.construct_yaml_map)
Constructor.add_multi_constructor(
'tag:yaml.org,2002:python/name:',
Constructor.construct_python_name)
Constructor.add_multi_constructor(
'tag:yaml.org,2002:python/module:',
Constructor.construct_python_module)
Constructor.add_multi_constructor(
'tag:yaml.org,2002:python/object:',
Constructor.construct_python_object)
Constructor.add_multi_constructor(
'tag:yaml.org,2002:python/object/apply:',
Constructor.construct_python_object_apply)
Constructor.add_multi_constructor(
'tag:yaml.org,2002:python/object/new:',
Constructor.construct_python_object_new)

View file

@ -1,85 +0,0 @@
__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader',
'CBaseDumper', 'CSafeDumper', 'CDumper']
from _yaml import CParser, CEmitter
from .constructor import *
from .serializer import *
from .representer import *
from .resolver import *
class CBaseLoader(CParser, BaseConstructor, BaseResolver):
def __init__(self, stream):
CParser.__init__(self, stream)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class CSafeLoader(CParser, SafeConstructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class CLoader(CParser, Constructor, Resolver):
def __init__(self, stream):
CParser.__init__(self, stream)
Constructor.__init__(self)
Resolver.__init__(self)
class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class CSafeDumper(CEmitter, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class CDumper(CEmitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
CEmitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width, encoding=encoding,
allow_unicode=allow_unicode, line_break=line_break,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)

View file

@ -1,62 +0,0 @@
__all__ = ['BaseDumper', 'SafeDumper', 'Dumper']
from .emitter import *
from .serializer import *
from .representer import *
from .resolver import *
class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
SafeRepresenter.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)
class Dumper(Emitter, Serializer, Representer, Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
Emitter.__init__(self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode, line_break=line_break)
Serializer.__init__(self, encoding=encoding,
explicit_start=explicit_start, explicit_end=explicit_end,
version=version, tags=tags)
Representer.__init__(self, default_style=default_style,
default_flow_style=default_flow_style)
Resolver.__init__(self)

File diff suppressed because it is too large Load diff

View file

@ -1,75 +0,0 @@
__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError']
class Mark:
def __init__(self, name, index, line, column, buffer, pointer):
self.name = name
self.index = index
self.line = line
self.column = column
self.buffer = buffer
self.pointer = pointer
def get_snippet(self, indent=4, max_length=75):
if self.buffer is None:
return None
head = ''
start = self.pointer
while start > 0 and self.buffer[start-1] not in '\0\r\n\x85\u2028\u2029':
start -= 1
if self.pointer-start > max_length/2-1:
head = ' ... '
start += 5
break
tail = ''
end = self.pointer
while end < len(self.buffer) and self.buffer[end] not in '\0\r\n\x85\u2028\u2029':
end += 1
if end-self.pointer > max_length/2-1:
tail = ' ... '
end -= 5
break
snippet = self.buffer[start:end]
return ' '*indent + head + snippet + tail + '\n' \
+ ' '*(indent+self.pointer-start+len(head)) + '^'
def __str__(self):
snippet = self.get_snippet()
where = " in \"%s\", line %d, column %d" \
% (self.name, self.line+1, self.column+1)
if snippet is not None:
where += ":\n"+snippet
return where
class YAMLError(Exception):
pass
class MarkedYAMLError(YAMLError):
def __init__(self, context=None, context_mark=None,
problem=None, problem_mark=None, note=None):
self.context = context
self.context_mark = context_mark
self.problem = problem
self.problem_mark = problem_mark
self.note = note
def __str__(self):
lines = []
if self.context is not None:
lines.append(self.context)
if self.context_mark is not None \
and (self.problem is None or self.problem_mark is None
or self.context_mark.name != self.problem_mark.name
or self.context_mark.line != self.problem_mark.line
or self.context_mark.column != self.problem_mark.column):
lines.append(str(self.context_mark))
if self.problem is not None:
lines.append(self.problem)
if self.problem_mark is not None:
lines.append(str(self.problem_mark))
if self.note is not None:
lines.append(self.note)
return '\n'.join(lines)

View file

@ -1,86 +0,0 @@
# Abstract classes.
class Event(object):
def __init__(self, start_mark=None, end_mark=None):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
attributes = [key for key in ['anchor', 'tag', 'implicit', 'value']
if hasattr(self, key)]
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
class NodeEvent(Event):
def __init__(self, anchor, start_mark=None, end_mark=None):
self.anchor = anchor
self.start_mark = start_mark
self.end_mark = end_mark
class CollectionStartEvent(NodeEvent):
def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None,
flow_style=None):
self.anchor = anchor
self.tag = tag
self.implicit = implicit
self.start_mark = start_mark
self.end_mark = end_mark
self.flow_style = flow_style
class CollectionEndEvent(Event):
pass
# Implementations.
class StreamStartEvent(Event):
def __init__(self, start_mark=None, end_mark=None, encoding=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.encoding = encoding
class StreamEndEvent(Event):
pass
class DocumentStartEvent(Event):
def __init__(self, start_mark=None, end_mark=None,
explicit=None, version=None, tags=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.explicit = explicit
self.version = version
self.tags = tags
class DocumentEndEvent(Event):
def __init__(self, start_mark=None, end_mark=None,
explicit=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.explicit = explicit
class AliasEvent(NodeEvent):
pass
class ScalarEvent(NodeEvent):
def __init__(self, anchor, tag, implicit, value,
start_mark=None, end_mark=None, style=None):
self.anchor = anchor
self.tag = tag
self.implicit = implicit
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style
class SequenceStartEvent(CollectionStartEvent):
pass
class SequenceEndEvent(CollectionEndEvent):
pass
class MappingStartEvent(CollectionStartEvent):
pass
class MappingEndEvent(CollectionEndEvent):
pass

View file

@ -1,40 +0,0 @@
__all__ = ['BaseLoader', 'SafeLoader', 'Loader']
from .reader import *
from .scanner import *
from .parser import *
from .composer import *
from .constructor import *
from .resolver import *
class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
BaseConstructor.__init__(self)
BaseResolver.__init__(self)
class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
SafeConstructor.__init__(self)
Resolver.__init__(self)
class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
def __init__(self, stream):
Reader.__init__(self, stream)
Scanner.__init__(self)
Parser.__init__(self)
Composer.__init__(self)
Constructor.__init__(self)
Resolver.__init__(self)

View file

@ -1,49 +0,0 @@
class Node(object):
def __init__(self, tag, value, start_mark, end_mark):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
value = self.value
#if isinstance(value, list):
# if len(value) == 0:
# value = '<empty>'
# elif len(value) == 1:
# value = '<1 item>'
# else:
# value = '<%d items>' % len(value)
#else:
# if len(value) > 75:
# value = repr(value[:70]+u' ... ')
# else:
# value = repr(value)
value = repr(value)
return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value)
class ScalarNode(Node):
id = 'scalar'
def __init__(self, tag, value,
start_mark=None, end_mark=None, style=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style
class CollectionNode(Node):
def __init__(self, tag, value,
start_mark=None, end_mark=None, flow_style=None):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
self.flow_style = flow_style
class SequenceNode(CollectionNode):
id = 'sequence'
class MappingNode(CollectionNode):
id = 'mapping'

View file

@ -1,589 +0,0 @@
# The following YAML grammar is LL(1) and is parsed by a recursive descent
# parser.
#
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
# implicit_document ::= block_node DOCUMENT-END*
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
# block_node_or_indentless_sequence ::=
# ALIAS
# | properties (block_content | indentless_block_sequence)?
# | block_content
# | indentless_block_sequence
# block_node ::= ALIAS
# | properties block_content?
# | block_content
# flow_node ::= ALIAS
# | properties flow_content?
# | flow_content
# properties ::= TAG ANCHOR? | ANCHOR TAG?
# block_content ::= block_collection | flow_collection | SCALAR
# flow_content ::= flow_collection | SCALAR
# block_collection ::= block_sequence | block_mapping
# flow_collection ::= flow_sequence | flow_mapping
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+
# block_mapping ::= BLOCK-MAPPING_START
# ((KEY block_node_or_indentless_sequence?)?
# (VALUE block_node_or_indentless_sequence?)?)*
# BLOCK-END
# flow_sequence ::= FLOW-SEQUENCE-START
# (flow_sequence_entry FLOW-ENTRY)*
# flow_sequence_entry?
# FLOW-SEQUENCE-END
# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
# flow_mapping ::= FLOW-MAPPING-START
# (flow_mapping_entry FLOW-ENTRY)*
# flow_mapping_entry?
# FLOW-MAPPING-END
# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
#
# FIRST sets:
#
# stream: { STREAM-START }
# explicit_document: { DIRECTIVE DOCUMENT-START }
# implicit_document: FIRST(block_node)
# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START }
# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START }
# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START }
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
# block_sequence: { BLOCK-SEQUENCE-START }
# block_mapping: { BLOCK-MAPPING-START }
# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY }
# indentless_sequence: { ENTRY }
# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
# flow_sequence: { FLOW-SEQUENCE-START }
# flow_mapping: { FLOW-MAPPING-START }
# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
__all__ = ['Parser', 'ParserError']
from .error import MarkedYAMLError
from .tokens import *
from .events import *
from .scanner import *
class ParserError(MarkedYAMLError):
pass
class Parser:
# Since writing a recursive-descendant parser is a straightforward task, we
# do not give many comments here.
DEFAULT_TAGS = {
'!': '!',
'!!': 'tag:yaml.org,2002:',
}
def __init__(self):
self.current_event = None
self.yaml_version = None
self.tag_handles = {}
self.states = []
self.marks = []
self.state = self.parse_stream_start
def dispose(self):
# Reset the state attributes (to clear self-references)
self.states = []
self.state = None
def check_event(self, *choices):
# Check the type of the next event.
if self.current_event is None:
if self.state:
self.current_event = self.state()
if self.current_event is not None:
if not choices:
return True
for choice in choices:
if isinstance(self.current_event, choice):
return True
return False
def peek_event(self):
# Get the next event.
if self.current_event is None:
if self.state:
self.current_event = self.state()
return self.current_event
def get_event(self):
# Get the next event and proceed further.
if self.current_event is None:
if self.state:
self.current_event = self.state()
value = self.current_event
self.current_event = None
return value
# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
# implicit_document ::= block_node DOCUMENT-END*
# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
def parse_stream_start(self):
# Parse the stream start.
token = self.get_token()
event = StreamStartEvent(token.start_mark, token.end_mark,
encoding=token.encoding)
# Prepare the next state.
self.state = self.parse_implicit_document_start
return event
def parse_implicit_document_start(self):
# Parse an implicit document.
if not self.check_token(DirectiveToken, DocumentStartToken,
StreamEndToken):
self.tag_handles = self.DEFAULT_TAGS
token = self.peek_token()
start_mark = end_mark = token.start_mark
event = DocumentStartEvent(start_mark, end_mark,
explicit=False)
# Prepare the next state.
self.states.append(self.parse_document_end)
self.state = self.parse_block_node
return event
else:
return self.parse_document_start()
def parse_document_start(self):
# Parse any extra document end indicators.
while self.check_token(DocumentEndToken):
self.get_token()
# Parse an explicit document.
if not self.check_token(StreamEndToken):
token = self.peek_token()
start_mark = token.start_mark
version, tags = self.process_directives()
if not self.check_token(DocumentStartToken):
raise ParserError(None, None,
"expected '<document start>', but found %r"
% self.peek_token().id,
self.peek_token().start_mark)
token = self.get_token()
end_mark = token.end_mark
event = DocumentStartEvent(start_mark, end_mark,
explicit=True, version=version, tags=tags)
self.states.append(self.parse_document_end)
self.state = self.parse_document_content
else:
# Parse the end of the stream.
token = self.get_token()
event = StreamEndEvent(token.start_mark, token.end_mark)
assert not self.states
assert not self.marks
self.state = None
return event
def parse_document_end(self):
# Parse the document end.
token = self.peek_token()
start_mark = end_mark = token.start_mark
explicit = False
if self.check_token(DocumentEndToken):
token = self.get_token()
end_mark = token.end_mark
explicit = True
event = DocumentEndEvent(start_mark, end_mark,
explicit=explicit)
# Prepare the next state.
self.state = self.parse_document_start
return event
def parse_document_content(self):
if self.check_token(DirectiveToken,
DocumentStartToken, DocumentEndToken, StreamEndToken):
event = self.process_empty_scalar(self.peek_token().start_mark)
self.state = self.states.pop()
return event
else:
return self.parse_block_node()
def process_directives(self):
self.yaml_version = None
self.tag_handles = {}
while self.check_token(DirectiveToken):
token = self.get_token()
if token.name == 'YAML':
if self.yaml_version is not None:
raise ParserError(None, None,
"found duplicate YAML directive", token.start_mark)
major, minor = token.value
if major != 1:
raise ParserError(None, None,
"found incompatible YAML document (version 1.* is required)",
token.start_mark)
self.yaml_version = token.value
elif token.name == 'TAG':
handle, prefix = token.value
if handle in self.tag_handles:
raise ParserError(None, None,
"duplicate tag handle %r" % handle,
token.start_mark)
self.tag_handles[handle] = prefix
if self.tag_handles:
value = self.yaml_version, self.tag_handles.copy()
else:
value = self.yaml_version, None
for key in self.DEFAULT_TAGS:
if key not in self.tag_handles:
self.tag_handles[key] = self.DEFAULT_TAGS[key]
return value
# block_node_or_indentless_sequence ::= ALIAS
# | properties (block_content | indentless_block_sequence)?
# | block_content
# | indentless_block_sequence
# block_node ::= ALIAS
# | properties block_content?
# | block_content
# flow_node ::= ALIAS
# | properties flow_content?
# | flow_content
# properties ::= TAG ANCHOR? | ANCHOR TAG?
# block_content ::= block_collection | flow_collection | SCALAR
# flow_content ::= flow_collection | SCALAR
# block_collection ::= block_sequence | block_mapping
# flow_collection ::= flow_sequence | flow_mapping
def parse_block_node(self):
return self.parse_node(block=True)
def parse_flow_node(self):
return self.parse_node()
def parse_block_node_or_indentless_sequence(self):
return self.parse_node(block=True, indentless_sequence=True)
def parse_node(self, block=False, indentless_sequence=False):
if self.check_token(AliasToken):
token = self.get_token()
event = AliasEvent(token.value, token.start_mark, token.end_mark)
self.state = self.states.pop()
else:
anchor = None
tag = None
start_mark = end_mark = tag_mark = None
if self.check_token(AnchorToken):
token = self.get_token()
start_mark = token.start_mark
end_mark = token.end_mark
anchor = token.value
if self.check_token(TagToken):
token = self.get_token()
tag_mark = token.start_mark
end_mark = token.end_mark
tag = token.value
elif self.check_token(TagToken):
token = self.get_token()
start_mark = tag_mark = token.start_mark
end_mark = token.end_mark
tag = token.value
if self.check_token(AnchorToken):
token = self.get_token()
end_mark = token.end_mark
anchor = token.value
if tag is not None:
handle, suffix = tag
if handle is not None:
if handle not in self.tag_handles:
raise ParserError("while parsing a node", start_mark,
"found undefined tag handle %r" % handle,
tag_mark)
tag = self.tag_handles[handle]+suffix
else:
tag = suffix
#if tag == '!':
# raise ParserError("while parsing a node", start_mark,
# "found non-specific tag '!'", tag_mark,
# "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.")
if start_mark is None:
start_mark = end_mark = self.peek_token().start_mark
event = None
implicit = (tag is None or tag == '!')
if indentless_sequence and self.check_token(BlockEntryToken):
end_mark = self.peek_token().end_mark
event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark)
self.state = self.parse_indentless_sequence_entry
else:
if self.check_token(ScalarToken):
token = self.get_token()
end_mark = token.end_mark
if (token.plain and tag is None) or tag == '!':
implicit = (True, False)
elif tag is None:
implicit = (False, True)
else:
implicit = (False, False)
event = ScalarEvent(anchor, tag, implicit, token.value,
start_mark, end_mark, style=token.style)
self.state = self.states.pop()
elif self.check_token(FlowSequenceStartToken):
end_mark = self.peek_token().end_mark
event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=True)
self.state = self.parse_flow_sequence_first_entry
elif self.check_token(FlowMappingStartToken):
end_mark = self.peek_token().end_mark
event = MappingStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=True)
self.state = self.parse_flow_mapping_first_key
elif block and self.check_token(BlockSequenceStartToken):
end_mark = self.peek_token().start_mark
event = SequenceStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=False)
self.state = self.parse_block_sequence_first_entry
elif block and self.check_token(BlockMappingStartToken):
end_mark = self.peek_token().start_mark
event = MappingStartEvent(anchor, tag, implicit,
start_mark, end_mark, flow_style=False)
self.state = self.parse_block_mapping_first_key
elif anchor is not None or tag is not None:
# Empty scalars are allowed even if a tag or an anchor is
# specified.
event = ScalarEvent(anchor, tag, (implicit, False), '',
start_mark, end_mark)
self.state = self.states.pop()
else:
if block:
node = 'block'
else:
node = 'flow'
token = self.peek_token()
raise ParserError("while parsing a %s node" % node, start_mark,
"expected the node content, but found %r" % token.id,
token.start_mark)
return event
# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
def parse_block_sequence_first_entry(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_block_sequence_entry()
def parse_block_sequence_entry(self):
if self.check_token(BlockEntryToken):
token = self.get_token()
if not self.check_token(BlockEntryToken, BlockEndToken):
self.states.append(self.parse_block_sequence_entry)
return self.parse_block_node()
else:
self.state = self.parse_block_sequence_entry
return self.process_empty_scalar(token.end_mark)
if not self.check_token(BlockEndToken):
token = self.peek_token()
raise ParserError("while parsing a block collection", self.marks[-1],
"expected <block end>, but found %r" % token.id, token.start_mark)
token = self.get_token()
event = SequenceEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
# indentless_sequence ::= (BLOCK-ENTRY block_node?)+
def parse_indentless_sequence_entry(self):
if self.check_token(BlockEntryToken):
token = self.get_token()
if not self.check_token(BlockEntryToken,
KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_indentless_sequence_entry)
return self.parse_block_node()
else:
self.state = self.parse_indentless_sequence_entry
return self.process_empty_scalar(token.end_mark)
token = self.peek_token()
event = SequenceEndEvent(token.start_mark, token.start_mark)
self.state = self.states.pop()
return event
# block_mapping ::= BLOCK-MAPPING_START
# ((KEY block_node_or_indentless_sequence?)?
# (VALUE block_node_or_indentless_sequence?)?)*
# BLOCK-END
def parse_block_mapping_first_key(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_block_mapping_key()
def parse_block_mapping_key(self):
if self.check_token(KeyToken):
token = self.get_token()
if not self.check_token(KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_block_mapping_value)
return self.parse_block_node_or_indentless_sequence()
else:
self.state = self.parse_block_mapping_value
return self.process_empty_scalar(token.end_mark)
if not self.check_token(BlockEndToken):
token = self.peek_token()
raise ParserError("while parsing a block mapping", self.marks[-1],
"expected <block end>, but found %r" % token.id, token.start_mark)
token = self.get_token()
event = MappingEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
def parse_block_mapping_value(self):
if self.check_token(ValueToken):
token = self.get_token()
if not self.check_token(KeyToken, ValueToken, BlockEndToken):
self.states.append(self.parse_block_mapping_key)
return self.parse_block_node_or_indentless_sequence()
else:
self.state = self.parse_block_mapping_key
return self.process_empty_scalar(token.end_mark)
else:
self.state = self.parse_block_mapping_key
token = self.peek_token()
return self.process_empty_scalar(token.start_mark)
# flow_sequence ::= FLOW-SEQUENCE-START
# (flow_sequence_entry FLOW-ENTRY)*
# flow_sequence_entry?
# FLOW-SEQUENCE-END
# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
#
# Note that while production rules for both flow_sequence_entry and
# flow_mapping_entry are equal, their interpretations are different.
# For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?`
# generate an inline mapping (set syntax).
def parse_flow_sequence_first_entry(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_flow_sequence_entry(first=True)
def parse_flow_sequence_entry(self, first=False):
if not self.check_token(FlowSequenceEndToken):
if not first:
if self.check_token(FlowEntryToken):
self.get_token()
else:
token = self.peek_token()
raise ParserError("while parsing a flow sequence", self.marks[-1],
"expected ',' or ']', but got %r" % token.id, token.start_mark)
if self.check_token(KeyToken):
token = self.peek_token()
event = MappingStartEvent(None, None, True,
token.start_mark, token.end_mark,
flow_style=True)
self.state = self.parse_flow_sequence_entry_mapping_key
return event
elif not self.check_token(FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry)
return self.parse_flow_node()
token = self.get_token()
event = SequenceEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
def parse_flow_sequence_entry_mapping_key(self):
token = self.get_token()
if not self.check_token(ValueToken,
FlowEntryToken, FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry_mapping_value)
return self.parse_flow_node()
else:
self.state = self.parse_flow_sequence_entry_mapping_value
return self.process_empty_scalar(token.end_mark)
def parse_flow_sequence_entry_mapping_value(self):
if self.check_token(ValueToken):
token = self.get_token()
if not self.check_token(FlowEntryToken, FlowSequenceEndToken):
self.states.append(self.parse_flow_sequence_entry_mapping_end)
return self.parse_flow_node()
else:
self.state = self.parse_flow_sequence_entry_mapping_end
return self.process_empty_scalar(token.end_mark)
else:
self.state = self.parse_flow_sequence_entry_mapping_end
token = self.peek_token()
return self.process_empty_scalar(token.start_mark)
def parse_flow_sequence_entry_mapping_end(self):
self.state = self.parse_flow_sequence_entry
token = self.peek_token()
return MappingEndEvent(token.start_mark, token.start_mark)
# flow_mapping ::= FLOW-MAPPING-START
# (flow_mapping_entry FLOW-ENTRY)*
# flow_mapping_entry?
# FLOW-MAPPING-END
# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
def parse_flow_mapping_first_key(self):
token = self.get_token()
self.marks.append(token.start_mark)
return self.parse_flow_mapping_key(first=True)
def parse_flow_mapping_key(self, first=False):
if not self.check_token(FlowMappingEndToken):
if not first:
if self.check_token(FlowEntryToken):
self.get_token()
else:
token = self.peek_token()
raise ParserError("while parsing a flow mapping", self.marks[-1],
"expected ',' or '}', but got %r" % token.id, token.start_mark)
if self.check_token(KeyToken):
token = self.get_token()
if not self.check_token(ValueToken,
FlowEntryToken, FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_value)
return self.parse_flow_node()
else:
self.state = self.parse_flow_mapping_value
return self.process_empty_scalar(token.end_mark)
elif not self.check_token(FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_empty_value)
return self.parse_flow_node()
token = self.get_token()
event = MappingEndEvent(token.start_mark, token.end_mark)
self.state = self.states.pop()
self.marks.pop()
return event
def parse_flow_mapping_value(self):
if self.check_token(ValueToken):
token = self.get_token()
if not self.check_token(FlowEntryToken, FlowMappingEndToken):
self.states.append(self.parse_flow_mapping_key)
return self.parse_flow_node()
else:
self.state = self.parse_flow_mapping_key
return self.process_empty_scalar(token.end_mark)
else:
self.state = self.parse_flow_mapping_key
token = self.peek_token()
return self.process_empty_scalar(token.start_mark)
def parse_flow_mapping_empty_value(self):
self.state = self.parse_flow_mapping_key
return self.process_empty_scalar(self.peek_token().start_mark)
def process_empty_scalar(self, mark):
return ScalarEvent(None, None, (True, False), '', mark, mark)

View file

@ -1,387 +0,0 @@
__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
'RepresenterError']
from .error import *
from .nodes import *
import datetime, sys, copyreg, types, base64, collections
class RepresenterError(YAMLError):
pass
class BaseRepresenter:
yaml_representers = {}
yaml_multi_representers = {}
def __init__(self, default_style=None, default_flow_style=None):
self.default_style = default_style
self.default_flow_style = default_flow_style
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def represent(self, data):
node = self.represent_data(data)
self.serialize(node)
self.represented_objects = {}
self.object_keeper = []
self.alias_key = None
def represent_data(self, data):
if self.ignore_aliases(data):
self.alias_key = None
else:
self.alias_key = id(data)
if self.alias_key is not None:
if self.alias_key in self.represented_objects:
node = self.represented_objects[self.alias_key]
#if node is None:
# raise RepresenterError("recursive objects are not allowed: %r" % data)
return node
#self.represented_objects[alias_key] = None
self.object_keeper.append(data)
data_types = type(data).__mro__
if data_types[0] in self.yaml_representers:
node = self.yaml_representers[data_types[0]](self, data)
else:
for data_type in data_types:
if data_type in self.yaml_multi_representers:
node = self.yaml_multi_representers[data_type](self, data)
break
else:
if None in self.yaml_multi_representers:
node = self.yaml_multi_representers[None](self, data)
elif None in self.yaml_representers:
node = self.yaml_representers[None](self, data)
else:
node = ScalarNode(None, str(data))
#if alias_key is not None:
# self.represented_objects[alias_key] = node
return node
@classmethod
def add_representer(cls, data_type, representer):
if not 'yaml_representers' in cls.__dict__:
cls.yaml_representers = cls.yaml_representers.copy()
cls.yaml_representers[data_type] = representer
@classmethod
def add_multi_representer(cls, data_type, representer):
if not 'yaml_multi_representers' in cls.__dict__:
cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
cls.yaml_multi_representers[data_type] = representer
def represent_scalar(self, tag, value, style=None):
if style is None:
style = self.default_style
node = ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
def represent_sequence(self, tag, sequence, flow_style=None):
value = []
node = SequenceNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
for item in sequence:
node_item = self.represent_data(item)
if not (isinstance(node_item, ScalarNode) and not node_item.style):
best_style = False
value.append(node_item)
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
node = MappingNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
if hasattr(mapping, 'items'):
mapping = list(mapping.items())
try:
mapping = sorted(mapping)
except TypeError:
pass
for item_key, item_value in mapping:
node_key = self.represent_data(item_key)
node_value = self.represent_data(item_value)
if not (isinstance(node_key, ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, ScalarNode) and not node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
def ignore_aliases(self, data):
return False
class SafeRepresenter(BaseRepresenter):
def ignore_aliases(self, data):
if data is None:
return True
if isinstance(data, tuple) and data == ():
return True
if isinstance(data, (str, bytes, bool, int, float)):
return True
def represent_none(self, data):
return self.represent_scalar('tag:yaml.org,2002:null', 'null')
def represent_str(self, data):
return self.represent_scalar('tag:yaml.org,2002:str', data)
def represent_binary(self, data):
if hasattr(base64, 'encodebytes'):
data = base64.encodebytes(data).decode('ascii')
else:
data = base64.encodestring(data).decode('ascii')
return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|')
def represent_bool(self, data):
if data:
value = 'true'
else:
value = 'false'
return self.represent_scalar('tag:yaml.org,2002:bool', value)
def represent_int(self, data):
return self.represent_scalar('tag:yaml.org,2002:int', str(data))
inf_value = 1e300
while repr(inf_value) != repr(inf_value*inf_value):
inf_value *= inf_value
def represent_float(self, data):
if data != data or (data == 0.0 and data == 1.0):
value = '.nan'
elif data == self.inf_value:
value = '.inf'
elif data == -self.inf_value:
value = '-.inf'
else:
value = repr(data).lower()
# Note that in some cases `repr(data)` represents a float number
# without the decimal parts. For instance:
# >>> repr(1e17)
# '1e17'
# Unfortunately, this is not a valid float representation according
# to the definition of the `!!float` tag. We fix this by adding
# '.0' before the 'e' symbol.
if '.' not in value and 'e' in value:
value = value.replace('e', '.0e', 1)
return self.represent_scalar('tag:yaml.org,2002:float', value)
def represent_list(self, data):
#pairs = (len(data) > 0 and isinstance(data, list))
#if pairs:
# for item in data:
# if not isinstance(item, tuple) or len(item) != 2:
# pairs = False
# break
#if not pairs:
return self.represent_sequence('tag:yaml.org,2002:seq', data)
#value = []
#for item_key, item_value in data:
# value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
# [(item_key, item_value)]))
#return SequenceNode(u'tag:yaml.org,2002:pairs', value)
def represent_dict(self, data):
return self.represent_mapping('tag:yaml.org,2002:map', data)
def represent_set(self, data):
value = {}
for key in data:
value[key] = None
return self.represent_mapping('tag:yaml.org,2002:set', value)
def represent_date(self, data):
value = data.isoformat()
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
def represent_datetime(self, data):
value = data.isoformat(' ')
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
def represent_yaml_object(self, tag, data, cls, flow_style=None):
if hasattr(data, '__getstate__'):
state = data.__getstate__()
else:
state = data.__dict__.copy()
return self.represent_mapping(tag, state, flow_style=flow_style)
def represent_undefined(self, data):
raise RepresenterError("cannot represent an object: %s" % data)
SafeRepresenter.add_representer(type(None),
SafeRepresenter.represent_none)
SafeRepresenter.add_representer(str,
SafeRepresenter.represent_str)
SafeRepresenter.add_representer(bytes,
SafeRepresenter.represent_binary)
SafeRepresenter.add_representer(bool,
SafeRepresenter.represent_bool)
SafeRepresenter.add_representer(int,
SafeRepresenter.represent_int)
SafeRepresenter.add_representer(float,
SafeRepresenter.represent_float)
SafeRepresenter.add_representer(list,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(tuple,
SafeRepresenter.represent_list)
SafeRepresenter.add_representer(dict,
SafeRepresenter.represent_dict)
SafeRepresenter.add_representer(set,
SafeRepresenter.represent_set)
SafeRepresenter.add_representer(datetime.date,
SafeRepresenter.represent_date)
SafeRepresenter.add_representer(datetime.datetime,
SafeRepresenter.represent_datetime)
SafeRepresenter.add_representer(None,
SafeRepresenter.represent_undefined)
class Representer(SafeRepresenter):
def represent_complex(self, data):
if data.imag == 0.0:
data = '%r' % data.real
elif data.real == 0.0:
data = '%rj' % data.imag
elif data.imag > 0:
data = '%r+%rj' % (data.real, data.imag)
else:
data = '%r%rj' % (data.real, data.imag)
return self.represent_scalar('tag:yaml.org,2002:python/complex', data)
def represent_tuple(self, data):
return self.represent_sequence('tag:yaml.org,2002:python/tuple', data)
def represent_name(self, data):
name = '%s.%s' % (data.__module__, data.__name__)
return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '')
def represent_module(self, data):
return self.represent_scalar(
'tag:yaml.org,2002:python/module:'+data.__name__, '')
def represent_object(self, data):
# We use __reduce__ API to save the data. data.__reduce__ returns
# a tuple of length 2-5:
# (function, args, state, listitems, dictitems)
# For reconstructing, we calls function(*args), then set its state,
# listitems, and dictitems if they are not None.
# A special case is when function.__name__ == '__newobj__'. In this
# case we create the object with args[0].__new__(*args).
# Another special case is when __reduce__ returns a string - we don't
# support it.
# We produce a !!python/object, !!python/object/new or
# !!python/object/apply node.
cls = type(data)
if cls in copyreg.dispatch_table:
reduce = copyreg.dispatch_table[cls](data)
elif hasattr(data, '__reduce_ex__'):
reduce = data.__reduce_ex__(2)
elif hasattr(data, '__reduce__'):
reduce = data.__reduce__()
else:
raise RepresenterError("cannot represent object: %r" % data)
reduce = (list(reduce)+[None]*5)[:5]
function, args, state, listitems, dictitems = reduce
args = list(args)
if state is None:
state = {}
if listitems is not None:
listitems = list(listitems)
if dictitems is not None:
dictitems = dict(dictitems)
if function.__name__ == '__newobj__':
function = args[0]
args = args[1:]
tag = 'tag:yaml.org,2002:python/object/new:'
newobj = True
else:
tag = 'tag:yaml.org,2002:python/object/apply:'
newobj = False
function_name = '%s.%s' % (function.__module__, function.__name__)
if not args and not listitems and not dictitems \
and isinstance(state, dict) and newobj:
return self.represent_mapping(
'tag:yaml.org,2002:python/object:'+function_name, state)
if not listitems and not dictitems \
and isinstance(state, dict) and not state:
return self.represent_sequence(tag+function_name, args)
value = {}
if args:
value['args'] = args
if state or not isinstance(state, dict):
value['state'] = state
if listitems:
value['listitems'] = listitems
if dictitems:
value['dictitems'] = dictitems
return self.represent_mapping(tag+function_name, value)
def represent_ordered_dict(self, data):
# Provide uniform representation across different Python versions.
data_type = type(data)
tag = 'tag:yaml.org,2002:python/object/apply:%s.%s' \
% (data_type.__module__, data_type.__name__)
items = [[key, value] for key, value in data.items()]
return self.represent_sequence(tag, [items])
Representer.add_representer(complex,
Representer.represent_complex)
Representer.add_representer(tuple,
Representer.represent_tuple)
Representer.add_representer(type,
Representer.represent_name)
Representer.add_representer(collections.OrderedDict,
Representer.represent_ordered_dict)
Representer.add_representer(types.FunctionType,
Representer.represent_name)
Representer.add_representer(types.BuiltinFunctionType,
Representer.represent_name)
Representer.add_representer(types.ModuleType,
Representer.represent_module)
Representer.add_multi_representer(object,
Representer.represent_object)

View file

@ -1,227 +0,0 @@
__all__ = ['BaseResolver', 'Resolver']
from .error import *
from .nodes import *
import re
class ResolverError(YAMLError):
pass
class BaseResolver:
DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str'
DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq'
DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map'
yaml_implicit_resolvers = {}
yaml_path_resolvers = {}
def __init__(self):
self.resolver_exact_paths = []
self.resolver_prefix_paths = []
@classmethod
def add_implicit_resolver(cls, tag, regexp, first):
if not 'yaml_implicit_resolvers' in cls.__dict__:
implicit_resolvers = {}
for key in cls.yaml_implicit_resolvers:
implicit_resolvers[key] = cls.yaml_implicit_resolvers[key][:]
cls.yaml_implicit_resolvers = implicit_resolvers
if first is None:
first = [None]
for ch in first:
cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
@classmethod
def add_path_resolver(cls, tag, path, kind=None):
# Note: `add_path_resolver` is experimental. The API could be changed.
# `new_path` is a pattern that is matched against the path from the
# root to the node that is being considered. `node_path` elements are
# tuples `(node_check, index_check)`. `node_check` is a node class:
# `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None`
# matches any kind of a node. `index_check` could be `None`, a boolean
# value, a string value, or a number. `None` and `False` match against
# any _value_ of sequence and mapping nodes. `True` matches against
# any _key_ of a mapping node. A string `index_check` matches against
# a mapping value that corresponds to a scalar key which content is
# equal to the `index_check` value. An integer `index_check` matches
# against a sequence value with the index equal to `index_check`.
if not 'yaml_path_resolvers' in cls.__dict__:
cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
new_path = []
for element in path:
if isinstance(element, (list, tuple)):
if len(element) == 2:
node_check, index_check = element
elif len(element) == 1:
node_check = element[0]
index_check = True
else:
raise ResolverError("Invalid path element: %s" % element)
else:
node_check = None
index_check = element
if node_check is str:
node_check = ScalarNode
elif node_check is list:
node_check = SequenceNode
elif node_check is dict:
node_check = MappingNode
elif node_check not in [ScalarNode, SequenceNode, MappingNode] \
and not isinstance(node_check, str) \
and node_check is not None:
raise ResolverError("Invalid node checker: %s" % node_check)
if not isinstance(index_check, (str, int)) \
and index_check is not None:
raise ResolverError("Invalid index checker: %s" % index_check)
new_path.append((node_check, index_check))
if kind is str:
kind = ScalarNode
elif kind is list:
kind = SequenceNode
elif kind is dict:
kind = MappingNode
elif kind not in [ScalarNode, SequenceNode, MappingNode] \
and kind is not None:
raise ResolverError("Invalid node kind: %s" % kind)
cls.yaml_path_resolvers[tuple(new_path), kind] = tag
def descend_resolver(self, current_node, current_index):
if not self.yaml_path_resolvers:
return
exact_paths = {}
prefix_paths = []
if current_node:
depth = len(self.resolver_prefix_paths)
for path, kind in self.resolver_prefix_paths[-1]:
if self.check_resolver_prefix(depth, path, kind,
current_node, current_index):
if len(path) > depth:
prefix_paths.append((path, kind))
else:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
for path, kind in self.yaml_path_resolvers:
if not path:
exact_paths[kind] = self.yaml_path_resolvers[path, kind]
else:
prefix_paths.append((path, kind))
self.resolver_exact_paths.append(exact_paths)
self.resolver_prefix_paths.append(prefix_paths)
def ascend_resolver(self):
if not self.yaml_path_resolvers:
return
self.resolver_exact_paths.pop()
self.resolver_prefix_paths.pop()
def check_resolver_prefix(self, depth, path, kind,
current_node, current_index):
node_check, index_check = path[depth-1]
if isinstance(node_check, str):
if current_node.tag != node_check:
return
elif node_check is not None:
if not isinstance(current_node, node_check):
return
if index_check is True and current_index is not None:
return
if (index_check is False or index_check is None) \
and current_index is None:
return
if isinstance(index_check, str):
if not (isinstance(current_index, ScalarNode)
and index_check == current_index.value):
return
elif isinstance(index_check, int) and not isinstance(index_check, bool):
if index_check != current_index:
return
return True
def resolve(self, kind, value, implicit):
if kind is ScalarNode and implicit[0]:
if value == '':
resolvers = self.yaml_implicit_resolvers.get('', [])
else:
resolvers = self.yaml_implicit_resolvers.get(value[0], [])
resolvers += self.yaml_implicit_resolvers.get(None, [])
for tag, regexp in resolvers:
if regexp.match(value):
return tag
implicit = implicit[1]
if self.yaml_path_resolvers:
exact_paths = self.resolver_exact_paths[-1]
if kind in exact_paths:
return exact_paths[kind]
if None in exact_paths:
return exact_paths[None]
if kind is ScalarNode:
return self.DEFAULT_SCALAR_TAG
elif kind is SequenceNode:
return self.DEFAULT_SEQUENCE_TAG
elif kind is MappingNode:
return self.DEFAULT_MAPPING_TAG
class Resolver(BaseResolver):
pass
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:bool',
re.compile(r'''^(?:yes|Yes|YES|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF)$''', re.X),
list('yYnNtTfFoO'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:float',
re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)?
|\.[0-9_]+(?:[eE][-+][0-9]+)?
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
|[-+]?\.(?:inf|Inf|INF)
|\.(?:nan|NaN|NAN))$''', re.X),
list('-+0123456789.'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:int',
re.compile(r'''^(?:[-+]?0b[0-1_]+
|[-+]?0[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list('-+0123456789'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:merge',
re.compile(r'^(?:<<)$'),
['<'])
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:null',
re.compile(r'''^(?: ~
|null|Null|NULL
| )$''', re.X),
['~', 'n', 'N', ''])
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:timestamp',
re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
|[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
(?:[Tt]|[ \t]+)[0-9][0-9]?
:[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
list('0123456789'))
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:value',
re.compile(r'^(?:=)$'),
['='])
# The following resolver is only for documentation purposes. It cannot work
# because plain scalars cannot start with '!', '&', or '*'.
Resolver.add_implicit_resolver(
'tag:yaml.org,2002:yaml',
re.compile(r'^(?:!|&|\*)$'),
list('!&*'))

File diff suppressed because it is too large Load diff

View file

@ -1,111 +0,0 @@
__all__ = ['Serializer', 'SerializerError']
from .error import YAMLError
from .events import *
from .nodes import *
class SerializerError(YAMLError):
pass
class Serializer:
ANCHOR_TEMPLATE = 'id%03d'
def __init__(self, encoding=None,
explicit_start=None, explicit_end=None, version=None, tags=None):
self.use_encoding = encoding
self.use_explicit_start = explicit_start
self.use_explicit_end = explicit_end
self.use_version = version
self.use_tags = tags
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
self.closed = None
def open(self):
if self.closed is None:
self.emit(StreamStartEvent(encoding=self.use_encoding))
self.closed = False
elif self.closed:
raise SerializerError("serializer is closed")
else:
raise SerializerError("serializer is already opened")
def close(self):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif not self.closed:
self.emit(StreamEndEvent())
self.closed = True
#def __del__(self):
# self.close()
def serialize(self, node):
if self.closed is None:
raise SerializerError("serializer is not opened")
elif self.closed:
raise SerializerError("serializer is closed")
self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
version=self.use_version, tags=self.use_tags))
self.anchor_node(node)
self.serialize_node(node, None, None)
self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
self.serialized_nodes = {}
self.anchors = {}
self.last_anchor_id = 0
def anchor_node(self, node):
if node in self.anchors:
if self.anchors[node] is None:
self.anchors[node] = self.generate_anchor(node)
else:
self.anchors[node] = None
if isinstance(node, SequenceNode):
for item in node.value:
self.anchor_node(item)
elif isinstance(node, MappingNode):
for key, value in node.value:
self.anchor_node(key)
self.anchor_node(value)
def generate_anchor(self, node):
self.last_anchor_id += 1
return self.ANCHOR_TEMPLATE % self.last_anchor_id
def serialize_node(self, node, parent, index):
alias = self.anchors[node]
if node in self.serialized_nodes:
self.emit(AliasEvent(alias))
else:
self.serialized_nodes[node] = True
self.descend_resolver(parent, index)
if isinstance(node, ScalarNode):
detected_tag = self.resolve(ScalarNode, node.value, (True, False))
default_tag = self.resolve(ScalarNode, node.value, (False, True))
implicit = (node.tag == detected_tag), (node.tag == default_tag)
self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
style=node.style))
elif isinstance(node, SequenceNode):
implicit = (node.tag
== self.resolve(SequenceNode, node.value, True))
self.emit(SequenceStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style))
index = 0
for item in node.value:
self.serialize_node(item, node, index)
index += 1
self.emit(SequenceEndEvent())
elif isinstance(node, MappingNode):
implicit = (node.tag
== self.resolve(MappingNode, node.value, True))
self.emit(MappingStartEvent(alias, node.tag, implicit,
flow_style=node.flow_style))
for key, value in node.value:
self.serialize_node(key, node, None)
self.serialize_node(value, node, key)
self.emit(MappingEndEvent())
self.ascend_resolver()

View file

@ -1,104 +0,0 @@
class Token(object):
def __init__(self, start_mark, end_mark):
self.start_mark = start_mark
self.end_mark = end_mark
def __repr__(self):
attributes = [key for key in self.__dict__
if not key.endswith('_mark')]
attributes.sort()
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
for key in attributes])
return '%s(%s)' % (self.__class__.__name__, arguments)
#class BOMToken(Token):
# id = '<byte order mark>'
class DirectiveToken(Token):
id = '<directive>'
def __init__(self, name, value, start_mark, end_mark):
self.name = name
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class DocumentStartToken(Token):
id = '<document start>'
class DocumentEndToken(Token):
id = '<document end>'
class StreamStartToken(Token):
id = '<stream start>'
def __init__(self, start_mark=None, end_mark=None,
encoding=None):
self.start_mark = start_mark
self.end_mark = end_mark
self.encoding = encoding
class StreamEndToken(Token):
id = '<stream end>'
class BlockSequenceStartToken(Token):
id = '<block sequence start>'
class BlockMappingStartToken(Token):
id = '<block mapping start>'
class BlockEndToken(Token):
id = '<block end>'
class FlowSequenceStartToken(Token):
id = '['
class FlowMappingStartToken(Token):
id = '{'
class FlowSequenceEndToken(Token):
id = ']'
class FlowMappingEndToken(Token):
id = '}'
class KeyToken(Token):
id = '?'
class ValueToken(Token):
id = ':'
class BlockEntryToken(Token):
id = '-'
class FlowEntryToken(Token):
id = ','
class AliasToken(Token):
id = '<alias>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class AnchorToken(Token):
id = '<anchor>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class TagToken(Token):
id = '<tag>'
def __init__(self, value, start_mark, end_mark):
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
class ScalarToken(Token):
id = '<scalar>'
def __init__(self, value, plain, start_mark, end_mark, style=None):
self.value = value
self.plain = plain
self.start_mark = start_mark
self.end_mark = end_mark
self.style = style

View file

@ -31,7 +31,7 @@
import hashlib import hashlib
from contextlib import closing from contextlib import closing
import yaml import ruamel.yaml as yaml
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp, install_tree, get_filetype from llnl.util.filesystem import mkdirp, install_tree, get_filetype

View file

@ -61,8 +61,8 @@
from six import iteritems from six import iteritems
from ordereddict_backport import OrderedDict from ordereddict_backport import OrderedDict
import yaml import ruamel.yaml as yaml
from yaml.error import MarkedYAMLError from ruamel.yaml.error import MarkedYAMLError
import llnl.util.lang import llnl.util.lang
import llnl.util.tty as tty import llnl.util.tty as tty

View file

@ -48,7 +48,7 @@
from six import string_types from six import string_types
from six import iteritems from six import iteritems
from yaml.error import MarkedYAMLError, YAMLError from ruamel.yaml.error import MarkedYAMLError, YAMLError
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp from llnl.util.filesystem import mkdirp

View file

@ -26,9 +26,10 @@
import shutil import shutil
import glob import glob
import tempfile import tempfile
import yaml
import re import re
import ruamel.yaml as yaml
from llnl.util.filesystem import mkdirp from llnl.util.filesystem import mkdirp
import spack.config import spack.config

View file

@ -31,7 +31,7 @@
import spack.error import spack.error
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
from yaml.error import MarkedYAMLError from ruamel.yaml.error import MarkedYAMLError
class ProviderIndex(object): class ProviderIndex(object):

View file

@ -42,7 +42,7 @@
from types import ModuleType from types import ModuleType
import yaml import ruamel.yaml as yaml
import llnl.util.lang import llnl.util.lang
import llnl.util.tty as tty import llnl.util.tty as tty

View file

@ -139,7 +139,7 @@
from spack.variant import DuplicateVariantError from spack.variant import DuplicateVariantError
from spack.variant import UnsatisfiableVariantSpecError from spack.variant import UnsatisfiableVariantSpecError
from spack.version import VersionList, VersionRange, Version, ver from spack.version import VersionList, VersionRange, Version, ver
from yaml.error import MarkedYAMLError from ruamel.yaml.error import MarkedYAMLError
__all__ = [ __all__ = [
'Spec', 'Spec',
@ -1470,6 +1470,7 @@ def to_node_dict(self, hash_function=None):
v.yaml_entry() for _, v in self.variants.items() v.yaml_entry() for _, v in self.variants.items()
) )
) )
params.update(sorted(self.compiler_flags.items())) params.update(sorted(self.compiler_flags.items()))
if params: if params:
d['parameters'] = params d['parameters'] = params
@ -1911,7 +1912,8 @@ def concretize(self, tests=False):
mvar.value = mvar.value + tuple(patches) mvar.value = mvar.value + tuple(patches)
# FIXME: Monkey patches mvar to store patches order # FIXME: Monkey patches mvar to store patches order
p = getattr(mvar, '_patches_in_order_of_appearance', []) p = getattr(mvar, '_patches_in_order_of_appearance', [])
mvar._patches_in_order_of_appearance = dedupe(p + patches) mvar._patches_in_order_of_appearance = list(
dedupe(p + patches))
for s in self.traverse(): for s in self.traverse():
if s.external_module: if s.external_module:

View file

@ -30,7 +30,7 @@
from llnl.util.filesystem import touch, mkdirp from llnl.util.filesystem import touch, mkdirp
import pytest import pytest
import yaml import ruamel.yaml as yaml
import spack.paths import spack.paths
import spack.config import spack.config

View file

@ -33,7 +33,7 @@
import ordereddict_backport import ordereddict_backport
import py import py
import pytest import pytest
import yaml import ruamel.yaml as yaml
from llnl.util.filesystem import remove_linked_tree from llnl.util.filesystem import remove_linked_tree

View file

@ -27,7 +27,7 @@
import contextlib import contextlib
import inspect import inspect
import yaml import ruamel.yaml as yaml
import pytest import pytest
from six import StringIO from six import StringIO

View file

@ -34,10 +34,10 @@
from ordereddict_backport import OrderedDict from ordereddict_backport import OrderedDict
from six import string_types, StringIO from six import string_types, StringIO
import yaml import ruamel.yaml as yaml
from yaml import Loader, Dumper from ruamel.yaml import Loader, Dumper
from yaml.nodes import MappingNode, SequenceNode, ScalarNode from ruamel.yaml.nodes import MappingNode, SequenceNode, ScalarNode
from yaml.constructor import ConstructorError from ruamel.yaml.constructor import ConstructorError
from llnl.util.tty.color import colorize, clen, cextra from llnl.util.tty.color import colorize, clen, cextra