Completed
Push — 0.5.3 ( a7ccc1...2a1862 )
by Felipe A.
01:00
created

StreamStateMachine   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 19
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 19
rs 10
wmc 6

3 Methods

Rating   Name   Duplication   Size   Complexity  
A finish() 0 5 2
A feed() 0 5 2
A flush() 0 4 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
    end = type('EndType', (object,), {})
13
    jumps = {}  # state machine jumps
14
    start = ''  # character which started current state
15
    current = ''  # initial and current state
16
    pending = ''  # buffer of current state data
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
            for mark, next in self.jumps[current].items():
29
                index = value.find(mark, offset)
30
                if -1 != index:
31
                    yield index, mark, next
32
        except KeyError:
33
            raise KeyError(
34
                'Current state %r not defined in %s.jumps.'
35
                % (current, self.__class__)
36
                )
37
        yield len(value), '', None  # failing is also an option
38
39
    def flush(self):
40
        result = (
41
            self.transform(self.pending, '', self.end)
42
            if self.pending else
43
            ''
44
            )
45
        self.pending = ''
46
        self.start = ''
47
        return result
48
49
    def __iter__(self):
50
        while True:
51
            index, mark, next = min(
52
                self.look(self.pending, self.current, self.start),
53
                key=lambda x: (x[0], -len(x[1]))
54
                )
55
            if next is None:
56
                break
57
            data = self.transform(self.pending[:index], mark, next)
58
            self.start = mark
59
            self.current = next
60
            self.pending = self.pending[index:]
61
            if data:
62
                yield data
63
        data = self.flush()
64
        if data:
65
            yield data
66
67
68
class StreamStateMachine(StateMachine):
69
    streaming = False
70
71
    def feed(self, data=''):
72
        self.streaming = True
73
        self.pending += data
74
        for i in self:
75
            yield i
76
77
    def flush(self):
78
        if self.streaming:
79
            return ''
80
        return super(StreamStateMachine, self).flush()
81
82
    def finish(self, data=''):
83
        self.pending += data
84
        self.streaming = False
85
        for i in self:
86
            yield i
87