Project

General

Profile

1
# I/O
2

    
3
import timeouts
4

    
5
##### Exceptions
6

    
7
class InputStreamsOnlyException(Exception):
8
    def __init__(self): Exception.__init__(self, 'Only input streams allowed')
9

    
10
##### Wrapped streams
11

    
12
class WrapStream:
13
    '''Forwards close() to the underlying stream'''
14
    def __init__(self, stream): self.stream = stream
15
    
16
    def close(self): self.stream.close()
17

    
18
##### Line iteration
19

    
20
class StreamIter(WrapStream):
21
    '''Iterates over the lines in a stream.
22
    Unlike stream.__iter__(), doesn't wait for EOF to start returning lines.
23
    Offers curr() method.
24
    '''
25
    def __init__(self, stream):
26
        WrapStream.__init__(self, stream)
27
        self.line = None
28
    
29
    def __iter__(self): return self
30
    
31
    def curr(self):
32
        if self.line == None: self.line = self.stream.readline()
33
        if self.line == '': raise StopIteration
34
        return self.line
35
    
36
    def next(self):
37
        line = self.curr()
38
        self.line = None
39
        return line
40

    
41
def copy(from_, to):
42
    for line in StreamIter(from_): to.write(line)
43

    
44
def consume(in_):
45
    for line in StreamIter(in_): pass
46

    
47
##### Timeouts
48

    
49
class TimeoutInputStream(WrapStream):
50
    '''@param timeout sec'''
51
    def __init__(self, stream, timeout):
52
        WrapStream.__init__(self, stream)
53
        self.timeout = timeout
54
    
55
    def readline(self):
56
        return timeouts.run(lambda: self.stream.readline(), self.timeout)
57
    
58
    def write(self, str_): raise InputStreamsOnlyException()
59

    
60
##### Filtered/traced streams
61

    
62
class FilterStream(WrapStream):
63
    '''Wraps a stream, filtering each string read or written'''
64
    def __init__(self, filter_, stream):
65
        WrapStream.__init__(self, stream)
66
        self.filter = filter_
67
    
68
    def readline(self): return self.filter(self.stream.readline())
69
    
70
    def read(self, n): return self.readline() # forward all reads to readline()
71
    
72
    def write(self, str_): return self.stream.write(self.filter(str_))
73

    
74
class TracedStream(FilterStream):
75
    '''Wraps a stream, running a trace function on each string read or written
76
    '''
77
    def __init__(self, trace, stream):
78
        def filter_(str_):
79
            trace(str_)
80
            return str_
81
        FilterStream.__init__(self, filter_, stream)
82

    
83
class LineCountStream(TracedStream):
84
    '''Wraps a unidirectional stream, making the current line number available.
85
    Lines start counting from 1.'''
86
    def __init__(self, stream):
87
        self.line_num = 1
88
        def trace(str_): self.line_num += str_.count('\n')
89
        TracedStream.__init__(self, trace, stream)
90

    
91
class LineCountInputStream(TracedStream):
92
    '''Wraps an input stream, making the current line number available.
93
    Lines start counting from 1.
94
    This is faster than LineCountStream for input streams, because all reading
95
    is done through readline() so newlines don't need to be explicitly counted.
96
    '''
97
    def __init__(self, stream):
98
        self.line_num = 1
99
        def trace(str_): self.line_num += 1
100
        TracedStream.__init__(self, trace, stream)
101
    
102
    def write(self, str_): raise InputStreamsOnlyException()
103

    
104
class CaptureStream(TracedStream):
105
    '''Wraps a stream, capturing matching text.
106
    Matches can be retrieved from self.matches.'''
107
    def __init__(self, stream, start_str, end_str):
108
        self.recording = False
109
        self.matches = []
110
        
111
        def trace(str_):
112
            start_idx = 0
113
            if not self.recording:
114
                start_idx = str_.find(start_str)
115
                if start_idx >= 0:
116
                    self.recording = True
117
                    self.matches.append('')
118
            if self.recording:
119
                end_idx = str_.find(end_str)
120
                if end_idx >= 0:
121
                    self.recording = False
122
                    end_idx += len(end_str)
123
                else: end_idx = len(str_)
124
                
125
                self.matches[-1] += str_[start_idx:end_idx]
126
        
127
        TracedStream.__init__(self, trace, stream)
(23-23/33)