From 0b03e27032bc8722cedcfa22f17311cc7594ecfa Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 17 Dec 2018 20:52:16 -0500 Subject: [PATCH] Add pyyaml version 3.13 to Python 2 requirements --- libs/py2/_yaml.pyd | Bin 0 -> 225280 bytes libs/py2/yaml/__init__.py | 315 ++++++++ libs/py2/yaml/composer.py | 139 ++++ libs/py2/yaml/constructor.py | 675 ++++++++++++++++ libs/py2/yaml/cyaml.py | 85 ++ libs/py2/yaml/dumper.py | 62 ++ libs/py2/yaml/emitter.py | 1140 ++++++++++++++++++++++++++ libs/py2/yaml/error.py | 75 ++ libs/py2/yaml/events.py | 86 ++ libs/py2/yaml/loader.py | 40 + libs/py2/yaml/nodes.py | 49 ++ libs/py2/yaml/parser.py | 589 ++++++++++++++ libs/py2/yaml/reader.py | 190 +++++ libs/py2/yaml/representer.py | 486 ++++++++++++ libs/py2/yaml/resolver.py | 227 ++++++ libs/py2/yaml/scanner.py | 1453 ++++++++++++++++++++++++++++++++++ libs/py2/yaml/serializer.py | 111 +++ libs/py2/yaml/tokens.py | 104 +++ libs/requirements-py2.txt | 1 + 19 files changed, 5827 insertions(+) create mode 100644 libs/py2/_yaml.pyd create mode 100644 libs/py2/yaml/__init__.py create mode 100644 libs/py2/yaml/composer.py create mode 100644 libs/py2/yaml/constructor.py create mode 100644 libs/py2/yaml/cyaml.py create mode 100644 libs/py2/yaml/dumper.py create mode 100644 libs/py2/yaml/emitter.py create mode 100644 libs/py2/yaml/error.py create mode 100644 libs/py2/yaml/events.py create mode 100644 libs/py2/yaml/loader.py create mode 100644 libs/py2/yaml/nodes.py create mode 100644 libs/py2/yaml/parser.py create mode 100644 libs/py2/yaml/reader.py create mode 100644 libs/py2/yaml/representer.py create mode 100644 libs/py2/yaml/resolver.py create mode 100644 libs/py2/yaml/scanner.py create mode 100644 libs/py2/yaml/serializer.py create mode 100644 libs/py2/yaml/tokens.py diff --git a/libs/py2/_yaml.pyd b/libs/py2/_yaml.pyd new file mode 100644 index 0000000000000000000000000000000000000000..3a505435678d4aa2689eef98b59417a2e217d8be GIT binary patch literal 225280 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 OanDMRF~cqV(e=hWV=A4hok2jEpQeNhBBIPr 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[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[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) + diff --git a/libs/py2/yaml/cyaml.py b/libs/py2/yaml/cyaml.py new file mode 100644 index 00000000..68dcd751 --- /dev/null +++ b/libs/py2/yaml/cyaml.py @@ -0,0 +1,85 @@ + +__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) + diff --git a/libs/py2/yaml/dumper.py b/libs/py2/yaml/dumper.py new file mode 100644 index 00000000..f811d2c9 --- /dev/null +++ b/libs/py2/yaml/dumper.py @@ -0,0 +1,62 @@ + +__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) + diff --git a/libs/py2/yaml/emitter.py b/libs/py2/yaml/emitter.py new file mode 100644 index 00000000..e5bcdccc --- /dev/null +++ b/libs/py2/yaml/emitter.py @@ -0,0 +1,1140 @@ + +# Emitter expects events obeying the following grammar: +# stream ::= STREAM-START document* STREAM-END +# document ::= DOCUMENT-START node DOCUMENT-END +# node ::= SCALAR | sequence | mapping +# sequence ::= SEQUENCE-START node* SEQUENCE-END +# mapping ::= MAPPING-START (node node)* MAPPING-END + +__all__ = ['Emitter', 'EmitterError'] + +from error import YAMLError +from events import * + +class EmitterError(YAMLError): + pass + +class ScalarAnalysis(object): + def __init__(self, scalar, empty, multiline, + allow_flow_plain, allow_block_plain, + allow_single_quoted, allow_double_quoted, + allow_block): + self.scalar = scalar + self.empty = empty + self.multiline = multiline + self.allow_flow_plain = allow_flow_plain + self.allow_block_plain = allow_block_plain + self.allow_single_quoted = allow_single_quoted + self.allow_double_quoted = allow_double_quoted + self.allow_block = allow_block + +class Emitter(object): + + DEFAULT_TAG_PREFIXES = { + u'!' : u'!', + u'tag:yaml.org,2002:' : u'!!', + } + + def __init__(self, stream, canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None): + + # The stream should have the methods `write` and possibly `flush`. + self.stream = stream + + # Encoding can be overriden by STREAM-START. + self.encoding = None + + # Emitter is a state machine with a stack of states to handle nested + # structures. + self.states = [] + self.state = self.expect_stream_start + + # Current event and the event queue. + self.events = [] + self.event = None + + # The current indentation level and the stack of previous indents. + self.indents = [] + self.indent = None + + # Flow level. + self.flow_level = 0 + + # Contexts. + self.root_context = False + self.sequence_context = False + self.mapping_context = False + self.simple_key_context = False + + # Characteristics of the last emitted character: + # - current position. + # - is it a whitespace? + # - is it an indention character + # (indentation space, '-', '?', or ':')? + self.line = 0 + self.column = 0 + self.whitespace = True + self.indention = True + + # Whether the document requires an explicit document indicator + self.open_ended = False + + # Formatting details. + self.canonical = canonical + self.allow_unicode = allow_unicode + self.best_indent = 2 + if indent and 1 < indent < 10: + self.best_indent = indent + self.best_width = 80 + if width and width > self.best_indent*2: + self.best_width = width + self.best_line_break = u'\n' + if line_break in [u'\r', u'\n', u'\r\n']: + self.best_line_break = line_break + + # Tag prefixes. + self.tag_prefixes = None + + # Prepared anchor and tag. + self.prepared_anchor = None + self.prepared_tag = None + + # Scalar analysis and style. + self.analysis = None + self.style = None + + def dispose(self): + # Reset the state attributes (to clear self-references) + self.states = [] + self.state = None + + def emit(self, event): + self.events.append(event) + while not self.need_more_events(): + self.event = self.events.pop(0) + self.state() + self.event = None + + # In some cases, we wait for a few next events before emitting. + + def need_more_events(self): + if not self.events: + return True + event = self.events[0] + if isinstance(event, DocumentStartEvent): + return self.need_events(1) + elif isinstance(event, SequenceStartEvent): + return self.need_events(2) + elif isinstance(event, MappingStartEvent): + return self.need_events(3) + else: + return False + + def need_events(self, count): + level = 0 + for event in self.events[1:]: + if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): + level += 1 + elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): + level -= 1 + elif isinstance(event, StreamEndEvent): + level = -1 + if level < 0: + return False + return (len(self.events) < count+1) + + def increase_indent(self, flow=False, indentless=False): + self.indents.append(self.indent) + if self.indent is None: + if flow: + self.indent = self.best_indent + else: + self.indent = 0 + elif not indentless: + self.indent += self.best_indent + + # States. + + # Stream handlers. + + def expect_stream_start(self): + if isinstance(self.event, StreamStartEvent): + if self.event.encoding and not getattr(self.stream, 'encoding', None): + self.encoding = self.event.encoding + self.write_stream_start() + self.state = self.expect_first_document_start + else: + raise EmitterError("expected StreamStartEvent, but got %s" + % self.event) + + def expect_nothing(self): + raise EmitterError("expected nothing, but got %s" % self.event) + + # Document handlers. + + def expect_first_document_start(self): + return self.expect_document_start(first=True) + + def expect_document_start(self, first=False): + if isinstance(self.event, DocumentStartEvent): + if (self.event.version or self.event.tags) and self.open_ended: + self.write_indicator(u'...', True) + self.write_indent() + if self.event.version: + version_text = self.prepare_version(self.event.version) + self.write_version_directive(version_text) + self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() + if self.event.tags: + handles = self.event.tags.keys() + handles.sort() + for handle in handles: + prefix = self.event.tags[handle] + self.tag_prefixes[prefix] = handle + handle_text = self.prepare_tag_handle(handle) + prefix_text = self.prepare_tag_prefix(prefix) + self.write_tag_directive(handle_text, prefix_text) + implicit = (first and not self.event.explicit and not self.canonical + and not self.event.version and not self.event.tags + and not self.check_empty_document()) + if not implicit: + self.write_indent() + self.write_indicator(u'---', True) + if self.canonical: + self.write_indent() + self.state = self.expect_document_root + elif isinstance(self.event, StreamEndEvent): + if self.open_ended: + self.write_indicator(u'...', True) + self.write_indent() + self.write_stream_end() + self.state = self.expect_nothing + else: + raise EmitterError("expected DocumentStartEvent, but got %s" + % self.event) + + def expect_document_end(self): + if isinstance(self.event, DocumentEndEvent): + self.write_indent() + if self.event.explicit: + self.write_indicator(u'...', True) + self.write_indent() + self.flush_stream() + self.state = self.expect_document_start + else: + raise EmitterError("expected DocumentEndEvent, but got %s" + % self.event) + + def expect_document_root(self): + self.states.append(self.expect_document_end) + self.expect_node(root=True) + + # Node handlers. + + def expect_node(self, root=False, sequence=False, mapping=False, + simple_key=False): + self.root_context = root + self.sequence_context = sequence + self.mapping_context = mapping + self.simple_key_context = simple_key + if isinstance(self.event, AliasEvent): + self.expect_alias() + elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): + self.process_anchor(u'&') + self.process_tag() + if isinstance(self.event, ScalarEvent): + self.expect_scalar() + elif isinstance(self.event, SequenceStartEvent): + if self.flow_level or self.canonical or self.event.flow_style \ + or self.check_empty_sequence(): + self.expect_flow_sequence() + else: + self.expect_block_sequence() + elif isinstance(self.event, MappingStartEvent): + if self.flow_level or self.canonical or self.event.flow_style \ + or self.check_empty_mapping(): + self.expect_flow_mapping() + else: + self.expect_block_mapping() + else: + raise EmitterError("expected NodeEvent, but got %s" % self.event) + + def expect_alias(self): + if self.event.anchor is None: + raise EmitterError("anchor is not specified for alias") + self.process_anchor(u'*') + self.state = self.states.pop() + + def expect_scalar(self): + self.increase_indent(flow=True) + self.process_scalar() + self.indent = self.indents.pop() + self.state = self.states.pop() + + # Flow sequence handlers. + + def expect_flow_sequence(self): + self.write_indicator(u'[', True, whitespace=True) + self.flow_level += 1 + self.increase_indent(flow=True) + self.state = self.expect_first_flow_sequence_item + + def expect_first_flow_sequence_item(self): + if isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + self.write_indicator(u']', False) + self.state = self.states.pop() + else: + if self.canonical or self.column > self.best_width: + self.write_indent() + self.states.append(self.expect_flow_sequence_item) + self.expect_node(sequence=True) + + def expect_flow_sequence_item(self): + if isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + if self.canonical: + self.write_indicator(u',', False) + self.write_indent() + self.write_indicator(u']', False) + self.state = self.states.pop() + else: + self.write_indicator(u',', False) + if self.canonical or self.column > self.best_width: + self.write_indent() + self.states.append(self.expect_flow_sequence_item) + self.expect_node(sequence=True) + + # Flow mapping handlers. + + def expect_flow_mapping(self): + self.write_indicator(u'{', True, whitespace=True) + self.flow_level += 1 + self.increase_indent(flow=True) + self.state = self.expect_first_flow_mapping_key + + def expect_first_flow_mapping_key(self): + if isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + self.write_indicator(u'}', False) + self.state = self.states.pop() + else: + if self.canonical or self.column > self.best_width: + self.write_indent() + if not self.canonical and self.check_simple_key(): + self.states.append(self.expect_flow_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator(u'?', True) + self.states.append(self.expect_flow_mapping_value) + self.expect_node(mapping=True) + + def expect_flow_mapping_key(self): + if isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + if self.canonical: + self.write_indicator(u',', False) + self.write_indent() + self.write_indicator(u'}', False) + self.state = self.states.pop() + else: + self.write_indicator(u',', False) + if self.canonical or self.column > self.best_width: + self.write_indent() + if not self.canonical and self.check_simple_key(): + self.states.append(self.expect_flow_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator(u'?', True) + self.states.append(self.expect_flow_mapping_value) + self.expect_node(mapping=True) + + def expect_flow_mapping_simple_value(self): + self.write_indicator(u':', False) + self.states.append(self.expect_flow_mapping_key) + self.expect_node(mapping=True) + + def expect_flow_mapping_value(self): + if self.canonical or self.column > self.best_width: + self.write_indent() + self.write_indicator(u':', True) + self.states.append(self.expect_flow_mapping_key) + self.expect_node(mapping=True) + + # Block sequence handlers. + + def expect_block_sequence(self): + indentless = (self.mapping_context and not self.indention) + self.increase_indent(flow=False, indentless=indentless) + self.state = self.expect_first_block_sequence_item + + def expect_first_block_sequence_item(self): + return self.expect_block_sequence_item(first=True) + + def expect_block_sequence_item(self, first=False): + if not first and isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.state = self.states.pop() + else: + self.write_indent() + self.write_indicator(u'-', True, indention=True) + self.states.append(self.expect_block_sequence_item) + self.expect_node(sequence=True) + + # Block mapping handlers. + + def expect_block_mapping(self): + self.increase_indent(flow=False) + self.state = self.expect_first_block_mapping_key + + def expect_first_block_mapping_key(self): + return self.expect_block_mapping_key(first=True) + + def expect_block_mapping_key(self, first=False): + if not first and isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.state = self.states.pop() + else: + self.write_indent() + if self.check_simple_key(): + self.states.append(self.expect_block_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator(u'?', True, indention=True) + self.states.append(self.expect_block_mapping_value) + self.expect_node(mapping=True) + + def expect_block_mapping_simple_value(self): + self.write_indicator(u':', False) + self.states.append(self.expect_block_mapping_key) + self.expect_node(mapping=True) + + def expect_block_mapping_value(self): + self.write_indent() + self.write_indicator(u':', True, indention=True) + self.states.append(self.expect_block_mapping_key) + self.expect_node(mapping=True) + + # Checkers. + + def check_empty_sequence(self): + return (isinstance(self.event, SequenceStartEvent) and self.events + and isinstance(self.events[0], SequenceEndEvent)) + + def check_empty_mapping(self): + return (isinstance(self.event, MappingStartEvent) and self.events + and isinstance(self.events[0], MappingEndEvent)) + + def check_empty_document(self): + if not isinstance(self.event, DocumentStartEvent) or not self.events: + return False + event = self.events[0] + return (isinstance(event, ScalarEvent) and event.anchor is None + and event.tag is None and event.implicit and event.value == u'') + + def check_simple_key(self): + length = 0 + if isinstance(self.event, NodeEvent) and self.event.anchor is not None: + if self.prepared_anchor is None: + self.prepared_anchor = self.prepare_anchor(self.event.anchor) + length += len(self.prepared_anchor) + if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \ + and self.event.tag is not None: + if self.prepared_tag is None: + self.prepared_tag = self.prepare_tag(self.event.tag) + length += len(self.prepared_tag) + if isinstance(self.event, ScalarEvent): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + length += len(self.analysis.scalar) + return (length < 128 and (isinstance(self.event, AliasEvent) + or (isinstance(self.event, ScalarEvent) + and not self.analysis.empty and not self.analysis.multiline) + or self.check_empty_sequence() or self.check_empty_mapping())) + + # Anchor, Tag, and Scalar processors. + + def process_anchor(self, indicator): + if self.event.anchor is None: + self.prepared_anchor = None + return + if self.prepared_anchor is None: + self.prepared_anchor = self.prepare_anchor(self.event.anchor) + if self.prepared_anchor: + self.write_indicator(indicator+self.prepared_anchor, True) + self.prepared_anchor = None + + def process_tag(self): + tag = self.event.tag + if isinstance(self.event, ScalarEvent): + if self.style is None: + self.style = self.choose_scalar_style() + if ((not self.canonical or tag is None) and + ((self.style == '' and self.event.implicit[0]) + or (self.style != '' and self.event.implicit[1]))): + self.prepared_tag = None + return + if self.event.implicit[0] and tag is None: + tag = u'!' + self.prepared_tag = None + else: + if (not self.canonical or tag is None) and self.event.implicit: + self.prepared_tag = None + return + if tag is None: + raise EmitterError("tag is not specified") + if self.prepared_tag is None: + self.prepared_tag = self.prepare_tag(tag) + if self.prepared_tag: + self.write_indicator(self.prepared_tag, True) + self.prepared_tag = None + + def choose_scalar_style(self): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + if self.event.style == '"' or self.canonical: + return '"' + if not self.event.style and self.event.implicit[0]: + if (not (self.simple_key_context and + (self.analysis.empty or self.analysis.multiline)) + and (self.flow_level and self.analysis.allow_flow_plain + or (not self.flow_level and self.analysis.allow_block_plain))): + return '' + if self.event.style and self.event.style in '|>': + if (not self.flow_level and not self.simple_key_context + and self.analysis.allow_block): + return self.event.style + if not self.event.style or self.event.style == '\'': + if (self.analysis.allow_single_quoted and + not (self.simple_key_context and self.analysis.multiline)): + return '\'' + return '"' + + def process_scalar(self): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + if self.style is None: + self.style = self.choose_scalar_style() + split = (not self.simple_key_context) + #if self.analysis.multiline and split \ + # and (not self.style or self.style in '\'\"'): + # self.write_indent() + if self.style == '"': + self.write_double_quoted(self.analysis.scalar, split) + elif self.style == '\'': + self.write_single_quoted(self.analysis.scalar, split) + elif self.style == '>': + self.write_folded(self.analysis.scalar) + elif self.style == '|': + self.write_literal(self.analysis.scalar) + else: + self.write_plain(self.analysis.scalar, split) + self.analysis = None + self.style = None + + # Analyzers. + + def prepare_version(self, version): + major, minor = version + if major != 1: + raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) + return u'%d.%d' % (major, minor) + + def prepare_tag_handle(self, handle): + if not handle: + raise EmitterError("tag handle must not be empty") + if handle[0] != u'!' or handle[-1] != u'!': + raise EmitterError("tag handle must start and end with '!': %r" + % (handle.encode('utf-8'))) + 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' \ + or ch in u'-_'): + raise EmitterError("invalid character %r in the tag handle: %r" + % (ch.encode('utf-8'), handle.encode('utf-8'))) + return handle + + def prepare_tag_prefix(self, prefix): + if not prefix: + raise EmitterError("tag prefix must not be empty") + chunks = [] + start = end = 0 + if prefix[0] == u'!': + end = 1 + while end < len(prefix): + ch = prefix[end] + if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-;/?!:@&=+$,_.~*\'()[]': + end += 1 + else: + if start < end: + chunks.append(prefix[start:end]) + start = end = end+1 + data = ch.encode('utf-8') + for ch in data: + chunks.append(u'%%%02X' % ord(ch)) + if start < end: + chunks.append(prefix[start:end]) + return u''.join(chunks) + + def prepare_tag(self, tag): + if not tag: + raise EmitterError("tag must not be empty") + if tag == u'!': + return tag + handle = None + suffix = tag + prefixes = self.tag_prefixes.keys() + prefixes.sort() + for prefix in prefixes: + if tag.startswith(prefix) \ + and (prefix == u'!' or len(prefix) < len(tag)): + handle = self.tag_prefixes[prefix] + suffix = tag[len(prefix):] + chunks = [] + start = end = 0 + while end < len(suffix): + ch = suffix[end] + if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-;/?:@&=+$,_.~*\'()[]' \ + or (ch == u'!' and handle != u'!'): + end += 1 + else: + if start < end: + chunks.append(suffix[start:end]) + start = end = end+1 + data = ch.encode('utf-8') + for ch in data: + chunks.append(u'%%%02X' % ord(ch)) + if start < end: + chunks.append(suffix[start:end]) + suffix_text = u''.join(chunks) + if handle: + return u'%s%s' % (handle, suffix_text) + else: + return u'!<%s>' % suffix_text + + def prepare_anchor(self, anchor): + if not anchor: + raise EmitterError("anchor must not be empty") + for ch in anchor: + if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_'): + raise EmitterError("invalid character %r in the anchor: %r" + % (ch.encode('utf-8'), anchor.encode('utf-8'))) + return anchor + + def analyze_scalar(self, scalar): + + # Empty scalar is a special case. + if not scalar: + return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, + allow_flow_plain=False, allow_block_plain=True, + allow_single_quoted=True, allow_double_quoted=True, + allow_block=False) + + # Indicators and special characters. + block_indicators = False + flow_indicators = False + line_breaks = False + special_characters = False + + # Important whitespace combinations. + leading_space = False + leading_break = False + trailing_space = False + trailing_break = False + break_space = False + space_break = False + + # Check document indicators. + if scalar.startswith(u'---') or scalar.startswith(u'...'): + block_indicators = True + flow_indicators = True + + # First character or preceded by a whitespace. + preceeded_by_whitespace = True + + # Last character or followed by a whitespace. + followed_by_whitespace = (len(scalar) == 1 or + scalar[1] in u'\0 \t\r\n\x85\u2028\u2029') + + # The previous character is a space. + previous_space = False + + # The previous character is a break. + previous_break = False + + index = 0 + while index < len(scalar): + ch = scalar[index] + + # Check for indicators. + if index == 0: + # Leading indicators are special characters. + if ch in u'#,[]{}&*!|>\'\"%@`': + flow_indicators = True + block_indicators = True + if ch in u'?:': + flow_indicators = True + if followed_by_whitespace: + block_indicators = True + if ch == u'-' and followed_by_whitespace: + flow_indicators = True + block_indicators = True + else: + # Some indicators cannot appear within a scalar as well. + if ch in u',?[]{}': + flow_indicators = True + if ch == u':': + flow_indicators = True + if followed_by_whitespace: + block_indicators = True + if ch == u'#' and preceeded_by_whitespace: + flow_indicators = True + block_indicators = True + + # Check for line breaks, special, and unicode characters. + if ch in u'\n\x85\u2028\u2029': + line_breaks = True + if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'): + if (ch == u'\x85' or u'\xA0' <= ch <= u'\uD7FF' + or u'\uE000' <= ch <= u'\uFFFD') and ch != u'\uFEFF': + unicode_characters = True + if not self.allow_unicode: + special_characters = True + else: + special_characters = True + + # Detect important whitespace combinations. + if ch == u' ': + if index == 0: + leading_space = True + if index == len(scalar)-1: + trailing_space = True + if previous_break: + break_space = True + previous_space = True + previous_break = False + elif ch in u'\n\x85\u2028\u2029': + if index == 0: + leading_break = True + if index == len(scalar)-1: + trailing_break = True + if previous_space: + space_break = True + previous_space = False + previous_break = True + else: + previous_space = False + previous_break = False + + # Prepare for the next character. + index += 1 + preceeded_by_whitespace = (ch in u'\0 \t\r\n\x85\u2028\u2029') + followed_by_whitespace = (index+1 >= len(scalar) or + scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029') + + # Let's decide what styles are allowed. + allow_flow_plain = True + allow_block_plain = True + allow_single_quoted = True + allow_double_quoted = True + allow_block = True + + # Leading and trailing whitespaces are bad for plain scalars. + if (leading_space or leading_break + or trailing_space or trailing_break): + allow_flow_plain = allow_block_plain = False + + # We do not permit trailing spaces for block scalars. + if trailing_space: + allow_block = False + + # Spaces at the beginning of a new line are only acceptable for block + # scalars. + if break_space: + allow_flow_plain = allow_block_plain = allow_single_quoted = False + + # Spaces followed by breaks, as well as special character are only + # allowed for double quoted scalars. + if space_break or special_characters: + allow_flow_plain = allow_block_plain = \ + allow_single_quoted = allow_block = False + + # Although the plain scalar writer supports breaks, we never emit + # multiline plain scalars. + if line_breaks: + allow_flow_plain = allow_block_plain = False + + # Flow indicators are forbidden for flow plain scalars. + if flow_indicators: + allow_flow_plain = False + + # Block indicators are forbidden for block plain scalars. + if block_indicators: + allow_block_plain = False + + return ScalarAnalysis(scalar=scalar, + empty=False, multiline=line_breaks, + allow_flow_plain=allow_flow_plain, + allow_block_plain=allow_block_plain, + allow_single_quoted=allow_single_quoted, + allow_double_quoted=allow_double_quoted, + allow_block=allow_block) + + # Writers. + + def flush_stream(self): + if hasattr(self.stream, 'flush'): + self.stream.flush() + + def write_stream_start(self): + # Write BOM if needed. + if self.encoding and self.encoding.startswith('utf-16'): + self.stream.write(u'\uFEFF'.encode(self.encoding)) + + def write_stream_end(self): + self.flush_stream() + + def write_indicator(self, indicator, need_whitespace, + whitespace=False, indention=False): + if self.whitespace or not need_whitespace: + data = indicator + else: + data = u' '+indicator + self.whitespace = whitespace + self.indention = self.indention and indention + self.column += len(data) + self.open_ended = False + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_indent(self): + indent = self.indent or 0 + if not self.indention or self.column > indent \ + or (self.column == indent and not self.whitespace): + self.write_line_break() + if self.column < indent: + self.whitespace = True + data = u' '*(indent-self.column) + self.column = indent + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_line_break(self, data=None): + if data is None: + data = self.best_line_break + self.whitespace = True + self.indention = True + self.line += 1 + self.column = 0 + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_version_directive(self, version_text): + data = u'%%YAML %s' % version_text + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_line_break() + + def write_tag_directive(self, handle_text, prefix_text): + data = u'%%TAG %s %s' % (handle_text, prefix_text) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_line_break() + + # Scalar streams. + + def write_single_quoted(self, text, split=True): + self.write_indicator(u'\'', True) + spaces = False + breaks = False + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if spaces: + if ch is None or ch != u' ': + if start+1 == end and self.column > self.best_width and split \ + and start != 0 and end != len(text): + self.write_indent() + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + elif breaks: + if ch is None or ch not in u'\n\x85\u2028\u2029': + if text[start] == u'\n': + self.write_line_break() + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + self.write_indent() + start = end + else: + if ch is None or ch in u' \n\x85\u2028\u2029' or ch == u'\'': + if start < end: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch == u'\'': + data = u'\'\'' + self.column += 2 + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + 1 + if ch is not None: + spaces = (ch == u' ') + breaks = (ch in u'\n\x85\u2028\u2029') + end += 1 + self.write_indicator(u'\'', False) + + ESCAPE_REPLACEMENTS = { + u'\0': u'0', + u'\x07': u'a', + u'\x08': u'b', + u'\x09': u't', + u'\x0A': u'n', + u'\x0B': u'v', + u'\x0C': u'f', + u'\x0D': u'r', + u'\x1B': u'e', + u'\"': u'\"', + u'\\': u'\\', + u'\x85': u'N', + u'\xA0': u'_', + u'\u2028': u'L', + u'\u2029': u'P', + } + + def write_double_quoted(self, text, split=True): + self.write_indicator(u'"', True) + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if ch is None or ch in u'"\\\x85\u2028\u2029\uFEFF' \ + or not (u'\x20' <= ch <= u'\x7E' + or (self.allow_unicode + and (u'\xA0' <= ch <= u'\uD7FF' + or u'\uE000' <= ch <= u'\uFFFD'))): + if start < end: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch is not None: + if ch in self.ESCAPE_REPLACEMENTS: + data = u'\\'+self.ESCAPE_REPLACEMENTS[ch] + elif ch <= u'\xFF': + data = u'\\x%02X' % ord(ch) + elif ch <= u'\uFFFF': + data = u'\\u%04X' % ord(ch) + else: + data = u'\\U%08X' % ord(ch) + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end+1 + if 0 < end < len(text)-1 and (ch == u' ' or start >= end) \ + and self.column+(end-start) > self.best_width and split: + data = text[start:end]+u'\\' + if start < end: + start = end + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_indent() + self.whitespace = False + self.indention = False + if text[start] == u' ': + data = u'\\' + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + end += 1 + self.write_indicator(u'"', False) + + def determine_block_hints(self, text): + hints = u'' + if text: + if text[0] in u' \n\x85\u2028\u2029': + hints += unicode(self.best_indent) + if text[-1] not in u'\n\x85\u2028\u2029': + hints += u'-' + elif len(text) == 1 or text[-2] in u'\n\x85\u2028\u2029': + hints += u'+' + return hints + + def write_folded(self, text): + hints = self.determine_block_hints(text) + self.write_indicator(u'>'+hints, True) + if hints[-1:] == u'+': + self.open_ended = True + self.write_line_break() + leading_space = True + spaces = False + breaks = True + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if breaks: + if ch is None or ch not in u'\n\x85\u2028\u2029': + if not leading_space and ch is not None and ch != u' ' \ + and text[start] == u'\n': + self.write_line_break() + leading_space = (ch == u' ') + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + if ch is not None: + self.write_indent() + start = end + elif spaces: + if ch != u' ': + if start+1 == end and self.column > self.best_width: + self.write_indent() + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + else: + if ch is None or ch in u' \n\x85\u2028\u2029': + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + if ch is None: + self.write_line_break() + start = end + if ch is not None: + breaks = (ch in u'\n\x85\u2028\u2029') + spaces = (ch == u' ') + end += 1 + + def write_literal(self, text): + hints = self.determine_block_hints(text) + self.write_indicator(u'|'+hints, True) + if hints[-1:] == u'+': + self.open_ended = True + self.write_line_break() + breaks = True + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if breaks: + if ch is None or ch not in u'\n\x85\u2028\u2029': + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + if ch is not None: + self.write_indent() + start = end + else: + if ch is None or ch in u'\n\x85\u2028\u2029': + data = text[start:end] + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + if ch is None: + self.write_line_break() + start = end + if ch is not None: + breaks = (ch in u'\n\x85\u2028\u2029') + end += 1 + + def write_plain(self, text, split=True): + if self.root_context: + self.open_ended = True + if not text: + return + if not self.whitespace: + data = u' ' + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.whitespace = False + self.indention = False + spaces = False + breaks = False + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if spaces: + if ch != u' ': + if start+1 == end and self.column > self.best_width and split: + self.write_indent() + self.whitespace = False + self.indention = False + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + elif breaks: + if ch not in u'\n\x85\u2028\u2029': + if text[start] == u'\n': + self.write_line_break() + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + self.write_indent() + self.whitespace = False + self.indention = False + start = end + else: + if ch is None or ch in u' \n\x85\u2028\u2029': + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch is not None: + spaces = (ch == u' ') + breaks = (ch in u'\n\x85\u2028\u2029') + end += 1 + diff --git a/libs/py2/yaml/error.py b/libs/py2/yaml/error.py new file mode 100644 index 00000000..577686db --- /dev/null +++ b/libs/py2/yaml/error.py @@ -0,0 +1,75 @@ + +__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] + +class Mark(object): + + 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 u'\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 u'\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].encode('utf-8') + 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) + diff --git a/libs/py2/yaml/events.py b/libs/py2/yaml/events.py new file mode 100644 index 00000000..f79ad389 --- /dev/null +++ b/libs/py2/yaml/events.py @@ -0,0 +1,86 @@ + +# 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 + diff --git a/libs/py2/yaml/loader.py b/libs/py2/yaml/loader.py new file mode 100644 index 00000000..293ff467 --- /dev/null +++ b/libs/py2/yaml/loader.py @@ -0,0 +1,40 @@ + +__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) + diff --git a/libs/py2/yaml/nodes.py b/libs/py2/yaml/nodes.py new file mode 100644 index 00000000..c4f070c4 --- /dev/null +++ b/libs/py2/yaml/nodes.py @@ -0,0 +1,49 @@ + +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 = '' + # 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' + diff --git a/libs/py2/yaml/parser.py b/libs/py2/yaml/parser.py new file mode 100644 index 00000000..f9e3057f --- /dev/null +++ b/libs/py2/yaml/parser.py @@ -0,0 +1,589 @@ + +# 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(object): + # Since writing a recursive-descendant parser is a straightforward task, we + # do not give many comments here. + + DEFAULT_TAGS = { + u'!': u'!', + u'!!': u'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 '', 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 == u'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 == u'TAG': + handle, prefix = token.value + if handle in self.tag_handles: + raise ParserError(None, None, + "duplicate tag handle %r" % handle.encode('utf-8'), + 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.encode('utf-8'), + tag_mark) + tag = self.tag_handles[handle]+suffix + else: + tag = suffix + #if tag == u'!': + # 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 == u'!') + 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 == u'!': + 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), u'', + 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 , 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 , 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), u'', mark, mark) + diff --git a/libs/py2/yaml/reader.py b/libs/py2/yaml/reader.py new file mode 100644 index 00000000..3249e6b9 --- /dev/null +++ b/libs/py2/yaml/reader.py @@ -0,0 +1,190 @@ +# 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 = "" + self.check_printable(stream) + self.buffer = stream+u'\0' + elif isinstance(stream, str): + self.name = "" + self.raw_buffer = stream + self.determine_encoding() + else: + self.stream = stream + self.name = getattr(stream, 'name', "") + 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 + diff --git a/libs/py2/yaml/representer.py b/libs/py2/yaml/representer.py new file mode 100644 index 00000000..4ea8cb1f --- /dev/null +++ b/libs/py2/yaml/representer.py @@ -0,0 +1,486 @@ + +__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) + diff --git a/libs/py2/yaml/resolver.py b/libs/py2/yaml/resolver.py new file mode 100644 index 00000000..528fbc0e --- /dev/null +++ b/libs/py2/yaml/resolver.py @@ -0,0 +1,227 @@ + +__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'!&*')) + diff --git a/libs/py2/yaml/scanner.py b/libs/py2/yaml/scanner.py new file mode 100644 index 00000000..834f662a --- /dev/null +++ b/libs/py2/yaml/scanner.py @@ -0,0 +1,1453 @@ + +# Scanner produces tokens of the following types: +# STREAM-START +# STREAM-END +# DIRECTIVE(name, value) +# DOCUMENT-START +# DOCUMENT-END +# BLOCK-SEQUENCE-START +# BLOCK-MAPPING-START +# BLOCK-END +# FLOW-SEQUENCE-START +# FLOW-MAPPING-START +# FLOW-SEQUENCE-END +# FLOW-MAPPING-END +# BLOCK-ENTRY +# FLOW-ENTRY +# KEY +# VALUE +# ALIAS(value) +# ANCHOR(value) +# TAG(value) +# SCALAR(value, plain, style) +# +# Read comments in the Scanner code for more details. +# + +__all__ = ['Scanner', 'ScannerError'] + +from error import MarkedYAMLError +from tokens import * + +class ScannerError(MarkedYAMLError): + pass + +class SimpleKey(object): + # See below simple keys treatment. + + def __init__(self, token_number, required, index, line, column, mark): + self.token_number = token_number + self.required = required + self.index = index + self.line = line + self.column = column + self.mark = mark + +class Scanner(object): + + def __init__(self): + """Initialize the scanner.""" + # It is assumed that Scanner and Reader will have a common descendant. + # Reader do the dirty work of checking for BOM and converting the + # input data to Unicode. It also adds NUL to the end. + # + # Reader supports the following methods + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. + + # Had we reached the end of the stream? + self.done = False + + # The number of unclosed '{' and '['. `flow_level == 0` means block + # context. + self.flow_level = 0 + + # List of processed tokens that are not yet emitted. + self.tokens = [] + + # Add the STREAM-START token. + self.fetch_stream_start() + + # Number of tokens that were emitted through the `get_token` method. + self.tokens_taken = 0 + + # The current indentation level. + self.indent = -1 + + # Past indentation levels. + self.indents = [] + + # Variables related to simple keys treatment. + + # A simple key is a key that is not denoted by the '?' indicator. + # Example of simple keys: + # --- + # block simple key: value + # ? not a simple key: + # : { flow simple key: value } + # We emit the KEY token before all keys, so when we find a potential + # simple key, we try to locate the corresponding ':' indicator. + # Simple keys should be limited to a single line and 1024 characters. + + # Can a simple key start at the current position? A simple key may + # start: + # - at the beginning of the line, not counting indentation spaces + # (in block context), + # - after '{', '[', ',' (in the flow context), + # - after '?', ':', '-' (in the block context). + # In the block context, this flag also signifies if a block collection + # may start at the current position. + self.allow_simple_key = True + + # Keep track of possible simple keys. This is a dictionary. The key + # is `flow_level`; there can be no more that one possible simple key + # for each level. The value is a SimpleKey record: + # (token_number, required, index, line, column, mark) + # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow), + # '[', or '{' tokens. + self.possible_simple_keys = {} + + # Public methods. + + 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() + 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() + if self.tokens: + return self.tokens[0] + + def get_token(self): + # Return the next token. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + self.tokens_taken += 1 + return self.tokens.pop(0) + + # Private methods. + + def need_more_tokens(self): + if self.done: + return False + if not self.tokens: + return True + # The current token may be a potential simple key, so we + # need to look further. + self.stale_possible_simple_keys() + if self.next_possible_simple_key() == self.tokens_taken: + return True + + def fetch_more_tokens(self): + + # Eat whitespaces and comments until we reach the next token. + self.scan_to_next_token() + + # Remove obsolete possible simple keys. + self.stale_possible_simple_keys() + + # Compare the current indentation and column. It may add some tokens + # and decrease the current indentation level. + self.unwind_indent(self.column) + + # Peek the next character. + ch = self.peek() + + # Is it the end of stream? + if ch == u'\0': + return self.fetch_stream_end() + + # Is it a directive? + if ch == u'%' and self.check_directive(): + return self.fetch_directive() + + # Is it the document start? + if ch == u'-' and self.check_document_start(): + return self.fetch_document_start() + + # Is it the document end? + if ch == u'.' and self.check_document_end(): + return self.fetch_document_end() + + # TODO: support for BOM within a stream. + #if ch == u'\uFEFF': + # return self.fetch_bom() <-- issue BOMToken + + # Note: the order of the following checks is NOT significant. + + # Is it the flow sequence start indicator? + if ch == u'[': + return self.fetch_flow_sequence_start() + + # Is it the flow mapping start indicator? + if ch == u'{': + return self.fetch_flow_mapping_start() + + # Is it the flow sequence end indicator? + if ch == u']': + return self.fetch_flow_sequence_end() + + # Is it the flow mapping end indicator? + if ch == u'}': + return self.fetch_flow_mapping_end() + + # Is it the flow entry indicator? + if ch == u',': + return self.fetch_flow_entry() + + # Is it the block entry indicator? + if ch == u'-' and self.check_block_entry(): + return self.fetch_block_entry() + + # Is it the key indicator? + if ch == u'?' and self.check_key(): + return self.fetch_key() + + # Is it the value indicator? + if ch == u':' and self.check_value(): + return self.fetch_value() + + # Is it an alias? + if ch == u'*': + return self.fetch_alias() + + # Is it an anchor? + if ch == u'&': + return self.fetch_anchor() + + # Is it a tag? + if ch == u'!': + return self.fetch_tag() + + # Is it a literal scalar? + if ch == u'|' and not self.flow_level: + return self.fetch_literal() + + # Is it a folded scalar? + if ch == u'>' and not self.flow_level: + return self.fetch_folded() + + # Is it a single quoted scalar? + if ch == u'\'': + return self.fetch_single() + + # Is it a double quoted scalar? + if ch == u'\"': + return self.fetch_double() + + # It must be a plain scalar then. + if self.check_plain(): + return self.fetch_plain() + + # No? It's an error. Let's produce a nice error message. + raise ScannerError("while scanning for the next token", None, + "found character %r that cannot start any token" + % ch.encode('utf-8'), self.get_mark()) + + # Simple keys treatment. + + def next_possible_simple_key(self): + # Return the number of the nearest possible simple key. Actually we + # don't need to loop through the whole dictionary. We may replace it + # with the following code: + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number + min_token_number = None + for level in self.possible_simple_keys: + key = self.possible_simple_keys[level] + if min_token_number is None or key.token_number < min_token_number: + min_token_number = key.token_number + return min_token_number + + def stale_possible_simple_keys(self): + # Remove entries that are no longer possible simple keys. According to + # the YAML specification, simple keys + # - should be limited to a single line, + # - should be no longer than 1024 characters. + # Disabling this procedure will allow simple keys of any length and + # height (may cause problems if indentation is broken though). + for level in self.possible_simple_keys.keys(): + key = self.possible_simple_keys[level] + if key.line != self.line \ + or self.index-key.index > 1024: + if key.required: + raise ScannerError("while scanning a simple key", key.mark, + "could not find expected ':'", self.get_mark()) + del self.possible_simple_keys[level] + + def save_possible_simple_key(self): + # The next token may start a simple key. We check if it's possible + # and save its position. This function is called for + # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'. + + # Check if a simple key is required at the current position. + required = not self.flow_level and self.indent == self.column + + # The next token might be a simple key. Let's save it's number and + # position. + if self.allow_simple_key: + self.remove_possible_simple_key() + token_number = self.tokens_taken+len(self.tokens) + key = SimpleKey(token_number, required, + self.index, self.line, self.column, self.get_mark()) + self.possible_simple_keys[self.flow_level] = key + + def remove_possible_simple_key(self): + # Remove the saved possible key position at the current flow level. + if self.flow_level in self.possible_simple_keys: + key = self.possible_simple_keys[self.flow_level] + + if key.required: + raise ScannerError("while scanning a simple key", key.mark, + "could not find expected ':'", self.get_mark()) + + del self.possible_simple_keys[self.flow_level] + + # Indentation functions. + + def unwind_indent(self, column): + + ## In flow context, tokens should respect indentation. + ## Actually the condition should be `self.indent >= column` according to + ## the spec. But this condition will prohibit intuitively correct + ## constructions such as + ## key : { + ## } + #if self.flow_level and self.indent > column: + # raise ScannerError(None, None, + # "invalid intendation or unclosed '[' or '{'", + # self.get_mark()) + + # In the flow context, indentation is ignored. We make the scanner less + # restrictive then specification requires. + if self.flow_level: + return + + # In block context, we may need to issue the BLOCK-END tokens. + while self.indent > column: + mark = self.get_mark() + self.indent = self.indents.pop() + self.tokens.append(BlockEndToken(mark, mark)) + + def add_indent(self, column): + # Check if we need to increase indentation. + if self.indent < column: + self.indents.append(self.indent) + self.indent = column + return True + return False + + # Fetchers. + + def fetch_stream_start(self): + # We always add STREAM-START as the first token and STREAM-END as the + # last token. + + # Read the token. + mark = self.get_mark() + + # Add STREAM-START. + self.tokens.append(StreamStartToken(mark, mark, + encoding=self.encoding)) + + + def fetch_stream_end(self): + + # Set the current intendation to -1. + self.unwind_indent(-1) + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + self.possible_simple_keys = {} + + # Read the token. + mark = self.get_mark() + + # Add STREAM-END. + self.tokens.append(StreamEndToken(mark, mark)) + + # The steam is finished. + self.done = True + + def fetch_directive(self): + + # Set the current intendation to -1. + self.unwind_indent(-1) + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + + # Scan and add DIRECTIVE. + self.tokens.append(self.scan_directive()) + + def fetch_document_start(self): + self.fetch_document_indicator(DocumentStartToken) + + def fetch_document_end(self): + self.fetch_document_indicator(DocumentEndToken) + + def fetch_document_indicator(self, TokenClass): + + # Set the current intendation to -1. + self.unwind_indent(-1) + + # Reset simple keys. Note that there could not be a block collection + # after '---'. + self.remove_possible_simple_key() + self.allow_simple_key = False + + # Add DOCUMENT-START or DOCUMENT-END. + start_mark = self.get_mark() + self.forward(3) + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_start(self): + self.fetch_flow_collection_start(FlowSequenceStartToken) + + def fetch_flow_mapping_start(self): + self.fetch_flow_collection_start(FlowMappingStartToken) + + def fetch_flow_collection_start(self, TokenClass): + + # '[' and '{' may start a simple key. + self.save_possible_simple_key() + + # Increase the flow level. + self.flow_level += 1 + + # Simple keys are allowed after '[' and '{'. + self.allow_simple_key = True + + # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_end(self): + self.fetch_flow_collection_end(FlowSequenceEndToken) + + def fetch_flow_mapping_end(self): + self.fetch_flow_collection_end(FlowMappingEndToken) + + def fetch_flow_collection_end(self, TokenClass): + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Decrease the flow level. + self.flow_level -= 1 + + # No simple keys after ']' or '}'. + self.allow_simple_key = False + + # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_entry(self): + + # Simple keys are allowed after ','. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add FLOW-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(FlowEntryToken(start_mark, end_mark)) + + def fetch_block_entry(self): + + # Block context needs additional checks. + if not self.flow_level: + + # Are we allowed to start a new entry? + if not self.allow_simple_key: + raise ScannerError(None, None, + "sequence entries are not allowed here", + self.get_mark()) + + # We may need to add BLOCK-SEQUENCE-START. + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockSequenceStartToken(mark, mark)) + + # It's an error for the block entry to occur in the flow context, + # but we let the parser detect this. + else: + pass + + # Simple keys are allowed after '-'. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add BLOCK-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(BlockEntryToken(start_mark, end_mark)) + + def fetch_key(self): + + # Block context needs additional checks. + if not self.flow_level: + + # Are we allowed to start a key (not nessesary a simple)? + if not self.allow_simple_key: + raise ScannerError(None, None, + "mapping keys are not allowed here", + self.get_mark()) + + # We may need to add BLOCK-MAPPING-START. + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockMappingStartToken(mark, mark)) + + # Simple keys are allowed after '?' in the block context. + self.allow_simple_key = not self.flow_level + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add KEY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(KeyToken(start_mark, end_mark)) + + def fetch_value(self): + + # Do we determine a simple key? + if self.flow_level in self.possible_simple_keys: + + # Add KEY. + key = self.possible_simple_keys[self.flow_level] + del self.possible_simple_keys[self.flow_level] + self.tokens.insert(key.token_number-self.tokens_taken, + KeyToken(key.mark, key.mark)) + + # If this key starts a new block mapping, we need to add + # BLOCK-MAPPING-START. + if not self.flow_level: + if self.add_indent(key.column): + self.tokens.insert(key.token_number-self.tokens_taken, + BlockMappingStartToken(key.mark, key.mark)) + + # There cannot be two simple keys one after another. + self.allow_simple_key = False + + # It must be a part of a complex key. + else: + + # Block context needs additional checks. + # (Do we really need them? They will be catched by the parser + # anyway.) + if not self.flow_level: + + # We are allowed to start a complex value if and only if + # we can start a simple key. + if not self.allow_simple_key: + raise ScannerError(None, None, + "mapping values are not allowed here", + self.get_mark()) + + # If this value starts a new block mapping, we need to add + # BLOCK-MAPPING-START. It will be detected as an error later by + # the parser. + if not self.flow_level: + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockMappingStartToken(mark, mark)) + + # Simple keys are allowed after ':' in the block context. + self.allow_simple_key = not self.flow_level + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add VALUE. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(ValueToken(start_mark, end_mark)) + + def fetch_alias(self): + + # ALIAS could be a simple key. + self.save_possible_simple_key() + + # No simple keys after ALIAS. + self.allow_simple_key = False + + # Scan and add ALIAS. + self.tokens.append(self.scan_anchor(AliasToken)) + + def fetch_anchor(self): + + # ANCHOR could start a simple key. + self.save_possible_simple_key() + + # No simple keys after ANCHOR. + self.allow_simple_key = False + + # Scan and add ANCHOR. + self.tokens.append(self.scan_anchor(AnchorToken)) + + def fetch_tag(self): + + # TAG could start a simple key. + self.save_possible_simple_key() + + # No simple keys after TAG. + self.allow_simple_key = False + + # Scan and add TAG. + self.tokens.append(self.scan_tag()) + + def fetch_literal(self): + self.fetch_block_scalar(style='|') + + def fetch_folded(self): + self.fetch_block_scalar(style='>') + + def fetch_block_scalar(self, style): + + # A simple key may follow a block scalar. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Scan and add SCALAR. + self.tokens.append(self.scan_block_scalar(style)) + + def fetch_single(self): + self.fetch_flow_scalar(style='\'') + + def fetch_double(self): + self.fetch_flow_scalar(style='"') + + def fetch_flow_scalar(self, style): + + # A flow scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after flow scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. + self.tokens.append(self.scan_flow_scalar(style)) + + def fetch_plain(self): + + # A plain scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after plain scalars. But note that `scan_plain` will + # change this flag if the scan is finished at the beginning of the + # line. + self.allow_simple_key = False + + # Scan and add SCALAR. May change `allow_simple_key`. + self.tokens.append(self.scan_plain()) + + # Checkers. + + def check_directive(self): + + # DIRECTIVE: ^ '%' ... + # The '%' indicator is already checked. + if self.column == 0: + return True + + def check_document_start(self): + + # DOCUMENT-START: ^ '---' (' '|'\n') + if self.column == 0: + if self.prefix(3) == u'---' \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return True + + def check_document_end(self): + + # DOCUMENT-END: ^ '...' (' '|'\n') + if self.column == 0: + if self.prefix(3) == u'...' \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return True + + def check_block_entry(self): + + # BLOCK-ENTRY: '-' (' '|'\n') + return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' + + def check_key(self): + + # KEY(flow context): '?' + if self.flow_level: + return True + + # KEY(block context): '?' (' '|'\n') + else: + return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' + + def check_value(self): + + # VALUE(flow context): ':' + if self.flow_level: + return True + + # VALUE(block context): ':' (' '|'\n') + else: + return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' + + def check_plain(self): + + # A plain scalar may start with any non-space character except: + # '-', '?', ':', ',', '[', ']', '{', '}', + # '#', '&', '*', '!', '|', '>', '\'', '\"', + # '%', '@', '`'. + # + # It may also start with + # '-', '?', ':' + # if it is followed by a non-space character. + # + # Note that we limit the last rule to the block context (except the + # '-' character) because we want the flow context to be space + # independent. + ch = self.peek() + return ch not in u'\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ + or (self.peek(1) not in u'\0 \t\r\n\x85\u2028\u2029' + and (ch == u'-' or (not self.flow_level and ch in u'?:'))) + + # Scanners. + + 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 : + # 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() + if self.peek() == u'#': + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + if self.scan_line_break(): + if not self.flow_level: + self.allow_simple_key = True + else: + found = True + + def scan_directive(self): + # See the specification for details. + start_mark = self.get_mark() + self.forward() + name = self.scan_directive_name(start_mark) + value = None + if name == u'YAML': + value = self.scan_yaml_directive_value(start_mark) + end_mark = self.get_mark() + elif name == u'TAG': + value = self.scan_tag_directive_value(start_mark) + end_mark = self.get_mark() + else: + end_mark = self.get_mark() + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + self.scan_directive_ignored_line(start_mark) + return DirectiveToken(name, value, start_mark, end_mark) + + def scan_directive_name(self, start_mark): + # See the specification for details. + length = 0 + ch = self.peek(length) + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_': + length += 1 + ch = self.peek(length) + if not length: + raise ScannerError("while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + value = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + return value + + def scan_yaml_directive_value(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + major = self.scan_yaml_directive_number(start_mark) + if self.peek() != '.': + raise ScannerError("while scanning a directive", start_mark, + "expected a digit or '.', but found %r" + % self.peek().encode('utf-8'), + self.get_mark()) + self.forward() + minor = self.scan_yaml_directive_number(start_mark) + if self.peek() not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected a digit or ' ', but found %r" + % self.peek().encode('utf-8'), + self.get_mark()) + return (major, minor) + + def scan_yaml_directive_number(self, start_mark): + # See the specification for details. + ch = self.peek() + if not (u'0' <= ch <= u'9'): + raise ScannerError("while scanning a directive", start_mark, + "expected a digit, but found %r" % ch.encode('utf-8'), + self.get_mark()) + length = 0 + while u'0' <= self.peek(length) <= u'9': + length += 1 + value = int(self.prefix(length)) + self.forward(length) + return value + + def scan_tag_directive_value(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + handle = self.scan_tag_directive_handle(start_mark) + while self.peek() == u' ': + self.forward() + prefix = self.scan_tag_directive_prefix(start_mark) + return (handle, prefix) + + def scan_tag_directive_handle(self, start_mark): + # See the specification for details. + value = self.scan_tag_handle('directive', start_mark) + ch = self.peek() + if ch != u' ': + raise ScannerError("while scanning a directive", start_mark, + "expected ' ', but found %r" % ch.encode('utf-8'), + self.get_mark()) + return value + + def scan_tag_directive_prefix(self, start_mark): + # See the specification for details. + value = self.scan_tag_uri('directive', start_mark) + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected ' ', but found %r" % ch.encode('utf-8'), + self.get_mark()) + return value + + def scan_directive_ignored_line(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + if self.peek() == u'#': + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + ch = self.peek() + if ch not in u'\0\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected a comment or a line break, but found %r" + % ch.encode('utf-8'), self.get_mark()) + self.scan_line_break() + + def scan_anchor(self, TokenClass): + # The specification does not restrict characters for anchors and + # aliases. This may lead to problems, for instance, the document: + # [ *alias, value ] + # can be interpteted in two ways, as + # [ "value" ] + # and + # [ *alias , "value" ] + # Therefore we restrict aliases to numbers and ASCII letters. + start_mark = self.get_mark() + indicator = self.peek() + if indicator == u'*': + name = 'alias' + else: + name = 'anchor' + self.forward() + length = 0 + ch = self.peek(length) + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_': + length += 1 + ch = self.peek(length) + if not length: + raise ScannerError("while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + value = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`': + raise ScannerError("while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + end_mark = self.get_mark() + return TokenClass(value, start_mark, end_mark) + + def scan_tag(self): + # See the specification for details. + start_mark = self.get_mark() + ch = self.peek(1) + if ch == u'<': + handle = None + self.forward(2) + suffix = self.scan_tag_uri('tag', start_mark) + if self.peek() != u'>': + raise ScannerError("while parsing a tag", start_mark, + "expected '>', but found %r" % self.peek().encode('utf-8'), + self.get_mark()) + self.forward() + elif ch in u'\0 \t\r\n\x85\u2028\u2029': + handle = None + suffix = u'!' + self.forward() + else: + length = 1 + use_handle = False + while ch not in u'\0 \r\n\x85\u2028\u2029': + if ch == u'!': + use_handle = True + break + length += 1 + ch = self.peek(length) + handle = u'!' + if use_handle: + handle = self.scan_tag_handle('tag', start_mark) + else: + handle = u'!' + self.forward() + suffix = self.scan_tag_uri('tag', start_mark) + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a tag", start_mark, + "expected ' ', but found %r" % ch.encode('utf-8'), + self.get_mark()) + value = (handle, suffix) + end_mark = self.get_mark() + return TagToken(value, start_mark, end_mark) + + def scan_block_scalar(self, style): + # See the specification for details. + + if style == '>': + folded = True + else: + folded = False + + chunks = [] + start_mark = self.get_mark() + + # Scan the header. + self.forward() + chomping, increment = self.scan_block_scalar_indicators(start_mark) + self.scan_block_scalar_ignored_line(start_mark) + + # Determine the indentation level and go to the first non-empty line. + min_indent = self.indent+1 + if min_indent < 1: + min_indent = 1 + if increment is None: + breaks, max_indent, end_mark = self.scan_block_scalar_indentation() + indent = max(min_indent, max_indent) + else: + indent = min_indent+increment-1 + breaks, end_mark = self.scan_block_scalar_breaks(indent) + line_break = u'' + + # Scan the inner part of the block scalar. + while self.column == indent and self.peek() != u'\0': + chunks.extend(breaks) + leading_non_space = self.peek() not in u' \t' + length = 0 + while self.peek(length) not in u'\0\r\n\x85\u2028\u2029': + length += 1 + chunks.append(self.prefix(length)) + self.forward(length) + line_break = self.scan_line_break() + breaks, end_mark = self.scan_block_scalar_breaks(indent) + if self.column == indent and self.peek() != u'\0': + + # Unfortunately, folding rules are ambiguous. + # + # This is the folding according to the specification: + + if folded and line_break == u'\n' \ + and leading_non_space and self.peek() not in u' \t': + if not breaks: + chunks.append(u' ') + else: + chunks.append(line_break) + + # This is Clark Evans's interpretation (also in the spec + # examples): + # + #if folded and line_break == u'\n': + # if not breaks: + # if self.peek() not in ' \t': + # chunks.append(u' ') + # else: + # chunks.append(line_break) + #else: + # chunks.append(line_break) + else: + break + + # Chomp the tail. + if chomping is not False: + chunks.append(line_break) + if chomping is True: + chunks.extend(breaks) + + # We are done. + return ScalarToken(u''.join(chunks), False, start_mark, end_mark, + style) + + def scan_block_scalar_indicators(self, start_mark): + # See the specification for details. + chomping = None + increment = None + ch = self.peek() + if ch in u'+-': + if ch == '+': + chomping = True + else: + chomping = False + self.forward() + ch = self.peek() + if ch in u'0123456789': + increment = int(ch) + if increment == 0: + raise ScannerError("while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark()) + self.forward() + elif ch in u'0123456789': + increment = int(ch) + if increment == 0: + raise ScannerError("while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark()) + self.forward() + ch = self.peek() + if ch in u'+-': + if ch == '+': + chomping = True + else: + chomping = False + self.forward() + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a block scalar", start_mark, + "expected chomping or indentation indicators, but found %r" + % ch.encode('utf-8'), self.get_mark()) + return chomping, increment + + def scan_block_scalar_ignored_line(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + if self.peek() == u'#': + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + ch = self.peek() + if ch not in u'\0\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a block scalar", start_mark, + "expected a comment or a line break, but found %r" + % ch.encode('utf-8'), self.get_mark()) + self.scan_line_break() + + def scan_block_scalar_indentation(self): + # See the specification for details. + chunks = [] + max_indent = 0 + end_mark = self.get_mark() + while self.peek() in u' \r\n\x85\u2028\u2029': + if self.peek() != u' ': + chunks.append(self.scan_line_break()) + end_mark = self.get_mark() + else: + self.forward() + if self.column > max_indent: + max_indent = self.column + return chunks, max_indent, end_mark + + def scan_block_scalar_breaks(self, indent): + # See the specification for details. + chunks = [] + end_mark = self.get_mark() + while self.column < indent and self.peek() == u' ': + self.forward() + while self.peek() in u'\r\n\x85\u2028\u2029': + chunks.append(self.scan_line_break()) + end_mark = self.get_mark() + while self.column < indent and self.peek() == u' ': + self.forward() + return chunks, end_mark + + def scan_flow_scalar(self, style): + # See the specification for details. + # Note that we loose indentation rules for quoted scalars. Quoted + # scalars don't need to adhere indentation because " and ' clearly + # mark the beginning and the end of them. Therefore we are less + # restrictive then the specification requires. We only need to check + # that document separators are not included in scalars. + if style == '"': + double = True + else: + double = False + chunks = [] + start_mark = self.get_mark() + quote = self.peek() + self.forward() + chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) + while self.peek() != quote: + chunks.extend(self.scan_flow_scalar_spaces(double, start_mark)) + chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) + self.forward() + end_mark = self.get_mark() + return ScalarToken(u''.join(chunks), False, start_mark, end_mark, + style) + + ESCAPE_REPLACEMENTS = { + u'0': u'\0', + u'a': u'\x07', + u'b': u'\x08', + u't': u'\x09', + u'\t': u'\x09', + u'n': u'\x0A', + u'v': u'\x0B', + u'f': u'\x0C', + u'r': u'\x0D', + u'e': u'\x1B', + u' ': u'\x20', + u'\"': u'\"', + u'\\': u'\\', + u'N': u'\x85', + u'_': u'\xA0', + u'L': u'\u2028', + u'P': u'\u2029', + } + + ESCAPE_CODES = { + u'x': 2, + u'u': 4, + u'U': 8, + } + + def scan_flow_scalar_non_spaces(self, double, start_mark): + # See the specification for details. + chunks = [] + while True: + length = 0 + while self.peek(length) not in u'\'\"\\\0 \t\r\n\x85\u2028\u2029': + length += 1 + if length: + chunks.append(self.prefix(length)) + self.forward(length) + ch = self.peek() + if not double and ch == u'\'' and self.peek(1) == u'\'': + chunks.append(u'\'') + self.forward(2) + elif (double and ch == u'\'') or (not double and ch in u'\"\\'): + chunks.append(ch) + self.forward() + elif double and ch == u'\\': + self.forward() + ch = self.peek() + if ch in self.ESCAPE_REPLACEMENTS: + chunks.append(self.ESCAPE_REPLACEMENTS[ch]) + self.forward() + elif ch in self.ESCAPE_CODES: + length = self.ESCAPE_CODES[ch] + self.forward() + for k in range(length): + if self.peek(k) not in u'0123456789ABCDEFabcdef': + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexdecimal numbers, but found %r" % + (length, self.peek(k).encode('utf-8')), self.get_mark()) + code = int(self.prefix(length), 16) + chunks.append(unichr(code)) + self.forward(length) + elif ch in u'\r\n\x85\u2028\u2029': + self.scan_line_break() + chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) + else: + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "found unknown escape character %r" % ch.encode('utf-8'), self.get_mark()) + else: + return chunks + + def scan_flow_scalar_spaces(self, double, start_mark): + # See the specification for details. + chunks = [] + length = 0 + while self.peek(length) in u' \t': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch == u'\0': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark()) + elif ch in u'\r\n\x85\u2028\u2029': + line_break = self.scan_line_break() + breaks = self.scan_flow_scalar_breaks(double, start_mark) + if line_break != u'\n': + chunks.append(line_break) + elif not breaks: + chunks.append(u' ') + chunks.extend(breaks) + else: + chunks.append(whitespaces) + return chunks + + def scan_flow_scalar_breaks(self, double, start_mark): + # See the specification for details. + chunks = [] + while True: + # Instead of checking indentation, we check for document + # separators. + prefix = self.prefix(3) + if (prefix == u'---' or prefix == u'...') \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected document separator", self.get_mark()) + while self.peek() in u' \t': + self.forward() + if self.peek() in u'\r\n\x85\u2028\u2029': + chunks.append(self.scan_line_break()) + else: + return chunks + + def scan_plain(self): + # See the specification for details. + # We add an additional restriction for the flow context: + # plain scalars in the flow context cannot contain ',', ':' and '?'. + # We also keep track of the `allow_simple_key` flag here. + # Indentation rules are loosed for the flow context. + chunks = [] + start_mark = self.get_mark() + end_mark = start_mark + indent = self.indent+1 + # We allow zero indentation for scalars, but then we need to check for + # document separators at the beginning of the line. + #if indent == 0: + # indent = 1 + spaces = [] + while True: + length = 0 + if self.peek() == u'#': + break + while True: + ch = self.peek(length) + if ch in u'\0 \t\r\n\x85\u2028\u2029' \ + or (not self.flow_level and ch == u':' and + self.peek(length+1) in u'\0 \t\r\n\x85\u2028\u2029') \ + or (self.flow_level and ch in u',:?[]{}'): + break + length += 1 + # It's not clear what we should do with ':' in the flow context. + if (self.flow_level and ch == u':' + and self.peek(length+1) not in u'\0 \t\r\n\x85\u2028\u2029,[]{}'): + self.forward(length) + raise ScannerError("while scanning a plain scalar", start_mark, + "found unexpected ':'", self.get_mark(), + "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.") + if length == 0: + break + self.allow_simple_key = False + chunks.extend(spaces) + chunks.append(self.prefix(length)) + self.forward(length) + end_mark = self.get_mark() + spaces = self.scan_plain_spaces(indent, start_mark) + if not spaces or self.peek() == u'#' \ + or (not self.flow_level and self.column < indent): + break + return ScalarToken(u''.join(chunks), True, start_mark, end_mark) + + def scan_plain_spaces(self, indent, start_mark): + # See the specification for details. + # The specification is really confusing about tabs in plain scalars. + # We just forbid them completely. Do not use tabs in YAML! + chunks = [] + length = 0 + while self.peek(length) in u' ': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch in u'\r\n\x85\u2028\u2029': + line_break = self.scan_line_break() + self.allow_simple_key = True + prefix = self.prefix(3) + if (prefix == u'---' or prefix == u'...') \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return + breaks = [] + while self.peek() in u' \r\n\x85\u2028\u2029': + if self.peek() == ' ': + self.forward() + else: + breaks.append(self.scan_line_break()) + prefix = self.prefix(3) + if (prefix == u'---' or prefix == u'...') \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return + if line_break != u'\n': + chunks.append(line_break) + elif not breaks: + chunks.append(u' ') + chunks.extend(breaks) + elif whitespaces: + chunks.append(whitespaces) + return chunks + + def scan_tag_handle(self, name, start_mark): + # See the specification for details. + # For some strange reasons, the specification does not allow '_' in + # tag handles. I have allowed it anyway. + ch = self.peek() + if ch != u'!': + raise ScannerError("while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch.encode('utf-8'), + self.get_mark()) + length = 1 + ch = self.peek(length) + if ch != u' ': + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_': + length += 1 + ch = self.peek(length) + if ch != u'!': + self.forward(length) + raise ScannerError("while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch.encode('utf-8'), + self.get_mark()) + length += 1 + value = self.prefix(length) + self.forward(length) + return value + + def scan_tag_uri(self, name, start_mark): + # See the specification for details. + # Note: we do not check if URI is well-formed. + chunks = [] + length = 0 + ch = self.peek(length) + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-;/?:@&=+$,_.!~*\'()[]%': + if ch == u'%': + chunks.append(self.prefix(length)) + self.forward(length) + length = 0 + chunks.append(self.scan_uri_escapes(name, start_mark)) + else: + length += 1 + ch = self.peek(length) + if length: + chunks.append(self.prefix(length)) + self.forward(length) + length = 0 + if not chunks: + raise ScannerError("while parsing a %s" % name, start_mark, + "expected URI, but found %r" % ch.encode('utf-8'), + self.get_mark()) + return u''.join(chunks) + + def scan_uri_escapes(self, name, start_mark): + # See the specification for details. + bytes = [] + mark = self.get_mark() + while self.peek() == u'%': + self.forward() + for k in range(2): + if self.peek(k) not in u'0123456789ABCDEFabcdef': + raise ScannerError("while scanning a %s" % name, start_mark, + "expected URI escape sequence of 2 hexdecimal numbers, but found %r" % + (self.peek(k).encode('utf-8')), self.get_mark()) + bytes.append(chr(int(self.prefix(2), 16))) + self.forward(2) + try: + value = unicode(''.join(bytes), 'utf-8') + except UnicodeDecodeError, exc: + raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) + return value + + def scan_line_break(self): + # Transforms: + # '\r\n' : '\n' + # '\r' : '\n' + # '\n' : '\n' + # '\x85' : '\n' + # '\u2028' : '\u2028' + # '\u2029 : '\u2029' + # default : '' + ch = self.peek() + if ch in u'\r\n\x85': + if self.prefix(2) == u'\r\n': + self.forward(2) + else: + self.forward() + return u'\n' + elif ch in u'\u2028\u2029': + self.forward() + return ch + return u'' + +#try: +# import psyco +# psyco.bind(Scanner) +#except ImportError: +# pass + diff --git a/libs/py2/yaml/serializer.py b/libs/py2/yaml/serializer.py new file mode 100644 index 00000000..0bf1e96d --- /dev/null +++ b/libs/py2/yaml/serializer.py @@ -0,0 +1,111 @@ + +__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() + diff --git a/libs/py2/yaml/tokens.py b/libs/py2/yaml/tokens.py new file mode 100644 index 00000000..4d0b48a3 --- /dev/null +++ b/libs/py2/yaml/tokens.py @@ -0,0 +1,104 @@ + +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 = '' + +class DirectiveToken(Token): + id = '' + 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 = '' + +class DocumentEndToken(Token): + id = '' + +class StreamStartToken(Token): + id = '' + 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 = '' + +class BlockSequenceStartToken(Token): + id = '' + +class BlockMappingStartToken(Token): + id = '' + +class BlockEndToken(Token): + id = '' + +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 = '' + 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 = '' + 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 = '' + 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 = '' + 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 + diff --git a/libs/requirements-py2.txt b/libs/requirements-py2.txt index ca7d7e12..6cf3358d 100644 --- a/libs/requirements-py2.txt +++ b/libs/requirements-py2.txt @@ -2,3 +2,4 @@ backports.functools-lru-cache beautifulsoup4 enum34 futures +pyyaml