Completed
Push — 0.5.3 ( 615661...6cbe30 )
by Felipe A.
01:11
created

StateMachine.flush()   A

Complexity

Conditions 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 9
rs 9.6666
cc 2
1
2
3
class StateMachine(object):
4
    '''
5
    Abstract simple character-driven state-machine for string transforms.
6
7
    Useful for implementig simple transpilations, compressors and so on.
8
9
    Important: when implementing this class, you must set the :attr:`current`
10
    attribute to a key defined in :attr:`jumps` dict.
11
    '''
12
    jumps = {}  # state machine jumps
13
    start = ''  # character which started current state
14
    current = ''  # initial and current state
15
    pending = ''  # buffer of current state data
16
    streaming = False  # stream mode toggle
17
18
    def __init__(self, data=''):
19
        self.pending = data
20
21
    def transform(self, data, mark, next):
22
        method = getattr(self, 'transform_%s' % self.current, None)
23
        return method(data, mark, next) if method else data
24
25
    def look(self, value, current, start):
26
        offset = len(start)
27
        try:
28
            index = len(value)
29
            if self.streaming:
30
                index -= max(map(len, self.jumps))
31
            size = 0
32
            mark = ''
33
            next = None
34
            for amark, anext in self.jumps[current].items():
35
                asize = len(amark)
36
                aindex = value.find(amark, offset, index + asize)
37
                if (
38
                  aindex == -1 or
39
                  aindex > index or
40
                  aindex == index and asize < size):
41
                    continue
42
                index = aindex
43
                size = asize
44
                mark = amark
45
                next = anext
46
        except KeyError:
47
            raise KeyError(
48
                'Current state %r not defined in %s.jumps.'
49
                % (current, self.__class__)
50
                )
51
        return index, mark, next
52
53
    def __iter__(self):
54
        '''
55
        Iterate over tramsformation result chunks.
56
57
        On non-streaming mode, flush and yield it on completion.
58
59
        :yields: transformation result chunka
60
        :ytype: str
61
        '''
62
        while True:
63
            index, mark, next = self.look(
64
                self.pending, self.current, self.start)
65
            if next is None:
66
                break
67
            data = self.transform(self.pending[:index], mark, next)
68
            self.start = mark
69
            self.current = next
70
            self.pending = self.pending[index:]
71
            if data:
72
                yield data
73
        if not self.streaming:
74
            data = (
75
                self.transform(self.pending, '', None)
76
                if self.pending else
77
                ''
78
                )
79
            self.pending = ''
80
            self.start = ''
81
            yield data
82
83
    def feed(self, data=''):
84
        self.streaming = True
85
        self.pending += data
86
        for i in self:
87
            yield i
88
89
    def finish(self, data=''):
90
        self.pending += data
91
        self.streaming = False
92
        for i in self:
93
            yield i
94