Kirill Simonov avatar Kirill Simonov committed 98cc99c

Implement yaml.dump().

Comments (0)

Files changed (15)

lib/yaml/__init__.py

 
 from error import *
+
 from reader import *
 from scanner import *
 from parser import *
 from composer import *
 from resolver import *
 from constructor import *
+
 from emitter import *
+from serializer import *
+from representer import *
+
+from detector import *
 
 from tokens import *
 from events import *
 from nodes import *
 
+from yaml_object import *
+
 def parse(data, Reader=Reader, Scanner=Scanner, Parser=Parser):
     reader = Reader(data)
     scanner = Scanner(reader)
     parser = Parser(scanner)
     return parser
 
-def load(data, Reader=Reader, Scanner=Scanner, Parser=Parser,
+def load_all(data, Reader=Reader, Scanner=Scanner, Parser=Parser,
         Composer=Composer, Resolver=Resolver, Constructor=Constructor):
     reader = Reader(data)
     scanner = Scanner(reader)
     constructor = Constructor(resolver)
     return constructor
 
-def load_document(*args, **kwds):
-    for document in load(*args, **kwds):
+def safe_load_all(data, Reader=Reader, Scanner=Scanner, Parser=Parser,
+        Composer=Composer, Resolver=Resolver, Constructor=SafeConstructor):
+    return load_all(data, Reader, Scanner, Parser, Composer, Resolver,
+            Constructor)
+
+def load(data, *args, **kwds):
+    for document in load_all(data, *args, **kwds):
         return document
 
+def safe_load(data, *args, **kwds):
+    for document in safe_load_all(data, *args, **kwds):
+        return document
+
+def emit(events, writer=None, Emitter=Emitter):
+    if writer is None:
+        try:
+            from cStringIO import StringIO
+        except ImportError:
+            from StringIO import StringIO
+        writer = StringIO()
+        return_value = True
+    else:
+        return_value = False
+    emitter = Emitter(writer)
+    for event in events:
+        emitter.emit(event)
+    if return_value:
+        return writer.getvalue()
+
+def dump_all(natives, writer=None, Emitter=Emitter,
+        Serializer=Serializer, Representer=Representer,
+        encoding=None, line_break=None, canonical=None,
+        indent=None, width=None, allow_unicode=None):
+    if writer is None:
+        try:
+            from cStringIO import StringIO
+        except ImportError:
+            from StringIO import StringIO
+        writer = StringIO()
+        return_value = True
+    else:
+        return_value = False
+    emitter = Emitter(writer)
+    serializer = Serializer(emitter, encoding=encoding, line_break=line_break,
+            canonical=canonical, indent=indent, width=width,
+            allow_unicode=allow_unicode)
+    representer = Representer(serializer)
+    for native in natives:
+        representer.represent(native)
+    representer.close()
+    if return_value:
+        return writer.getvalue()
+
+def safe_dump_all(natives, writer=None, Emitter=Emitter,
+        Serializer=Serializer, Representer=SafeRepresenter,
+        encoding=None, line_break=None, canonical=None,
+        indent=None, width=None, allow_unicode=None):
+    return dump_all(natives, writer, Emitter, Serializer, Representer,
+            encoding, line_break, canonical, indent, width, allow_unicode)
+
+def dump(native, *args, **kwds):
+    return dump_all([native], *args, **kwds)
+
+def safe_dump(native, *args, **kwds):
+    return safe_dump_all([native], *args, **kwds)
+

lib/yaml/composer.py

     def compose_scalar_node(self):
         event = self.parser.get()
         return ScalarNode(event.tag, event.value, event.implicit,
-                event.start_mark, event.end_mark)
+                event.start_mark, event.end_mark, style=event.style)
 
     def compose_sequence_node(self):
         start_event = self.parser.get()
             value.append(self.compose_node())
         end_event = self.parser.get()
         return SequenceNode(start_event.tag, value,
-                start_event.start_mark, end_event.end_mark)
+                start_event.start_mark, end_event.end_mark,
+                flow_style=start_event.flow_style)
 
     def compose_mapping_node(self):
         start_event = self.parser.get()
             value[item_key] = item_value
         end_event = self.parser.get()
         return MappingNode(start_event.tag, value,
-                start_event.start_mark, end_event.end_mark)
+                start_event.start_mark, end_event.end_mark,
+                flow_style=start_event.flow_style)
 

