source: pysyck/trunk/tests/test_loader.py @ 176

Revision 176, 9.2 KB checked in by xi, 9 years ago (diff)

Fixed #16 (duplicate keys in dictionary cause unpredictable behavior).
Thanks to nickesk(at)cs.bu.edu for the report.

If a mapping node contains duplicate keys, it is transformed to a list of pairs.
This behaviour may change in the future since it contradicts the YAML specification.

RevLine 
[7]1
2import unittest
3import syck
[16]4import test_parser
[9]5import os
[7]6
[9]7try:
8    import datetime
9except ImportError:
10    pass
11
12try:
13    import mx.DateTime
14except ImportError:
15    pass
16
17try:
[49]18    Set = set
19except:
20    try:
21        from sets import Set
22    except ImportError:
23        def Set(items):
[18]24            set = {}
25            for items in items:
26                set[items] = None
27            return set
[9]28
[49]29
[7]30INF = 1e300000
31NAN = INF/INF
32
33ARROW = "GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05\x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;"
34BINARY = r"""
35- !binary "\
36 R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
37 OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
38 +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
39 AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
40-
41 !binary |
42  R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
43  OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
44  +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
45  AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
46"""
47
[8]48MERGE = """
49---
50- &CENTER { x: 1, y: 2 }
51- &LEFT { x: 0, y: 2 }
52- &BIG { r: 10 }
53- &SMALL { r: 1 }
54
55# All the following maps are equal:
56
57- # Explicit keys
58  x: 1
59  y: 2
60  r: 10
61  label: center/big
62
63- # Merge one map
64  << : *CENTER
65  r: 10
66  label: center/big
67
68- # Merge multiple maps
69  << : [ *CENTER, *BIG ]
70  label: center/big
71
72- # Override
73  << : [ *BIG, *LEFT, *SMALL ]
74  x: 1
75  label: center/big
76""", { 'x': 1, 'y': 2, 'r': 10, 'label': 'center/big' }
77
78OMAP = """
79# Explicitly typed ordered map (dictionary).
80Bestiary: !omap
81  - aardvark: African pig-like ant eater. Ugly.
82  - anteater: South-American ant eater. Two species.
83  - anaconda: South-American constrictor snake. Scaly.
84  # Etc.
85# Flow style
86Numbers: !omap [ one: 1, two: 2, three : 3 ]
87""", {
88    'Bestiary': [
89        ('aardvark', 'African pig-like ant eater. Ugly.'),
90        ('anteater', 'South-American ant eater. Two species.'),
91        ('anaconda', 'South-American constrictor snake. Scaly.'),
92    ],
93    'Numbers': [
94        ('one', 1),
95        ('two', 2),
96        ('three', 3),
97    ],
98}
99
100PAIRS = """
101# Explicitly typed pairs.
102Block tasks: !pairs
103  - meeting: with team.
104  - meeting: with boss.
105  - break: lunch.
106  - meeting: with client.
107Flow tasks: !pairs [ meeting: with team, meeting: with boss ]
108""", {
109    'Block tasks': [
110        ('meeting', 'with team.'),
111        ('meeting', 'with boss.'),
112        ('break', 'lunch.'),
113        ('meeting', 'with client.'),
114    ],
115    'Flow tasks': [
116        ('meeting', 'with team'),
117        ('meeting', 'with boss'),
118    ],
119}
120
121SET = """
122# Syck does not understand it
123## Explicitly typed set.
124#baseball players: !set
125#  ? Mark McGwire
126#  ? Sammy Sosa
127#  ? Ken Griffey
128# Flow style
129baseball teams: !set { Boston Red Sox, Detroit Tigers, New York Yankees }
130""", {
131#    'baseball players': sets.Set(['Mark McGwire', 'Sammy Sosa', 'Ken Griffey']),
[49]132    'baseball teams': Set(['Boston Red Sox', 'Detroit Tigers', 'New York Yankees']),
[8]133}
134
[20]135ALIASES = """
136foo: &bar baz
137bar: *bar
138"""
139
[22]140MUTABLE_KEY = """
141? []
142: []
143"""
144
[176]145DUPLICATE_KEY = """
1460: 0
1470: 1
148"""
149
[16]150class TestDocuments(test_parser.TestDocuments):
[7]151
152    def _testDocuments(self, source, length):
[16]153        actual_length = len(list(syck.load_documents(source)))
[7]154        self.assertEqual(actual_length, length)
[16]155        actual_length = len(list(syck.parse_documents(source)))
[7]156        self.assertEqual(actual_length, length)
157
[16]158class TestValuesAndSources(test_parser.TestValuesAndSources):
[7]159
160    def testValues1(self):
[16]161        self._testValues(test_parser.COMPARE1)
[7]162
163    def testValues2(self):
[16]164        self._testValues(test_parser.COMPARE2)
[7]165
166    def testValues3(self):
[16]167        self._testValues(test_parser.COMPARE3)
[7]168
169    def testFileValues1(self):
[16]170        self._testFileValues(test_parser.COMPARE1)
[7]171
172    def testFileValues2(self):
[16]173        self._testFileValues(test_parser.COMPARE2)
[7]174
175    def testFileValues3(self):
[16]176        self._testFileValues(test_parser.COMPARE3)
[7]177
178    def testNonsense(self):
179        class MyFile1:
180            def read(self, max_length):
181                return ' '*(max_length+1)
182        class MyFile2:
183            def read(self, max_length):
184                return None
185        self.assertRaises(ValueError, lambda: syck.parse(MyFile1()))
186        self.assertRaises(ValueError, lambda: syck.load(MyFile1()))
187        self.assertRaises(TypeError, lambda: syck.parse(MyFile2()))
188        self.assertRaises(TypeError, lambda: syck.load(MyFile2()))
189
190    def _testValues(self, (source, structure)):
191        self.assertEqualStructure(syck.parse(source), structure)
192        self.assertEqual(syck.load(source), structure)
193
194    def _testFileValues(self, (source, structure)):
[22]195        tempfile = os.tmpfile()
196        tempfile.write(source)
197        tempfile.seek(0)
[23]198        self.assertEqualStructure(syck.parse(tempfile), structure)
199        tempfile.seek(0)
200        self.assertEqual(syck.load(tempfile), structure)
201        tempfile.seek(0)
[7]202
203class TestImplicitScalars(unittest.TestCase):
204
205
206    def testBinary(self):
207        self.assertEqual(syck.load(BINARY), [ARROW, ARROW])
208
209    def testBoolean(self):
210        # Syck does not recognize 'y'.
211        #self.assertEqual(syck.load('- y\n- NO\n- True\n- on\n'), [True, False, True, True])
212        self.assertEqual(syck.load('- yes\n- NO\n- True\n- on\n'), [True, False, True, True])
213
214    def testFloat(self):
[23]215        self.assertEqual(syck.load('6.8523015e+5'), 685230.15)
[7]216        # Syck does not understand '_'.
217        #self.assertAlmostEqual(syck.load('685.230_15e+03'), 685230.15)
218        #self.assertAlmostEqual(syck.load('685_230.15'), 685230.15)
[23]219        self.assertEqual(syck.load('685.23015e+03'), 685230.15)
220        self.assertEqual(syck.load('685230.15'), 685230.15)
221        self.assertEqual(syck.load('190:20:30.15'), 685230.15)
222        self.assertEqual(repr(syck.load('-.inf')), repr(-INF))
223        self.assertEqual(repr(syck.load('.nan')), repr(NAN))
[7]224
225    def testInteger(self):
226        # Syck does not understand '_' and binary integer.
227        #self.assertEqual(syck.load(
228        #    '- 685230\n- +685_230\n- 02472256\n- 0x_0A_74_AE\n'
229        #    '- 0b1010_0111_0100_1010_1110\n- 190:20:30\n'), [685230]*6)
230        self.assertEqual(syck.load(
231            '- 685230\n- +685230\n- 02472256\n- 0x0A74AE\n'
232            '- 190:20:30\n'), [685230]*5)
233
234    def testNull(self):
235        self.assertEqual(syck.load('-\n- NULL\n- ~\n'), [None]*3)
236
237    def testTimestamp(self):
238        # Again, Syck does not understand the latest two forms.
239        #self.assertEqual(syck.load(
240        #        '- 2001-12-15T02:59:43.1Z\n'
241        #        '- 2001-12-14t21:59:43.10-05:00\n'
242        #        '- 2001-12-14 21:59:43.10 -5\n'
243        #        '- 2001-12-15 2:59:43.10\n'),
244        #    [datetime.datetime(2001, 12, 15, 2, 59, 43, 100000)]*4)
245        self.assertEqual(syck.load(
246                '- 2001-12-15T02:59:43.1Z\n'
247                '- 2001-12-14t21:59:43.10-05:00\n'
248                '- 2001-12-14 21:59:43.10 -05\n'
249                '- 2001-12-15 02:59:43.10 Z\n'),
250            [datetime.datetime(2001, 12, 15, 2, 59, 43, 100000)]*4)
251        self.assertEqual(syck.load('2002-12-14'), datetime.datetime(2002, 12, 14))
252
253    def testString(self):
254        self.assertEqual('abcd', 'abcd')
255
[8]256class TestMerge(unittest.TestCase):
257
258    def testMerge(self):
259        document = syck.load(MERGE[0])
260        self.assertEqual(document[4], MERGE[1])
261        self.assertEqual(document[5], MERGE[1])
262        self.assertEqual(document[6], MERGE[1])
263        self.assertEqual(document[7], MERGE[1])
264
265class TestCollections(unittest.TestCase):
266
267    def testOmap(self):
268        self.assertEqual(syck.load(OMAP[0]), OMAP[1])
269
270    def testPairs(self):
271        self.assertEqual(syck.load(PAIRS[0]), PAIRS[1])
272
273    def testSet(self):
274        self.assertEqual(syck.load(SET[0]), SET[1])
275
[20]276class TestAliasesParsingAndLoading(unittest.TestCase):
277
278    def testAliasesParsing(self):
279        node = syck.parse(ALIASES)
280        values = node.value.values()
281        self.assert_(values[0] is values[1])
282
283    def testAliasesLoading(self):
284        document = syck.load(ALIASES)
285        self.assert_(document['foo'] is document['bar'])
286
[22]287class TestMutableKey(unittest.TestCase):
288
289    def testMutableKey(self):
290        document = syck.load(MUTABLE_KEY)
291        self.assertEqual(type(document), list)
292        self.assertEqual(len(document), 1)
293        self.assertEqual(type(document[0]), tuple)
294        self.assertEqual(len(document[0]), 2)
295        self.assertEqual(document[0][0], document[0][1])
[176]296
297    def testDuplicateKey(self):
298        document = syck.load(DUPLICATE_KEY)
299        self.assertEqual(type(document), list)
300        self.assertEqual(len(document), 2)
301        self.assertEqual(len(document[0]), 2)
302        self.assertEqual(len(document[1]), 2)
303        self.assertEqual(document[0][0], document[1][0])
304
Note: See TracBrowser for help on using the repository browser.