From d9d88a4c942e30cd12d09d911425d2022d9094ab Mon Sep 17 00:00:00 2001 From: xi Date: Thu, 23 Feb 2006 00:18:34 +0000 Subject: [PATCH] Working on Constructor. git-svn-id: http://svn.pyyaml.org/branches/pyyaml3000@55 18f92427-320e-0410-9341-c67f048884a3 --- lib/yaml/__init__.py | 84 ++++---- lib/yaml/composer.py | 8 +- lib/yaml/constructor.py | 254 +++++++++++++++++++++++++ lib/yaml/nodes.py | 6 +- lib/yaml/parser.py | 2 +- lib/yaml/resolver.py | 76 +++++++- lib/yaml/scanner.py | 28 ++- tests/data/bool.data | 4 + tests/data/bool.detect | 1 + tests/data/duplicate-mapping-key.error-message | 6 + tests/data/float.data | 6 + tests/data/float.detect | 1 + tests/data/int.data | 6 + tests/data/int.detect | 1 + tests/data/merge.data | 1 + tests/data/merge.detect | 1 + tests/data/null.data | 3 + tests/data/null.detect | 1 + tests/data/str.data | 1 + tests/data/str.detect | 1 + tests/data/timestamp.data | 5 + tests/data/timestamp.detect | 1 + tests/data/value.data | 1 + tests/data/value.detect | 1 + tests/test_detector.py | 36 ++++ tests/test_structure.py | 44 ++++- tests/test_yaml.py | 1 + 27 files changed, 510 insertions(+), 70 deletions(-) rewrite lib/yaml/__init__.py (60%) create mode 100644 lib/yaml/constructor.py create mode 100644 tests/data/bool.data create mode 100644 tests/data/bool.detect create mode 100644 tests/data/duplicate-mapping-key.error-message create mode 100644 tests/data/float.data create mode 100644 tests/data/float.detect create mode 100644 tests/data/int.data create mode 100644 tests/data/int.detect create mode 100644 tests/data/merge.data create mode 100644 tests/data/merge.detect create mode 100644 tests/data/null.data create mode 100644 tests/data/null.detect create mode 100644 tests/data/str.data create mode 100644 tests/data/str.detect create mode 100644 tests/data/timestamp.data create mode 100644 tests/data/timestamp.detect create mode 100644 tests/data/value.data create mode 100644 tests/data/value.detect create mode 100644 tests/test_detector.py diff --git a/lib/yaml/__init__.py b/lib/yaml/__init__.py dissimilarity index 60% index f1fcf10..10832dd 100644 --- a/lib/yaml/__init__.py +++ b/lib/yaml/__init__.py @@ -1,51 +1,33 @@ - -from error import YAMLError -from reader import Reader -from scanner import Scanner -from parser import Parser -from composer import Composer -from resolver import Resolver - -from tokens import * -from events import * - -def scan(data, Reader=Reader, Scanner=Scanner): - reader = Reader(data) - scanner = Scanner(reader) - return iter(scanner) - -def parse(data, Reader=Reader, Scanner=Scanner, Parser=Parser): - reader = Reader(data) - scanner = Scanner(reader) - parser = Parser(scanner) - return iter(parser) - -def compose(data, Reader=Reader, Scanner=Scanner, Parser=Parser, - Composer=Composer): - reader = Reader(data) - scanner = Scanner(reader) - parser = Parser(scanner) - composer = Composer(parser) - return iter(composer) - -def compose_document(*args, **kwds): - try: - return compose(*args, **kwds).next() - except StopIteration: - return None - -def resolve(data, Reader=Reader, Scanner=Scanner, Parser=Parser, - Composer=Composer, Resolver=Resolver): - reader = Reader(data) - scanner = Scanner(reader) - parser = Parser(scanner) - composer = Composer(parser) - resolver = Resolver(composer) - return iter(resolver) - -def resolve_document(*args, **kwds): - try: - return resolve(*args, **kwds).next() - except StopIteration: - return None - + +from error import YAMLError +from reader import Reader +from scanner import Scanner +from parser import Parser +from composer import Composer +from resolver import Resolver +from constructor import Constructor + +from tokens import * +from events import * +from nodes 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, + Composer=Composer, Resolver=Resolver, Constructor=Constructor): + reader = Reader(data) + scanner = Scanner(reader) + parser = Parser(scanner) + composer = Composer(parser) + resolver = Resolver(composer) + constructor = Constructor(resolver) + return constructor + +def load_document(*args, **kwds): + for document in load(*args, **kwds): + return document + diff --git a/lib/yaml/composer.py b/lib/yaml/composer.py index fcd93ab..7d60bc1 100644 --- a/lib/yaml/composer.py +++ b/lib/yaml/composer.py @@ -81,11 +81,15 @@ class Composer: def compose_mapping_node(self): start_event = self.parser.get() - value = [] + value = {} while not self.parser.check(CollectionEndEvent): + key_event = self.parser.peek() item_key = self.compose_node() item_value = self.compose_node() - value.append((item_key, item_value)) + if item_key in value: + raise ComposerError("while composing a mapping", start_event.start_marker, + "found duplicate key", key_event.start_marker) + value[item_key] = item_value end_event = self.parser.get() return MappingNode(start_event.tag, value, start_event.start_marker, end_event.end_marker) diff --git a/lib/yaml/constructor.py b/lib/yaml/constructor.py new file mode 100644 index 0000000..352709b --- /dev/null +++ b/lib/yaml/constructor.py @@ -0,0 +1,254 @@ + +from error import * +from nodes import * + +try: + import datetime + datetime_available = True +except ImportError: + datetime_available = False + +try: + set +except NameError: + from sets import Set as set + +import binascii + +class ConstructorError(MarkedYAMLError): + pass + +class BaseConstructor: + + def __init__(self, resolver): + self.resolver = resolver + self.constructed_objects = {} + + def check(self): + # If there are more documents available? + return self.resolver.check() + + def get(self): + # Construct and return the next document. + if self.resolver.check(): + return self.construct_document(self.resolver.get()) + + def __iter__(self): + # Iterator protocol. + while self.resolver.check(): + yield self.construct_document(self.resolver.get()) + + def construct_document(self, node): + native = self.construct_object(node) + self.constructed_objects = {} + return native + + def construct_object(self, node): + if node in self.constructed_objects: + return self.constructed_objects[node] + if node.tag in self.yaml_constructors: + native = self.yaml_constructors[node.tag](self, node) + elif None in self.yaml_constructors: + native = self.yaml_constructors[None](self, node) + elif isinstance(node, ScalarNode): + native = self.construct_scalar(node) + elif isinstance(node, SequenceNode): + native = self.construct_sequence(node) + elif isinstance(node, MappingNode): + native = self.construct_mapping(node) + self.constructed_objects[node] = native + return native + + def construct_scalar(self, node): + if not isinstance(node, ScalarNode): + if isinstance(node, MappingNode): + for key_node in node.value: + if key_node.tag == u'tag:yaml.org,2002:value': + return self.construct_scalar(node.value[key_node]) + raise ConstructorError(None, None, + "expected a scalar node, but found %s" % node.id, + node.start_marker) + return node.value + + def construct_sequence(self, node): + if not isinstance(node, SequenceNode): + raise ConstructorError(None, None, + "expected a sequence node, but found %s" % node.id, + node.start_marker) + return [self.construct_object(child) for child in node.value] + + def construct_mapping(self, node): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_marker) + mapping = {} + for key_node in node.value: + key = self.construct_object(key_node) + try: + duplicate_key = key in mapping + except TypeError, exc: + raise ConstructorError("while constructing a mapping", node.start_marker, + "found unacceptable key (%s)" % exc, key_node.start_marker) + if duplicate_key: + raise ConstructorError("while constructing a mapping", node.start_marker, + "found duplicate key", key_node.start_marker) + value = self.construct_object(node.value[key_node]) + mapping[key] = value + return mapping + + def construct_pairs(self, node): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_marker) + pairs = [] + for key_node in node.value: + key = self.construct_object(key_node) + value = self.construct_object(node.value[key_node]) + pairs.append((key, value)) + return pairs + + def add_constructor(cls, tag, constructor): + if not 'yaml_constructors' in cls.__dict__: + cls.yaml_constructors = cls.yaml_constructors.copy() + cls.yaml_constructors[tag] = constructor + add_constructor = classmethod(add_constructor) + + yaml_constructors = {} + +class Constructor(BaseConstructor): + + def construct_yaml_null(self, node): + self.construct_scalar(node) + return None + + bool_values = { + u'y': True, + u'yes': True, + u'n': False, + u'no': False, + u'true': True, + u'false': False, + u'on': True, + u'off': False, + } + + def construct_yaml_bool(self, node): + value = self.construct_scalar(node) + return self.bool_values[value.lower()] + + def construct_yaml_int(self, node): + value = str(self.construct_scalar(node)) + value = value.replace('_', '') + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '0': + return 0 + elif value.startswith('0b'): + return sign*int(value[2:], 2) + elif value.startswith('0x'): + return sign*int(value[2:], 16) + elif value[0] == '0': + return sign*int(value, 8) + elif ':' in value: + digits = [int(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*int(value) + + inf_value = 1e300000 + nan_value = inf_value/inf_value + + def construct_yaml_float(self, node): + value = str(self.construct_scalar(node)) + value = value.replace('_', '') + sign = +1 + if value[0] == '-': + value = -1 + if value[0] in '+-': + value = value[1:] + if value.lower() == '.inf': + return sign*self.inf_value + elif value.lower() == '.nan': + return self.nan_value + elif ':' in value: + digits = [float(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0.0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return float(value) + + def construct_yaml_binary(self, node): + value = self.construct_scalar(node) + try: + return str(value).decode('base64') + except (binascii.Error, UnicodeEncodeError), exc: + raise ConstructorError(None, None, + "failed to decode base64 data: %s" % exc, node.start_mark) + + def construct_yaml_str(self, node): + value = self.construct_scalar(node) + try: + return str(value) + except UnicodeEncodeError: + return value + +Constructor.add_constructor( + u'tag:yaml.org,2002:null', + Constructor.construct_yaml_null) + +Constructor.add_constructor( + u'tag:yaml.org,2002:bool', + Constructor.construct_yaml_bool) + +Constructor.add_constructor( + u'tag:yaml.org,2002:int', + Constructor.construct_yaml_int) + +Constructor.add_constructor( + u'tag:yaml.org,2002:float', + Constructor.construct_yaml_float) + +Constructor.add_constructor( + u'tag:yaml.org,2002:str', + Constructor.construct_yaml_str) + +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_class.add_constructor(cls.yaml_tag, cls.from_yaml) + +class YAMLObject(object): + + __metaclass__ = YAMLObjectMetaclass + + yaml_constructor_class = 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_marker) + from_yaml = classmethod(from_yaml) + + def to_yaml(self): + assert False # needs dumper + diff --git a/lib/yaml/nodes.py b/lib/yaml/nodes.py index c3fd626..377d24c 100644 --- a/lib/yaml/nodes.py +++ b/lib/yaml/nodes.py @@ -22,14 +22,14 @@ class Node: return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) class ScalarNode(Node): - pass + id = 'scalar' class CollectionNode(Node): pass class SequenceNode(CollectionNode): - pass + id = 'sequence' class MappingNode(CollectionNode): - pass + id = 'mapping' diff --git a/lib/yaml/parser.py b/lib/yaml/parser.py index 0c3d616..2a77d82 100644 --- a/lib/yaml/parser.py +++ b/lib/yaml/parser.py @@ -71,7 +71,7 @@ class ParserError(MarkedYAMLError): class Parser: # Since writing an LL(1) parser is a straightforward task, we do not give # many comments here. - # Note that we use Python generators. If you rewrite the parser to another + # Note that we use Python generators. If you rewrite the parser in another # language, you may replace all 'yield'-s with event handler calls. DEFAULT_TAGS = { diff --git a/lib/yaml/resolver.py b/lib/yaml/resolver.py index 25218d2..e4d1d8d 100644 --- a/lib/yaml/resolver.py +++ b/lib/yaml/resolver.py @@ -1,6 +1,8 @@ from nodes import * +import re + class BaseResolver: DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' @@ -39,12 +41,12 @@ class BaseResolver: elif isinstance(node, SequenceNode): self.resolve_sequence(path, node) for index in range(len(node.value)): - self.resolve_node(path+[node, index], node.value[index]) + self.resolve_node(path+[(node, index)], node.value[index]) elif isinstance(node, MappingNode): self.resolve_mapping(path, node) - for key, value in node.value: + for key in node.value: self.resolve_node(path+[node, None], key) - self.resolve_node(path+[node, key], value) + self.resolve_node(path+[node, key], node.value[key]) def resolve_scalar(self, path, node): if node.tag is None: @@ -61,8 +63,74 @@ class BaseResolver: node.tag = self.DEFAULT_MAPPING_TAG def detect_scalar(self, value): - return None + 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'''^(?:y|Y|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'^(?:=)$'), + ['=']) + diff --git a/lib/yaml/scanner.py b/lib/yaml/scanner.py index 2f47044..d9ae9de 100644 --- a/lib/yaml/scanner.py +++ b/lib/yaml/scanner.py @@ -1,12 +1,24 @@ -# Tokens: -# YAML-DIRECTIVE(major_version, minor_version), TAG-DIRECTIVE(handle, prefix) -# RESERVED-DIRECTIVE(name) -# DOCUMENT-START, DOCUMENT-END -# BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END -# FLOW-SEQUENCE-START, FLOW-MAPPING-START, FLOW-SEQUENCE-END, FLOW-MAPPING-END -# ENTRY, KEY, VALUE -# ALIAS(name), ANCHOR(name), TAG(value), SCALAR(value, plain) +# Scanner produces tokens of the following types: +# DIRECTIVE(name, value) +# DOCUMENT-START +# DOCUMENT-END +# STREAM-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) __all__ = ['Scanner', 'ScannerError'] diff --git a/tests/data/bool.data b/tests/data/bool.data new file mode 100644 index 0000000..3f28955 --- /dev/null +++ b/tests/data/bool.data @@ -0,0 +1,4 @@ +- y +- NO +- True +- on diff --git a/tests/data/bool.detect b/tests/data/bool.detect new file mode 100644 index 0000000..947ebbb --- /dev/null +++ b/tests/data/bool.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:bool diff --git a/tests/data/duplicate-mapping-key.error-message b/tests/data/duplicate-mapping-key.error-message new file mode 100644 index 0000000..7e7b4d1 --- /dev/null +++ b/tests/data/duplicate-mapping-key.error-message @@ -0,0 +1,6 @@ +--- +&anchor foo: + foo: bar + *anchor: duplicate key + baz: bat + *anchor: duplicate key diff --git a/tests/data/float.data b/tests/data/float.data new file mode 100644 index 0000000..524d5db --- /dev/null +++ b/tests/data/float.data @@ -0,0 +1,6 @@ +- 6.8523015e+5 +- 685.230_15e+03 +- 685_230.15 +- 190:20:30.15 +- -.inf +- .NaN diff --git a/tests/data/float.detect b/tests/data/float.detect new file mode 100644 index 0000000..1e12343 --- /dev/null +++ b/tests/data/float.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:float diff --git a/tests/data/int.data b/tests/data/int.data new file mode 100644 index 0000000..d44d376 --- /dev/null +++ b/tests/data/int.data @@ -0,0 +1,6 @@ +- 685230 +- +685_230 +- 02472256 +- 0x_0A_74_AE +- 0b1010_0111_0100_1010_1110 +- 190:20:30 diff --git a/tests/data/int.detect b/tests/data/int.detect new file mode 100644 index 0000000..575c9eb --- /dev/null +++ b/tests/data/int.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:int diff --git a/tests/data/merge.data b/tests/data/merge.data new file mode 100644 index 0000000..e455bbc --- /dev/null +++ b/tests/data/merge.data @@ -0,0 +1 @@ +- << diff --git a/tests/data/merge.detect b/tests/data/merge.detect new file mode 100644 index 0000000..1672d0d --- /dev/null +++ b/tests/data/merge.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:merge diff --git a/tests/data/null.data b/tests/data/null.data new file mode 100644 index 0000000..ad12528 --- /dev/null +++ b/tests/data/null.data @@ -0,0 +1,3 @@ +- +- ~ +- null diff --git a/tests/data/null.detect b/tests/data/null.detect new file mode 100644 index 0000000..19110c7 --- /dev/null +++ b/tests/data/null.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:null diff --git a/tests/data/str.data b/tests/data/str.data new file mode 100644 index 0000000..7cbdb7c --- /dev/null +++ b/tests/data/str.data @@ -0,0 +1 @@ +- abcd diff --git a/tests/data/str.detect b/tests/data/str.detect new file mode 100644 index 0000000..7d5026f --- /dev/null +++ b/tests/data/str.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:str diff --git a/tests/data/timestamp.data b/tests/data/timestamp.data new file mode 100644 index 0000000..7d214ce --- /dev/null +++ b/tests/data/timestamp.data @@ -0,0 +1,5 @@ +- 2001-12-15T02:59:43.1Z +- 2001-12-14t21:59:43.10-05:00 +- 2001-12-14 21:59:43.10 -5 +- 2001-12-15 2:59:43.10 +- 2002-12-14 diff --git a/tests/data/timestamp.detect b/tests/data/timestamp.detect new file mode 100644 index 0000000..2013936 --- /dev/null +++ b/tests/data/timestamp.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:timestamp diff --git a/tests/data/value.data b/tests/data/value.data new file mode 100644 index 0000000..c5b7680 --- /dev/null +++ b/tests/data/value.data @@ -0,0 +1 @@ +- = diff --git a/tests/data/value.detect b/tests/data/value.detect new file mode 100644 index 0000000..7c37d02 --- /dev/null +++ b/tests/data/value.detect @@ -0,0 +1 @@ +tag:yaml.org,2002:value diff --git a/tests/test_detector.py b/tests/test_detector.py new file mode 100644 index 0000000..491929d --- /dev/null +++ b/tests/test_detector.py @@ -0,0 +1,36 @@ + +import test_appliance + +from yaml.reader import Reader +from yaml.scanner import Scanner +from yaml.parser import * +from yaml.composer import * +from yaml.resolver import * +from yaml.nodes import * + +class TestDetector(test_appliance.TestAppliance): + + def _testDetector(self, test_name, data_filename, detect_filename): + node = None + correct_tag = None + try: + correct_tag = file(detect_filename, 'rb').read().strip() + resolver = Resolver(Composer(Parser(Scanner(Reader(file(data_filename, 'rb')))))) + node = list(iter(resolver))[0] + self.failUnless(isinstance(node, SequenceNode)) + for scalar in node.value: + self.failUnless(isinstance(scalar, ScalarNode)) + self.failUnlessEqual(scalar.tag, correct_tag) + except: + print + print "DATA:" + print file(data_filename, 'rb').read() + print "CORRECT_TAG:" + print file(detect_filename, 'rb').read() + print "ROOT NODE:", node + print "SCALAR NODES:", node.value + raise + +TestDetector.add_tests('testDetector', '.data', '.detect') + + diff --git a/tests/test_structure.py b/tests/test_structure.py index 15f485a..f1ec330 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -6,6 +6,7 @@ from yaml.scanner import Scanner from yaml.parser import * from yaml.composer import * from yaml.resolver import * +from yaml.constructor import * from yaml.nodes import * class TestStructure(test_appliance.TestAppliance): @@ -137,12 +138,53 @@ class TestResolver(test_appliance.TestAppliance): self._compare(item1, item2) elif isinstance(node1, MappingNode): self.failUnlessEqual(len(node1.value), len(node2.value)) - for (key1, value1), (key2, value2) in zip(node1.value, node2.value): + items1 = node1.value.items() + items1.sort(lambda (k1,v1), (k2,v2): cmp((k1.tag,k1.value,v1.tag,v1.value), + (k2.tag,k2.value,v2.tag,v2.value))) + items2 = node2.value.items() + items2.sort(lambda (k1,v1), (k2,v2): cmp((k1.tag,k1.value,v1.tag,v1.value), + (k2.tag,k2.value,v2.tag,v2.value))) + for (key1, value1), (key2, value2) in zip(items1, items2): self._compare(key1, key2) self._compare(value1, value2) TestResolver.add_tests('testResolver', '.data', '.canonical') +class MyConstructor(Constructor): + + def construct_sequence(self, node): + return tuple(Constructor.construct_sequence(self, node)) + + def construct_mapping(self, node): + pairs = self.construct_pairs(node) + pairs.sort() + return pairs + +class TestConstructor(test_appliance.TestAppliance): + + def _testConstructor(self, test_name, data_filename, canonical_filename): + natives1 = None + natives2 = None + try: + constructor1 = MyConstructor(Resolver(Composer(Parser(Scanner(Reader(file(data_filename, 'rb'))))))) + natives1 = list(iter(constructor1)) + canonical = test_appliance.CanonicalParser(file(canonical_filename, 'rb').read()) + canonical.parse() + constructor2 = MyConstructor(Resolver(Composer(canonical))) + natives2 = list(iter(constructor2)) + self.failUnlessEqual(natives1, natives2) + except: + print + print "DATA1:" + print file(data_filename, 'rb').read() + print "DATA2:" + print file(canonical_filename, 'rb').read() + print "NATIVES1:", natives1 + print "NATIVES2:", natives2 + raise + +TestConstructor.add_tests('testConstructor', '.data', '.canonical') + class TestParserOnCanonical(test_appliance.TestAppliance): def _testParserOnCanonical(self, test_name, canonical_filename): diff --git a/tests/test_yaml.py b/tests/test_yaml.py index f0c0527..ccdd8d5 100644 --- a/tests/test_yaml.py +++ b/tests/test_yaml.py @@ -7,6 +7,7 @@ from test_canonical import * from test_tokens import * from test_structure import * from test_errors import * +from test_detector import * from test_syck import * def main(module='__main__'): -- 2.11.4.GIT