lib/yaml/constructor.py

 
-__all__ = ['BaseConstructor', 'Constructor', 'ConstructorError',
-    'YAMLObject', 'YAMLObjectMetaclass']
+__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
+    'ConstructorError']
 
 from error import *
 from nodes import *
 
     yaml_constructors = {}
 
-class Constructor(BaseConstructor):
+class SafeConstructor(BaseConstructor):
 
     def construct_yaml_null(self, node):
         self.construct_scalar(node)
                 "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
                 node.start_mark)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:null',
-        Constructor.construct_yaml_null)
+        SafeConstructor.construct_yaml_null)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:bool',
-        Constructor.construct_yaml_bool)
+        SafeConstructor.construct_yaml_bool)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:int',
-        Constructor.construct_yaml_int)
+        SafeConstructor.construct_yaml_int)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:float',
-        Constructor.construct_yaml_float)
+        SafeConstructor.construct_yaml_float)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:binary',
-        Constructor.construct_yaml_binary)
+        SafeConstructor.construct_yaml_binary)
 
 if datetime_available:
-    Constructor.add_constructor(
+    SafeConstructor.add_constructor(
             u'tag:yaml.org,2002:timestamp',
-            Constructor.construct_yaml_timestamp)
+            SafeConstructor.construct_yaml_timestamp)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:omap',
-        Constructor.construct_yaml_omap)
+        SafeConstructor.construct_yaml_omap)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:pairs',
-        Constructor.construct_yaml_pairs)
+        SafeConstructor.construct_yaml_pairs)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:set',
-        Constructor.construct_yaml_set)
+        SafeConstructor.construct_yaml_set)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:str',
-        Constructor.construct_yaml_str)
+        SafeConstructor.construct_yaml_str)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:seq',
-        Constructor.construct_yaml_seq)
+        SafeConstructor.construct_yaml_seq)
 
-Constructor.add_constructor(
+SafeConstructor.add_constructor(
         u'tag:yaml.org,2002:map',
-        Constructor.construct_yaml_map)
+        SafeConstructor.construct_yaml_map)
 
-Constructor.add_constructor(None,
-        Constructor.construct_undefined)
+SafeConstructor.add_constructor(None,
+        SafeConstructor.construct_undefined)
 
-class YAMLObjectMetaclass(type):
+class Constructor(SafeConstructor):
+    pass
 
-    def __init__(cls, name, bases, kwds):
-        super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
-        if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
-            cls.yaml_constructor.add_constructor(cls.yaml_tag, cls.from_yaml)
-
-class YAMLObject(object):
-
-    __metaclass__ = YAMLObjectMetaclass
-
-    yaml_constructor = Constructor
-
-    yaml_tag = None
-
-    def from_yaml(cls, constructor, node):
-        raise ConstructorError(None, None,
-                "found undefined constructor for the tag %r"
-                % node.tag.encode('utf-8'), node.start_mark)
-    from_yaml = classmethod(from_yaml)
-
-    def to_yaml(self):
-        assert False    # needs dumper
-

lib/yaml/detector.py

