|
@@ -26,7 +26,7 @@ if sys.hexversion < 0x02050000:
|
26
|
26
|
import re
|
27
|
27
|
import signal
|
28
|
28
|
import subprocess
|
29
|
|
-import fcntl
|
|
29
|
+import select
|
30
|
30
|
import os
|
31
|
31
|
import difflib
|
32
|
32
|
|
|
@@ -226,44 +226,54 @@ class PatchStream(object):
|
226
|
226
|
|
227
|
227
|
|
228
|
228
|
class PatchStreamForwarder(object):
|
229
|
|
- """A non-block stream forwarder. Note input stream is non-seekable, and
|
230
|
|
- upstream has eaten some lines.
|
|
229
|
+ """A blocking stream forwarder use `select` and line buffered mode. Feed
|
|
230
|
+ input stream to a diff format translator and read output stream from it.
|
|
231
|
+ Note input stream is non-seekable, and upstream has eaten some lines.
|
231
|
232
|
"""
|
232
|
233
|
def __init__(self, istream, translator):
|
233
|
234
|
assert isinstance(istream, PatchStream)
|
234
|
|
- self._istream = istream
|
235
|
|
- self._translator = translator
|
|
235
|
+ assert isinstance(translator, subprocess.Popen)
|
|
236
|
+ self._istream = iter(istream)
|
|
237
|
+ self._in = translator.stdin
|
|
238
|
+ self._out = translator.stdout
|
236
|
239
|
|
237
|
|
- self._set_non_block(self._translator.stdin)
|
238
|
|
- self._set_non_block(self._translator.stdout)
|
|
240
|
+ def _can_read(self, timeout=0):
|
|
241
|
+ return select.select([self._out.fileno()], [], [], timeout)[0]
|
239
|
242
|
|
240
|
|
- self._forward_until_block()
|
241
|
|
-
|
242
|
|
- def _set_non_block(self, hdl):
|
243
|
|
- fd = hdl.fileno()
|
244
|
|
- fl = fcntl.fcntl(fd, fcntl.F_GETFL)
|
245
|
|
- fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
246
|
|
-
|
247
|
|
- def _forward_until_block(self):
|
248
|
|
- for line in self._istream:
|
249
|
|
- try:
|
250
|
|
- self._translator.stdin.write(line.encode('utf-8'))
|
251
|
|
- except IOError:
|
252
|
|
- break # EAGAIN
|
253
|
|
- else:
|
254
|
|
- self._translator.stdin.close()
|
|
243
|
+ def _forward_line(self):
|
|
244
|
+ try:
|
|
245
|
+ line = next(self._istream)
|
|
246
|
+ self._in.write(line.encode('utf-8'))
|
|
247
|
+ except StopIteration:
|
|
248
|
+ # XXX: close() does not notify select() for EOF event in python
|
|
249
|
+ # 2.x, one of these two interface must be buggy
|
|
250
|
+ #
|
|
251
|
+ # Sending EOF manually does not work either
|
|
252
|
+ #
|
|
253
|
+ #print('StopIteration, closing (sending EOF)')
|
|
254
|
+ #self._in.write('\x1a'.encode('utf-8'))
|
|
255
|
+ self._in.close()
|
255
|
256
|
|
256
|
257
|
def __iter__(self):
|
257
|
258
|
while True:
|
258
|
|
- try:
|
259
|
|
- line = self._translator.stdout.readline()
|
260
|
|
- if not line:
|
|
259
|
+ if self._can_read():
|
|
260
|
+ line = self._out.readline()
|
|
261
|
+ if line:
|
|
262
|
+ yield line
|
|
263
|
+ else:
|
|
264
|
+ #print('got EOF')
|
|
265
|
+ return
|
|
266
|
+ elif not self._in.closed:
|
|
267
|
+ self._forward_line()
|
|
268
|
+ else:
|
|
269
|
+ #print('no data to read and istream closed')
|
|
270
|
+ # XXX: `close` or `select` seems buggy in python 2.x, select
|
|
271
|
+ # does not tell ready event on _translator.stdout anymore once
|
|
272
|
+ # the stdin is closed. Just add a timeout here to detect, when
|
|
273
|
+ # that happen the remaining in output stream will be discarded
|
|
274
|
+ #
|
|
275
|
+ if not self._can_read(0.5):
|
261
|
276
|
return
|
262
|
|
- yield line
|
263
|
|
- except IOError:
|
264
|
|
- if not self._translator.stdin.closed:
|
265
|
|
- self._forward_until_block()
|
266
|
|
- continue # EAGAIN
|
267
|
277
|
|
268
|
278
|
|
269
|
279
|
class DiffParser(object):
|
|
@@ -283,9 +293,10 @@ class DiffParser(object):
|
283
|
293
|
#
|
284
|
294
|
self._type = 'context'
|
285
|
295
|
try:
|
|
296
|
+ # Use line buffered mode so that to readline() in block mode
|
286
|
297
|
self._translator = subprocess.Popen(
|
287
|
298
|
['filterdiff', '--format=unified'], stdin=subprocess.PIPE,
|
288
|
|
- stdout=subprocess.PIPE)
|
|
299
|
+ stdout=subprocess.PIPE, bufsize=1)
|
289
|
300
|
except OSError:
|
290
|
301
|
raise SystemExit('*** Context diff support depends on '
|
291
|
302
|
'filterdiff')
|