summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxi <xi@18f92427-320e-0410-9341-c67f048884a3>2006-02-23 00:18:34 +0000
committerxi <xi@18f92427-320e-0410-9341-c67f048884a3>2006-02-23 00:18:34 +0000
commitd9d88a4c942e30cd12d09d911425d2022d9094ab (patch)
tree3273d56d41681bc4a6c40228d7f853e0f3b7ce81
parentdb82b0f5828351f5c4430a0f3056ae8c13b2cc93 (diff)
downloadpyyaml-d9d88a4c942e30cd12d09d911425d2022d9094ab.tar.gz
Working on Constructor.
git-svn-id: http://svn.pyyaml.org/branches/pyyaml3000@55 18f92427-320e-0410-9341-c67f048884a3
-rw-r--r--lib/yaml/__init__.py38
-rw-r--r--lib/yaml/composer.py8
-rw-r--r--lib/yaml/constructor.py254
-rw-r--r--lib/yaml/nodes.py6
-rw-r--r--lib/yaml/parser.py2
-rw-r--r--lib/yaml/resolver.py76
-rw-r--r--lib/yaml/scanner.py28
-rw-r--r--tests/data/bool.data4
-rw-r--r--tests/data/bool.detect1
-rw-r--r--tests/data/duplicate-mapping-key.error-message6
-rw-r--r--tests/data/float.data6
-rw-r--r--tests/data/float.detect1
-rw-r--r--tests/data/int.data6
-rw-r--r--tests/data/int.detect1
-rw-r--r--tests/data/merge.data1
-rw-r--r--tests/data/merge.detect1
-rw-r--r--tests/data/null.data3
-rw-r--r--tests/data/null.detect1
-rw-r--r--tests/data/str.data1
-rw-r--r--tests/data/str.detect1
-rw-r--r--tests/data/timestamp.data5
-rw-r--r--tests/data/timestamp.detect1
-rw-r--r--tests/data/value.data1
-rw-r--r--tests/data/value.detect1
-rw-r--r--tests/test_detector.py36
-rw-r--r--tests/test_structure.py44
-rw-r--r--tests/test_yaml.py1
27 files changed, 487 insertions, 47 deletions
diff --git a/lib/yaml/__init__.py b/lib/yaml/__init__.py
index f1fcf10..10832dd 100644
--- a/lib/yaml/__init__.py
+++ b/lib/yaml/__init__.py
@@ -5,47 +5,29 @@ 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 *
-
-def scan(data, Reader=Reader, Scanner=Scanner):
- reader = Reader(data)
- scanner = Scanner(reader)
- return iter(scanner)
+from nodes import *
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
+ return parser
-def resolve(data, Reader=Reader, Scanner=Scanner, Parser=Parser,
- Composer=Composer, Resolver=Resolver):
+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)
- return iter(resolver)
+ constructor = Constructor(resolver)
+ return constructor
-def resolve_document(*args, **kwds):
- try:
- return resolve(*args, **kwds).next()
- except StopIteration:
- return None
+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__'):