| 1 | import YamlTest |
|---|
| 2 | from here import flushLeft |
|---|
| 3 | from test import assertEquals |
|---|
| 4 | from TestPushDumper import mockEvents |
|---|
| 5 | from yaml import load |
|---|
| 6 | |
|---|
| 7 | """ |
|---|
| 8 | This is experimental code. I am moving toward a rewrite of the YAML |
|---|
| 9 | parser so that it uses a pull interface. I am using a mock object |
|---|
| 10 | to emulate the parser, so that I can experiment with the interface a bit, |
|---|
| 11 | and also to sketch out some ideas for schema-driven parsing. |
|---|
| 12 | """ |
|---|
| 13 | |
|---|
| 14 | class MockParser: |
|---|
| 15 | def __init__(self, events): |
|---|
| 16 | self.events = events |
|---|
| 17 | self.index = 0 |
|---|
| 18 | |
|---|
| 19 | def __getattr__(self, name): |
|---|
| 20 | (nextName, value) = self.events[self.index] |
|---|
| 21 | if name != nextName: |
|---|
| 22 | raise "Improper mocking for event %d (%s vs. %s)" % \ |
|---|
| 23 | (self.index, name, nextName) |
|---|
| 24 | self.index += 1 |
|---|
| 25 | return lambda: value |
|---|
| 26 | |
|---|
| 27 | class Loader: |
|---|
| 28 | def __init__(self, parser): |
|---|
| 29 | self.parser = parser |
|---|
| 30 | |
|---|
| 31 | def load(self, data): |
|---|
| 32 | typ = self.parser.getType() |
|---|
| 33 | return self._load(typ) |
|---|
| 34 | |
|---|
| 35 | def _load(self, typ): |
|---|
| 36 | if typ == 'seq': |
|---|
| 37 | return self._loadSeq() |
|---|
| 38 | elif typ == 'map': |
|---|
| 39 | return self._loadMap() |
|---|
| 40 | else: |
|---|
| 41 | return self._loadScalar() |
|---|
| 42 | |
|---|
| 43 | def _loadSeq(self): |
|---|
| 44 | results = [] |
|---|
| 45 | def itemFunc(self, results, typ): |
|---|
| 46 | results.append(self._load(typ)) |
|---|
| 47 | return self.iterateItems(results, itemFunc) |
|---|
| 48 | |
|---|
| 49 | def _loadMap(self): |
|---|
| 50 | results = {} |
|---|
| 51 | def itemFunc(self, results, typ): |
|---|
| 52 | key = self._load(typ) |
|---|
| 53 | valTyp = self.parser.getType() |
|---|
| 54 | value = self._load(typ) |
|---|
| 55 | results[key] = value |
|---|
| 56 | return self.iterateItems(results, itemFunc) |
|---|
| 57 | |
|---|
| 58 | def iterateItems(self, results, func): |
|---|
| 59 | while 1: |
|---|
| 60 | typ = self.parser.getType() |
|---|
| 61 | if typ is None: |
|---|
| 62 | return results |
|---|
| 63 | else: |
|---|
| 64 | func(self, results, typ) |
|---|
| 65 | |
|---|
| 66 | def _loadScalar(self): |
|---|
| 67 | return self.parser.getScalar() |
|---|
| 68 | |
|---|
| 69 | def mockParser(data): |
|---|
| 70 | events = mockEvents(data) |
|---|
| 71 | return MockParser(events) |
|---|
| 72 | |
|---|
| 73 | def mockLoad(data): |
|---|
| 74 | loader = Loader(mockParser(data)) |
|---|
| 75 | return loader.load(None) # None for data cause events are all mocked |
|---|
| 76 | |
|---|
| 77 | def testRoundTrip(data): |
|---|
| 78 | obj = mockLoad(data) |
|---|
| 79 | assertEquals(obj, data) |
|---|
| 80 | |
|---|
| 81 | class Test(YamlTest.YamlTest): |
|---|
| 82 | |
|---|
| 83 | def testMock(self): |
|---|
| 84 | parser = MockParser( [ |
|---|
| 85 | ('getScalar', 'foo'), |
|---|
| 86 | ('getArray', [1,2,3]), |
|---|
| 87 | ('getScalar', None), |
|---|
| 88 | ]) |
|---|
| 89 | assertEquals(parser.getScalar(), 'foo') |
|---|
| 90 | assertEquals(parser.getArray(), [1,2,3]) |
|---|
| 91 | assertEquals(parser.getScalar(), None) |
|---|
| 92 | |
|---|
| 93 | def testList(self): |
|---|
| 94 | testRoundTrip([1,2,3]) |
|---|
| 95 | |
|---|
| 96 | def testDict(self): |
|---|
| 97 | testRoundTrip({'foo': 'bar'}) |
|---|
| 98 | |
|---|
| 99 | def testListDict(self): |
|---|
| 100 | testRoundTrip( [ [1,2,3], {'foo': 'bar'} ] ) |
|---|
| 101 | |
|---|
| 102 | def testComplexStructure(self): |
|---|
| 103 | data = load(flushLeft( |
|---|
| 104 | """ |
|---|
| 105 | list: |
|---|
| 106 | - {foo: bar} |
|---|
| 107 | - [1, 2, 3] |
|---|
| 108 | dict: |
|---|
| 109 | name: steve |
|---|
| 110 | games: [hoops, pool] |
|---|
| 111 | """)) |
|---|
| 112 | testRoundTrip(data) |
|---|
| 113 | |
|---|
| 114 | if __name__ == '__main__': |
|---|
| 115 | import unittest |
|---|
| 116 | unittest.main() |
|---|