+
+__all__ = ['BaseDetector', 'Detector']
+
+import re
+
+class BaseDetector:
+
+    yaml_detectors = {}
+
+    def add_detector(cls, tag, regexp, first):
+        if not 'yaml_detectors' in cls.__dict__:
+            cls.yaml_detectors = cls.yaml_detectors.copy()
+        for ch in first:
+            cls.yaml_detectors.setdefault(ch, []).append((tag, regexp))
+    add_detector = classmethod(add_detector)
+
+    def detect(self, value):
+        if value == u'':
+            detectors = self.yaml_detectors.get(u'', [])
+        else:
+            detectors = self.yaml_detectors.get(value[0], [])
+        detectors += self.yaml_detectors.get(None, [])
+        for tag, regexp in detectors:
+            if regexp.match(value):
+                return tag
+
+class Detector(BaseDetector):
+    pass
+
+Detector.add_detector(
+        u'tag:yaml.org,2002:bool',
+        re.compile(ur'''^(?:yes|Yes|YES|n|N|no|No|NO
+                    |true|True|TRUE|false|False|FALSE
+                    |on|On|ON|off|Off|OFF)$''', re.X),
+        list(u'yYnNtTfFoO'))
+
+Detector.add_detector(
+        u'tag:yaml.org,2002:float',
+        re.compile(ur'''^(?:[-+]?(?:[0-9][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.'))
+
+Detector.add_detector(
+        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'))
+
+Detector.add_detector(
+        u'tag:yaml.org,2002:merge',
+        re.compile(ur'^(?:<<)$'),
+        ['<'])
+
+Detector.add_detector(
+        u'tag:yaml.org,2002:null',
+        re.compile(ur'''^(?: ~
+                    |null|Null|NULL
+                    | )$''', re.X),
+        [u'~', u'n', u'N', u''])
+
+Detector.add_detector(
+        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'))
+
+Detector.add_detector(
+        u'tag:yaml.org,2002:value',
+        re.compile(ur'^(?:=)$'),
+        ['='])
+
+# The following detector is only for documentation purposes. It cannot work
+# because plain scalars cannot start with '!', '&', or '*'.
+Detector.add_detector(
+        u'tag:yaml.org,2002:yaml',
+        re.compile(ur'^(?:!|&|\*)$'),
+        list(u'!&*'))
+

lib/yaml/emitter.py

 
         # Formatting details.
         self.canonical = False
+        self.allow_unicode = False
         self.best_line_break = u'\n'
         self.best_indent = 2
         self.best_width = 80
         if isinstance(self.event, StreamStartEvent):
             self.encoding = self.event.encoding
             self.canonical = self.event.canonical
+            self.allow_unicode = self.event.allow_unicode
             if self.event.indent and self.event.indent > 1:
                 self.best_indent = self.event.indent
             if self.event.width and self.event.width > self.best_indent:
                     contains_flow_indicator = True
                     if followed_by_space or last:
                         contains_block_indicator = True
-                if ch == u'-' and followed_by_space or last:
+                if ch == u'-' and (followed_by_space or last):
                     contains_flow_indicator = True
                     contains_block_indicator = True
             else:
                     contains_flow_indicator = True
                     if followed_by_space or last:
                         contains_block_indicator = True
-                if ch == u'#' and preceeded_by_space:
+                if ch == u'#' and (preceeded_by_space or first):
                     contains_flow_indicator = True
                     contains_block_indicator = True
             if ch in u'\n\x85\u2028\u2029':
                 if ch < u'\x80':
                     contains_special_characters = True
                 else:
-                    contains_special_characters = True
-                    # TODO: We need an option to allow unescaped unicode
-                    # characters.
                     contains_unicode_characters = True
             if ch == u' ':
                 if not spaces and not breaks:
             preceeded_by_space = (ch in u'\0 \t\r\n\x85\u2028\u2029')
             followed_by_space = (index+1 < len(scalar) and
                     scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029')
+        if contains_unicode_characters and not self.allow_unicode:
+            contains_special_characters = True
         allow_flow_plain = not (contains_flow_indicator or contains_special_characters
             or contains_leading_spaces or contains_leading_breaks
             or contains_trailing_spaces or contains_trailing_breaks
             ch = None
             if end < len(text):
                 ch = text[end]
-            if ch is None or not (u'\x20' <= ch <= u'\x7E') or ch in u'"\\':
+            if ch is None or ch in u'"\\'   \
+                    or not (u'\x20' <= ch <= u'\x7E'
+                            or (self.allow_unicode and ch > u'\x7F'
+                                and ch not in u'\x85\u2028\u2029')):
                 if start < end:
                     data = text[start:end]
                     self.column += len(data)

lib/yaml/events.py

 class StreamStartEvent(Event):
     def __init__(self, start_mark=None, end_mark=None,
             encoding=None, line_break=None, canonical=None,
-            indent=None, width=None):
+            indent=None, width=None, allow_unicode=None):
         self.start_mark = start_mark
         self.end_mark = end_mark
         self.encoding = encoding
         self.canonical = canonical
         self.indent = indent
         self.width = width
+        self.allow_unicode = allow_unicode
 
 class StreamEndEvent(Event):
     pass

lib/yaml/nodes.py

 
 class ScalarNode(Node):
     id = 'scalar'
-    def __init__(self, tag, value, implicit, start_mark, end_mark):
+    def __init__(self, tag, value, implicit,
+            start_mark=None, end_mark=None, style=None):
         self.tag = tag
         self.value = value
         self.implicit = implicit
         self.start_mark = start_mark
         self.end_mark = end_mark
+        self.style = style
 
 class CollectionNode(Node):
-    pass
+    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'

lib/yaml/representer.py

+
+__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
+    'RepresenterError']
+
+from error import *
+from nodes import *
+from detector import *
+
+try:
+    import datetime
+    datetime_available = True
+except ImportError:
+    datetime_available = False
+
+try:
+    set
+except NameError:
+    from sets import Set as set
+
+class RepresenterError(YAMLError):
+    pass
+
+class BaseRepresenter(BaseDetector):
+
+    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'
+
+    def __init__(self, serializer):
+        self.serializer = serializer
+        self.represented_objects = {}
+
+    def close(self):
+        self.serializer.close()
+
+    def represent(self, native):
+        node = self.represent_object(native)
+        self.serializer.serialize(node)
+        self.represented_objects = {}
+
+    def represent_object(self, native):
+        if self.ignore_aliases(native):
+            alias_key = None
+        else:
+            alias_key = id(native)
+        if alias_key is not None:
+            if alias_key in self.represented_objects:
+                node = self.represented_objects[alias_key]
+                if node is None:
+                    raise RepresenterError("recursive objects are not allowed: %r" % native)
+                return node
+            self.represented_objects[alias_key] = None
+        for native_type in type(native).__mro__:
+            if native_type in self.yaml_representers:
+                node = self.yaml_representers[native_type](self, native)
+                break
+        else:
+            if None in self.yaml_representers:
+                node = self.yaml_representers[None](self, native)
+            else:
+                node = ScalarNode(None, unicode(native))
+        if alias_key is not None:
+            self.represented_objects[alias_key] = node
+        return node
+
+    def add_representer(cls, native_type, representer):
+        if not 'yaml_representers' in cls.__dict__:
+            cls.yaml_representers = cls.yaml_representers.copy()
+        cls.yaml_representers[native_type] = representer
+    add_representer = classmethod(add_representer)
+
+    yaml_representers = {}
+
+    def represent_scalar(self, tag, value, style=None):
+        detected_tag = self.detect(value)
+        if detected_tag is None:
+            detected_tag = self.DEFAULT_SCALAR_TAG
+        implicit = (tag == detected_tag)
+        if tag == self.DEFAULT_SCALAR_TAG:
+            tag = None
+        return ScalarNode(tag, value, implicit=implicit, style=style)
+
+    def represent_sequence(self, tag, sequence, flow_style=None):
+        if tag == self.DEFAULT_SEQUENCE_TAG:
+            tag = None
+        value = []
+        for item in sequence:
+            value.append(self.represent_object(item))
+        return SequenceNode(tag, value, flow_style=flow_style)
+
+    def represent_mapping(self, tag, mapping, flow_style=None):
+        if tag == self.DEFAULT_MAPPING_TAG:
+            tag = None
+        value = {}
+        if hasattr(mapping, 'keys'):
+            for item_key in mapping.keys():
+                item_value = mapping[item_key]
+                value[self.represent_object(item_key)] =    \
+                        self.represent_object(item_value)
+        else:
+            for item_key, item_value in mapping:
+                value[self.represent_object(item_key)] =    \
+                        self.represent_object(item_value)
+        return MappingNode(tag, value, flow_style=flow_style)
+
+    def ignore_aliases(self, native):
+        return False
+
+class SafeRepresenter(Detector, BaseRepresenter):
+
+    def ignore_aliases(self, native):
+        if native in [None, ()]:
+            return True
+        if isinstance(native, (str, unicode, bool, int, float)):
+            return True
+
+    def represent_none(self, native):
+        return self.represent_scalar(u'tag:yaml.org,2002:null',
+                u'null')
+
+    def represent_str(self, native):
+        try:
+            native.encode('ascii')
+            ascii = True
+        except (UnicodeDecodeError, UnicodeEncodeError):
+            ascii = False
+        if ascii:
+            return self.represent_scalar(u'tag:yaml.org,2002:str',
+                    unicode(native, 'ascii'))
+        else:
+            return self.represent_scalar(u'tag:yaml.org,2002:binary',
+                    unicode(native.encode('base64')), style='|')
+
+    def represent_unicode(self, native):
+        return self.represent_scalar(u'tag:yaml.org,2002:str', native)
+
+    def represent_bool(self, native):
+        if native:
+            value = u'true'
+        else:
+            value = u'false'
+        return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
+
+    def represent_int(self, native):
+        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(native))
+
+    def represent_long(self, native):
+        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(native))
+
+    inf_value = 1e300000
+    nan_value = inf_value/inf_value
+
+    def represent_float(self, native):
+        if native == self.inf_value:
+            value = u'.inf'
+        elif native == -self.inf_value:
+            value = u'-.inf'
+        elif native == self.nan_value or native != native:
+            value = u'.nan'
+        else:
+            value = unicode(native)
+        return self.represent_scalar(u'tag:yaml.org,2002:float', value)
+
+    def represent_list(self, native):
+        pairs = (len(native) > 0)
+        for item in native:
+            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', native)
+        value = []
+        for item_key, item_value in native:
+            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, native):
+        return self.represent_mapping(u'tag:yaml.org,2002:map', native)
+
+    def represent_set(self, native):
+        value = {}
+        for key in native:
+            value[key] = None
+        return self.represent_mapping(u'tag:yaml.org,2002:set', value)
+
+    def represent_date(self, native):
+        value = u'%04d-%02d-%02d' % (native.year, native.month, native.day)
+        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
+
+    def represent_datetime(self, native):
+        value = u'%04d-%02d-%02d %02d:%02d:%02d' \
+                % (native.year, native.month, native.day,
+                    native.hour, native.minute, native.second)
+        if native.microsecond:
+            value += u'.' + unicode(native.microsecond/1000000.0).split(u'.')[1]
+        if native.utcoffset():
+            value += unicode(native.utcoffset())
+        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
+
+    def represent_undefined(self, native):
+        raise RepresenterError("cannot represent an object: %s" % native)
+
+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(dict,
+        SafeRepresenter.represent_dict)
+
+SafeRepresenter.add_representer(set,
+        SafeRepresenter.represent_set)
+
+if datetime_available:
+    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):
+    pass
+

lib/yaml/resolver.py

 
-__all__ = ['BaseResolver', 'Resolver', 'ResolverError']
+__all__ = ['Resolver', 'ResolverError']
 
 from error import MarkedYAMLError
+from detector import Detector
 from nodes import *
 
 import re
 class ResolverError(MarkedYAMLError):
     pass
 
-class BaseResolver:
+class Resolver(Detector):
 
     DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
     DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
 
     def resolve_scalar(self, path, node):
         if node.tag is None and node.implicit:
-            node.tag = self.detect_scalar(node.value)
+            node.tag = self.detect(node.value)
         if node.tag is None or node.tag == u'!':
             node.tag = self.DEFAULT_SCALAR_TAG
 
         if node.tag is None or node.tag == u'!':
             node.tag = self.DEFAULT_MAPPING_TAG
 
-    def detect_scalar(self, value):
-        if value == u'':
-            detectors = self.yaml_detectors.get(u'', [])
-        else:
-            detectors = self.yaml_detectors.get(value[0], [])
-        detectors += self.yaml_detectors.get(None, [])
-        for tag, regexp in detectors:
-            if regexp.match(value):
-                return tag
-
-    def add_detector(cls, tag, regexp, first):
-        if not 'yaml_detectors' in cls.__dict__:
-            cls.yaml_detectors = cls.yaml_detectors.copy()
-        for ch in first:
-            cls.yaml_detectors.setdefault(ch, []).append((tag, regexp))
-    add_detector = classmethod(add_detector)
-
-    yaml_detectors = {}
-
-class Resolver(BaseResolver):
-    pass
-
-Resolver.add_detector(
-        u'tag:yaml.org,2002:bool',
-        re.compile(ur'''^(?:yes|Yes|YES|n|N|no|No|NO
-                    |true|True|TRUE|false|False|FALSE
-                    |on|On|ON|off|Off|OFF)$''', re.X),
-        list(u'yYnNtTfFoO'))
-
-Resolver.add_detector(
-        u'tag:yaml.org,2002:float',
-        re.compile(ur'''^(?:[-+]?(?:[0-9][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_detector(
-        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_detector(
-        u'tag:yaml.org,2002:merge',
-        re.compile(ur'^(?:<<)$'),
-        ['<'])
-
-Resolver.add_detector(
-        u'tag:yaml.org,2002:null',
-        re.compile(ur'''^(?: ~
-                    |null|Null|NULL
-                    | )$''', re.X),
-        [u'~', u'n', u'N', u''])
-
-Resolver.add_detector(
-        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_detector(
-        u'tag:yaml.org,2002:value',
-        re.compile(ur'^(?:=)$'),
-        ['='])
-
-# The following detector is only for documentation purposes. It cannot work
-# because plain scalars cannot start with '!', '&', or '*'.
-Resolver.add_detector(
-        u'tag:yaml.org,2002:yaml',
-        re.compile(ur'^(?:!|&|\*)$'),
-        list(u'!&*'))
-

lib/yaml/serializer.py

+
+__all__ = ['Serializer', 'SerializerError']
+
+from error import YAMLError
+from events import *
+from nodes import *
+
+class SerializerError(YAMLError):
+    pass
+
+class Serializer:
+
+    ANCHOR_TEMPLATE = u'id%03d'
+
+    def __init__(self, emitter, encoding=None, line_break=None, canonical=None,
+            indent=None, width=None, allow_unicode=None):
+        self.emitter = emitter
+        self.serialized_nodes = {}
+        self.anchors = {}
+        self.last_anchor_id = 0
+        self.closed = None
+        self.open(encoding, line_break, canonical, indent, width)
+
+    def open(self, encoding=None, line_break=None, canonical=None,
+            indent=None, width=None, allow_unicode=None):
+        if self.closed is None:
+            self.emitter.emit(StreamStartEvent(encoding=encoding,
+                line_break=line_break, canonical=canonical,
+                indent=indent, width=width, allow_unicode=allow_unicode))
+            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.emitter.emit(StreamEndEvent())
+            self.closed = True
+
+    def __del__(self):
+        self.close()
+
+    def serialize(self, node, explicit_start=None, explicit_end=None,
+            version=None, tags=None):
+        if self.closed is None:
+            raise SerializerError("serializer is not opened")
+        elif self.closed:
+            raise SerializerError("serializer is closed")
+        self.emitter.emit(DocumentStartEvent(explicit=explicit_start,
+            version=version, tags=tags))
+        self.anchor_node(node)
+        self.serialize_node(node)
+        self.emitter.emit(DocumentEndEvent(explicit=explicit_end))
+        self.serialized_nodes = {}
+        self.anchors = {}
+        self.last_alias_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 in node.value:
+                    self.anchor_node(key)
+                    self.anchor_node(node.value[key])
+
+    def generate_anchor(self, node):
+        self.last_anchor_id += 1
+        return self.ANCHOR_TEMPLATE % self.last_anchor_id
+
+    def serialize_node(self, node):
+        alias = self.anchors[node]
+        if node in self.serialized_nodes:
+            self.emitter.emit(AliasEvent(alias))
+        else:
+            self.serialized_nodes[node] = True
+            if isinstance(node, ScalarNode):
+                self.emitter.emit(ScalarEvent(alias, node.tag, node.value,
+                    implicit=node.implicit, style=node.style))
+            elif isinstance(node, SequenceNode):
+                self.emitter.emit(SequenceStartEvent(alias, node.tag,
+                    flow_style=node.flow_style))
+                for item in node.value:
+                    self.serialize_node(item)
+                self.emitter.emit(SequenceEndEvent())
+            elif isinstance(node, MappingNode):
+                self.emitter.emit(MappingStartEvent(alias, node.tag,
+                    flow_style=node.flow_style))
+                for key in node.value:
+                    self.serialize_node(key)
+                    self.serialize_node(node.value[key])
+                self.emitter.emit(MappingEndEvent())
+

lib/yaml/yaml_object.py

+
+__all__ = ['YAMLObject', 'YAMLObjectMetaclass']
+
+from constructor import *
+from representer import *
+
+class YAMLObjectMetaclass(type):
+
+    def __init__(cls, name, bases, kwds):
+        super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
+        if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
+            cls.yaml_constructor.add_constructor(cls.yaml_tag, cls.from_yaml)
+            cls.yaml_representer.add_representer(cls, cls.to_yaml)
+
+class YAMLObject(object):
+
+    __metaclass__ = YAMLObjectMetaclass
+
+    yaml_constructor = Constructor
+    yaml_representer = Representer
+
+    yaml_tag = None
+
+    def from_yaml(cls, constructor, node):
+        raise ConstructorError(None, None,
+                "found undefined constructor for the tag %r"
+                % node.tag.encode('utf-8'), node.start_mark)
+    from_yaml = classmethod(from_yaml)
+
+    def to_yaml(cls, representer, native):
+        raise RepresenterError(
+                "found undefined representer for the object: %s" % native)
+    to_yaml = classmethod(to_yaml)
+

tests/data/scalars.events

 - !Scalar { value: 'data', style: '"', tag: '!mytag' }
 - !Scalar { implicit: true, value: 'block scalar with tag' }
 - !Scalar { value: 'data', style: '|', tag: '!mytag' }
+- !Scalar { implicit: true, value: 'single character' }
+- !Scalar { value: 'a', implicit: true }
+- !Scalar { implicit: true, value: 'single digit' }
+- !Scalar { value: '1', implicit: true }
 - !MappingEnd
 - !DocumentEnd
 

tests/test_emitter.py

 class TestEmitterEvents(test_appliance.TestAppliance):
 
     def _testEmitterEvents(self, test_name, events_filename):
-        events = list(load_document(file(events_filename, 'rb'), Constructor=EventsConstructor))
+        events = list(load(file(events_filename, 'rb'), Constructor=EventsConstructor))
         #self._dump(events_filename, events)
         writer = StringIO.StringIO()
         emitter = Emitter(writer)

tests/test_representer.py

+
+import test_appliance
+
+try:
+    import datetime
+except ImportError:
+    pass
+try:
+    set
+except NameError:
+    from sets import Set as set
+
+from yaml import *
+
+class MyConstructor(Constructor):
+    pass
+class MyRepresenter(Representer):
+    pass
+
+class MyTestClass1(object):
+
+    def __init__(self, x, y=0, z=0):
+        self.x = x
+        self.y = y
+        self.z = z
+
+    def __eq__(self, other):
+        if isinstance(other, MyTestClass1):
+            return self.__class__, self.__dict__ == other.__class__, other.__dict__
+        else:
+            return False
+
+def construct1(constructor, node):
+    mapping = constructor.construct_mapping(node)
+    return MyTestClass1(**mapping)
+def represent1(representer, native):
+    return representer.represent_mapping("!tag1", native.__dict__)
+
+class MyTestClass2(MyTestClass1, YAMLObject):
+
+    yaml_constructor = MyConstructor
+    yaml_tag = "!tag2"
+
+    def from_yaml(cls, constructor, node):
+        x = constructor.construct_yaml_int(node)
+        return cls(x=x)
+    from_yaml = classmethod(from_yaml)
+
+    def to_yaml(cls, representer, native):
+        return representer.represent_scalar(cls.yaml_tag, str(native.x))
+    to_yaml = classmethod(to_yaml)
+
+class MyTestClass3(MyTestClass2):
+
+    yaml_tag = "!tag3"
+
+    def from_yaml(cls, constructor, node):
+        mapping = constructor.construct_mapping(node)
+        if '=' in mapping:
+            x = mapping['=']
+            del mapping['=']
+            mapping['x'] = x
+        return cls(**mapping)
+    from_yaml = classmethod(from_yaml)
+
+    def to_yaml(cls, representer, native):
+        return representer.represent_mapping(cls.yaml_tag, native.__dict__)
+    to_yaml = classmethod(to_yaml)
+
+MyConstructor.add_constructor("!tag1", construct1)
+MyRepresenter.add_representer(MyTestClass1, represent1)
+
+class TestTypeRepresenter(test_appliance.TestAppliance):
+
+    def _testTypes(self, test_name, data_filename, code_filename):
+        natives1 = eval(file(code_filename, 'rb').read())
+        natives2 = None
+        output = None
+        try:
+            output = dump(natives1, Representer=MyRepresenter)
+            natives2 = load(output, Constructor=MyConstructor)
+            try:
+                self.failUnlessEqual(natives1, natives2)
+            except AssertionError:
+                if isinstance(natives1, dict):
+                    natives1 = natives1.items()
+                    natives1.sort()
+                    natives1 = repr(natives1)
+                    natives2 = natives2.items()
+                    natives2.sort()
+                    natives2 = repr(natives2)
+                if natives1 != natives2:
+                    raise
+        except:
+            print
+            print "OUTPUT:"
+            print output
+            print "NATIVES1:", natives1
+            print "NATIVES2:", natives2
+            raise
+
+TestTypeRepresenter.add_tests('testTypes', '.data', '.code')
+

tests/test_yaml.py

 from test_detector import *
 from test_constructor import *
 from test_emitter import *
+from test_representer import *
 from test_syck import *
 
 def main(module='__main__'):
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.