3 from .exceptions
import EOF
, TIMEOUT
5 class Expecter(object):
6 def __init__(self
, spawn
, searcher
, searchwindowsize
=-1):
8 self
.searcher
= searcher
9 if searchwindowsize
== -1:
10 searchwindowsize
= spawn
.searchwindowsize
11 self
.searchwindowsize
= searchwindowsize
13 def new_data(self
, data
):
15 searcher
= self
.searcher
17 pos
= spawn
._buffer
.tell()
18 spawn
._buffer
.write(data
)
19 spawn
._before
.write(data
)
21 # determine which chunk of data to search; if a windowsize is
22 # specified, this is the *new* data + the preceding <windowsize> bytes
23 if self
.searchwindowsize
:
24 spawn
._buffer
.seek(max(0, pos
- self
.searchwindowsize
))
25 window
= spawn
._buffer
.read(self
.searchwindowsize
+ len(data
))
27 # otherwise, search the whole buffer (really slow for large datasets)
29 index
= searcher
.search(window
, len(data
))
31 spawn
._buffer
= spawn
.buffer_type()
32 spawn
._buffer
.write(window
[searcher
.end
:])
33 spawn
.before
= spawn
._before
.getvalue()[0:-(len(window
) - searcher
.start
)]
34 spawn
._before
= spawn
.buffer_type()
35 spawn
.after
= window
[searcher
.start
: searcher
.end
]
36 spawn
.match
= searcher
.match
37 spawn
.match_index
= index
40 elif self
.searchwindowsize
:
41 spawn
._buffer
= spawn
.buffer_type()
42 spawn
._buffer
.write(window
)
44 def eof(self
, err
=None):
47 spawn
.before
= spawn
.buffer
48 spawn
._buffer
= spawn
.buffer_type()
49 spawn
._before
= spawn
.buffer_type()
51 index
= self
.searcher
.eof_index
54 spawn
.match_index
= index
58 spawn
.match_index
= None
60 msg
+= '\nsearcher: %s' % self
.searcher
62 msg
= str(err
) + '\n' + msg
65 def timeout(self
, err
=None):
68 spawn
.before
= spawn
.buffer
70 index
= self
.searcher
.timeout_index
73 spawn
.match_index
= index
77 spawn
.match_index
= None
79 msg
+= '\nsearcher: %s' % self
.searcher
81 msg
= str(err
) + '\n' + msg
86 spawn
.before
= spawn
.buffer
89 spawn
.match_index
= None
91 def expect_loop(self
, timeout
=-1):
95 if timeout
is not None:
96 end_time
= time
.time() + timeout
99 incoming
= spawn
.buffer
100 spawn
._buffer
= spawn
.buffer_type()
101 spawn
._before
= spawn
.buffer_type()
103 idx
= self
.new_data(incoming
)
104 # Keep reading until exception or return.
107 # No match at this point
108 if (timeout
is not None) and (timeout
< 0):
109 return self
.timeout()
110 # Still have time left, so read more data
111 incoming
= spawn
.read_nonblocking(spawn
.maxread
, timeout
)
112 if self
.spawn
.delayafterread
is not None:
113 time
.sleep(self
.spawn
.delayafterread
)
114 if timeout
is not None:
115 timeout
= end_time
- time
.time()
119 return self
.timeout(e
)
125 class searcher_string(object):
126 '''This is a plain string search helper for the spawn.expect_any() method.
127 This helper class is for speed. For more powerful regex patterns
128 see the helper class, searcher_re.
132 eof_index - index of EOF, or -1
133 timeout_index - index of TIMEOUT, or -1
135 After a successful match by the search() method the following attributes
138 start - index into the buffer, first byte of match
139 end - index into the buffer, first byte after match
140 match - the matching string itself
144 def __init__(self
, strings
):
145 '''This creates an instance of searcher_string. This argument 'strings'
146 may be a list; a sequence of strings; or the EOF or TIMEOUT types. '''
149 self
.timeout_index
= -1
151 for n
, s
in enumerate(strings
):
156 self
.timeout_index
= n
158 self
._strings
.append((n
, s
))
161 '''This returns a human-readable string that represents the state of
164 ss
= [(ns
[0], ' %d: %r' % ns
) for ns
in self
._strings
]
165 ss
.append((-1, 'searcher_string:'))
166 if self
.eof_index
>= 0:
167 ss
.append((self
.eof_index
, ' %d: EOF' % self
.eof_index
))
168 if self
.timeout_index
>= 0:
169 ss
.append((self
.timeout_index
,
170 ' %d: TIMEOUT' % self
.timeout_index
))
172 ss
= list(zip(*ss
))[1]
175 def search(self
, buffer, freshlen
, searchwindowsize
=None):
176 '''This searches 'buffer' for the first occurrence of one of the search
177 strings. 'freshlen' must indicate the number of bytes at the end of
178 'buffer' which have not been searched before. It helps to avoid
179 searching the same, possibly big, buffer over and over again.
181 See class spawn for the 'searchwindowsize' argument.
183 If there is a match this returns the index of that string, and sets
184 'start', 'end' and 'match'. Otherwise, this returns -1. '''
188 # 'freshlen' helps a lot here. Further optimizations could
191 # using something like the Boyer-Moore Fast String Searching
192 # Algorithm; pre-compiling the search through a list of
193 # strings into something that can scan the input once to
194 # search for all N strings; realize that if we search for
195 # ['bar', 'baz'] and the input is '...foo' we need not bother
196 # rescanning until we've read three more bytes.
198 # Sadly, I don't know enough about this interesting topic. /grahn
200 for index
, s
in self
._strings
:
201 if searchwindowsize
is None:
202 # the match, if any, can only be in the fresh data,
203 # or at the very end of the old data
204 offset
= -(freshlen
+ len(s
))
206 # better obey searchwindowsize
207 offset
= -searchwindowsize
208 n
= buffer.find(s
, offset
)
209 if n
>= 0 and (first_match
is None or n
< first_match
):
211 best_index
, best_match
= index
, s
212 if first_match
is None:
214 self
.match
= best_match
215 self
.start
= first_match
216 self
.end
= self
.start
+ len(self
.match
)
220 class searcher_re(object):
221 '''This is regular expression string search helper for the
222 spawn.expect_any() method. This helper class is for powerful
223 pattern matching. For speed, see the helper class, searcher_string.
227 eof_index - index of EOF, or -1
228 timeout_index - index of TIMEOUT, or -1
230 After a successful match by the search() method the following attributes
233 start - index into the buffer, first byte of match
234 end - index into the buffer, first byte after match
235 match - the re.match object returned by a successful re.search
239 def __init__(self
, patterns
):
240 '''This creates an instance that searches for 'patterns' Where
241 'patterns' may be a list or other sequence of compiled regular
242 expressions, or the EOF or TIMEOUT types.'''
245 self
.timeout_index
= -1
247 for n
, s
in zip(list(range(len(patterns
))), patterns
):
252 self
.timeout_index
= n
254 self
._searches
.append((n
, s
))
257 '''This returns a human-readable string that represents the state of
260 #ss = [(n, ' %d: re.compile("%s")' %
261 # (n, repr(s.pattern))) for n, s in self._searches]
263 for n
, s
in self
._searches
:
264 ss
.append((n
, ' %d: re.compile(%r)' % (n
, s
.pattern
)))
265 ss
.append((-1, 'searcher_re:'))
266 if self
.eof_index
>= 0:
267 ss
.append((self
.eof_index
, ' %d: EOF' % self
.eof_index
))
268 if self
.timeout_index
>= 0:
269 ss
.append((self
.timeout_index
, ' %d: TIMEOUT' %
272 ss
= list(zip(*ss
))[1]
275 def search(self
, buffer, freshlen
, searchwindowsize
=None):
276 '''This searches 'buffer' for the first occurrence of one of the regular
277 expressions. 'freshlen' must indicate the number of bytes at the end of
278 'buffer' which have not been searched before.
280 See class spawn for the 'searchwindowsize' argument.
282 If there is a match this returns the index of that string, and sets
283 'start', 'end' and 'match'. Otherwise, returns -1.'''
286 # 'freshlen' doesn't help here -- we cannot predict the
287 # length of a match, and the re module provides no help.
288 if searchwindowsize
is None:
291 searchstart
= max(0, len(buffer) - searchwindowsize
)
292 for index
, s
in self
._searches
:
293 match
= s
.search(buffer, searchstart
)
297 if first_match
is None or n
< first_match
:
301 if first_match
is None:
303 self
.start
= first_match
304 self
.match
= the_match
305 self
.end
= self
.match
.end()