Source

pyyaml-legacy / yaml / tests / testPullParser.py

Full commit
import YamlTest
from here import flushLeft
from test import assertEquals
from testPushDumper import mockEvents
from yaml import load

"""
This is experimental code.  I am moving toward a rewrite of the YAML 
parser so that it uses a pull interface.  I am using a mock object
to emulate the parser, so that I can experiment with the interface a bit,
and also to sketch out some ideas for schema-driven parsing.
"""

class MockParser:
    def __init__(self, events):
        self.events = events
        self.index = 0

    def __getattr__(self, name):
        (nextName, value) = self.events[self.index]
        if name != nextName:
            raise "Improper mocking for event %d (%s vs. %s)" % \
                (self.index, name, nextName)
        self.index += 1
        return lambda: value

class Loader:
    def __init__(self, parser):
        self.parser = parser

    def load(self, data):
        typ = self.parser.getType()
        return self._load(typ)

    def _load(self, typ):
        if typ == 'seq':
            return self._loadSeq()
        elif typ == 'map':
            return self._loadMap()
        else:
            return self._loadScalar()

    def _loadSeq(self):
        results = []
        def itemFunc(self, results, typ):
            results.append(self._load(typ))
        return self.iterateItems(results, itemFunc)

    def _loadMap(self):
        results = {}
        def itemFunc(self, results, typ):
            key = self._load(typ)
            valTyp = self.parser.getType()
            value = self._load(typ)
            results[key] = value
        return self.iterateItems(results, itemFunc)

    def iterateItems(self, results, func):
        while 1:
            typ = self.parser.getType()
            if typ is None:
                return results
            else:
                func(self, results, typ)

    def _loadScalar(self):
        return self.parser.getScalar()

def mockParser(data):
    events = mockEvents(data)
    return MockParser(events)

def mockLoad(data):
    loader = Loader(mockParser(data))
    return loader.load(None) # None for data cause events are all mocked 

def testRoundTrip(data):
    obj = mockLoad(data)
    assertEquals(obj, data)

class Test(YamlTest.YamlTest):

    def testMock(self):
        parser = MockParser( [
            ('getScalar', 'foo'),
            ('getArray', [1,2,3]),
            ('getScalar', None),
        ])
        assertEquals(parser.getScalar(), 'foo')
        assertEquals(parser.getArray(), [1,2,3])
        assertEquals(parser.getScalar(), None)

    def testList(self):
        testRoundTrip([1,2,3])

    def testDict(self):
        testRoundTrip({'foo': 'bar'})

    def testListDict(self):
        testRoundTrip( [ [1,2,3], {'foo': 'bar'} ] )

    def testComplexStructure(self):
        data = load(flushLeft(
            """
            list:
                - {foo: bar}
                - [1, 2, 3]
            dict:
                name: steve
                games: [hoops, pool]
            """))
        testRoundTrip(data)

if __name__ == '__main__':
    import unittest
    unittest.main()