Permalink
Browse files

Support context diff via filterdiff, fixed #15

  • Loading branch information...
1 parent 9cc9298 commit e7854dddde3f8eba6b366a2bd8fee965a570cc8e @ymattw committed Mar 21, 2013
Showing with 226 additions and 17 deletions.
  1. +59 −17 cdiff.py
  2. +56 −0 tests/context/in.diff
  3. +39 −0 tests/context/out.normal
  4. +36 −0 tests/context/out.side-by-side
  5. +36 −0 tests/context/out.w70
View
@@ -26,6 +26,8 @@
import re
import subprocess
import errno
+import fcntl
+import os
import difflib
@@ -429,10 +431,6 @@ def is_binary_differ(self, line):
return re.match('^Binary files .* differ$', line.rstrip())
-class ContextDiff(Diff):
- pass
-
-
class PatchStream(object):
def __init__(self, diff_hdl):
@@ -469,48 +467,92 @@ def __iter__(self):
yield line
+class PatchStreamForwarder(object):
+ """A non-block stream forwarder. Note input stream is non-seekable, and
+ upstream has eaten some lines.
+ """
+ def __init__(self, istream, translator):
+ assert isinstance(istream, PatchStream)
+ self._istream = istream
+ self._translator = translator
+
+ self._istream_open = True
+ self._set_non_block(self._translator.stdin)
+ self._set_non_block(self._translator.stdout)
+
+ self._forward_until_block()
+
+ def _set_non_block(self, hdl):
+ fd = hdl.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+ def _forward_until_block(self):
+ for line in self._istream:
+ try:
+ self._translator.stdin.write(line.encode('utf-8'))
+ except IOError:
+ break # EAGAIN
+ else:
+ self._translator.stdin.close()
+ self._istream_open = False
+
+ def __iter__(self):
+ while True:
+ try:
+ line = self._translator.stdout.readline()
+ if not line:
+ return
+ yield line
+ except IOError:
+ if self._istream_open:
+ self._forward_until_block()
+ continue # EAGAIN
+
+
class DiffParser(object):
def __init__(self, stream):
- self._stream = stream
- header = [decode(line) for line in
- self._stream.read_stream_header(100)]
+ header = [decode(line) for line in stream.read_stream_header(100)]
size = len(header)
if size >= 4 and (header[0].startswith('*** ') and
header[1].startswith('--- ') and
header[2].rstrip() == '***************' and
header[3].startswith('*** ') and
header[3].rstrip().endswith(' ****')):
- self._type = 'context'
-
# For context diff, try use `filterdiff` to translate it to unified
# format and provide a new stream
#
- # TODO
-
+ self._type = 'context'
+ try:
+ self._translator = subprocess.Popen(
+ ['filterdiff', '--format=unified'], stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ except OSError:
+ raise SystemExit('*** Context diff support depends on '
+ 'filterdiff')
+ self._stream = PatchStreamForwarder(stream, self._translator)
return
for n in range(size):
if header[n].startswith('--- ') and (n < size - 1) and \
header[n+1].startswith('+++ '):
self._type = 'unified'
+ self._stream = stream
break
else:
- # `filterdiff` translate unknown diff to nothing, fall through to
+ # `filterdiff` translates unknown diff to nothing, fall through to
# unified diff give cdiff a chance to show everything as headers
#
sys.stderr.write("*** unknown format, fall through to 'unified'\n")
self._type = 'unified'
+ self._stream = stream
def get_diff_generator(self):
"""parse all diff lines, construct a list of Diff objects"""
- if self._type == 'unified':
- difflet = UnifiedDiff(None, None, None, None)
- else:
- raise RuntimeError('unsupported diff format')
-
+ difflet = UnifiedDiff(None, None, None, None)
diff = Diff([], None, None, [])
headers = []
View
@@ -0,0 +1,56 @@
+*** a/Lib/test/test_minidom.py Fri Sep 30 08:46:25 2011 +0300
+--- b/Lib/test/test_minidom.py Sat Oct 01 21:02:49 2011 +0300
+***************
+*** 467,472 ****
+--- 467,479 ----
+ dom.unlink()
+ self.confirm(domstr == str.replace("\n", "\r\n"))
+
++ def testPrettyTextNode(self):
++ str = '<A>B</A>'
++ dom = parseString(str)
++ dom2 = parseString(dom.toprettyxml())
++ self.confirm(dom.childNodes[0].childNodes[0].toxml()==
++ dom2.childNodes[0].childNodes[0].toxml())
++
+ def testProcessingInstruction(self):
+ dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
+ pi = dom.documentElement.firstChild
+*** a/Lib/xml/dom/minidom.py Fri Sep 30 08:46:25 2011 +0300
+--- b/Lib/xml/dom/minidom.py Sat Oct 01 21:02:49 2011 +0300
+***************
+*** 836,842 ****
+ _write_data(writer, attrs[a_name].value)
+ writer.write("\"")
+ if self.childNodes:
+! writer.write(">%s"%(newl))
+ for node in self.childNodes:
+ node.writexml(writer,indent+addindent,addindent,newl)
+ writer.write("%s</%s>%s" % (indent,self.tagName,newl))
+--- 836,844 ----
+ _write_data(writer, attrs[a_name].value)
+ writer.write("\"")
+ if self.childNodes:
+! writer.write(">")
+! if self.childNodes[0].nodeType != Node.TEXT_NODE: # Strict check
+! writer.write(newl)
+ for node in self.childNodes:
+ node.writexml(writer,indent+addindent,addindent,newl)
+ writer.write("%s</%s>%s" % (indent,self.tagName,newl))
+***************
+*** 1061,1067 ****
+ return newText
+
+ def writexml(self, writer, indent="", addindent="", newl=""):
+! _write_data(writer, "%s%s%s"%(indent, self.data, newl))
+
+ # DOM Level 3 (WD 9 April 2002)
+
+--- 1063,1069 ----
+ return newText
+
+ def writexml(self, writer, indent="", addindent="", newl=""):
+! _write_data(writer, self.data)
+
+ # DOM Level 3 (WD 9 April 2002)
+
View
@@ -0,0 +1,39 @@
+--- a/Lib/test/test_minidom.py Fri Sep 30 08:46:25 2011 +0300
++++ b/Lib/test/test_minidom.py Sat Oct 01 21:02:49 2011 +0300
+@@ -467,6 +467,13 @@
+ dom.unlink()
+ self.confirm(domstr == str.replace("\n", "\r\n"))
++
++ def testPrettyTextNode(self):
++ str = '<A>B</A>'
++ dom = parseString(str)
++ dom2 = parseString(dom.toprettyxml())
++ self.confirm(dom.childNodes[0].childNodes[0].toxml()==
++ dom2.childNodes[0].childNodes[0].toxml())
+
+ def testProcessingInstruction(self):
+ dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
+ pi = dom.documentElement.firstChild
+--- a/Lib/xml/dom/minidom.py Fri Sep 30 08:46:25 2011 +0300
++++ b/Lib/xml/dom/minidom.py Sat Oct 01 21:02:49 2011 +0300
+@@ -836,7 +836,9 @@
+ _write_data(writer, attrs[a_name].value)
+ writer.write("\"")
+ if self.childNodes:
+- writer.write(">%s"%(newl))
++ writer.write(">")
++ if self.childNodes[0].nodeType != Node.TEXT_NODE: # Strict check
++ writer.write(newl)
+ for node in self.childNodes:
+ node.writexml(writer,indent+addindent,addindent,newl)
+ writer.write("%s</%s>%s" % (indent,self.tagName,newl))
+@@ -1061,7 +1063,7 @@
+ return newText
+
+ def writexml(self, writer, indent="", addindent="", newl=""):
+- _write_data(writer, "%s%s%s"%(indent, self.data, newl))
++ _write_data(writer, self.data)
+
+ # DOM Level 3 (WD 9 April 2002)
+
+
@@ -0,0 +1,36 @@
+--- a/Lib/test/test_minidom.py Fri Sep 30 08:46:25 2011 +0300
++++ b/Lib/test/test_minidom.py Sat Oct 01 21:02:49 2011 +0300
+@@ -467,6 +467,13 @@
+467  dom.unlink() 467  dom.unlink()
+468  self.confirm(domstr == str.replace("\n", "\r\n")) 468  self.confirm(domstr == str.replace("\n", "\r\n"))
+  469 
+  470  def testPrettyTextNode(self):
+  471  str = '<A>B</A>'
+  472  dom = parseString(str)
+  473  dom2 = parseString(dom.toprettyxml())
+  474  self.confirm(dom.childNodes[0].childNodes[0].toxml()==
+  475  dom2.childNodes[0].childNodes[0].toxml())
+469  476 
+470  def testProcessingInstruction(self): 477  def testProcessingInstruction(self):
+471  dom = parseString('<e><?mypi \t\n data \t\n ?></e>') 478  dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
+472  pi = dom.documentElement.firstChild 479  pi = dom.documentElement.firstChild
+--- a/Lib/xml/dom/minidom.py Fri Sep 30 08:46:25 2011 +0300
++++ b/Lib/xml/dom/minidom.py Sat Oct 01 21:02:49 2011 +0300
+@@ -836,7 +836,9 @@
+ 836  _write_data(writer, attrs[a_name].value)  836  _write_data(writer, attrs[a_name].value)
+ 837  writer.write("\"")  837  writer.write("\"")
+ 838  if self.childNodes:  838  if self.childNodes:
+ 839  writer.write(">%s"%(newl))  839  writer.write(">")
+   840  if self.childNodes[0].nodeType != Node.TEXT_NODE: # Strict check
+   841  writer.write(newl)
+ 840  for node in self.childNodes:  842  for node in self.childNodes:
+ 841  node.writexml(writer,indent+addindent,addindent,newl)  843  node.writexml(writer,indent+addindent,addindent,newl)
+ 842  writer.write("%s</%s>%s" % (indent,self.tagName,newl))  844  writer.write("%s</%s>%s" % (indent,self.tagName,newl))
+@@ -1061,7 +1063,7 @@
+1061  return newText 1063  return newText
+1062  1064 
+1063  def writexml(self, writer, indent="", addindent="", newl=""): 1065  def writexml(self, writer, indent="", addindent="", newl=""):
+1064  _write_data(writer, "%s%s%s"%(indent, self.data, newl)) 1066  _write_data(writer, self.data)
+1065  1067 
+1066  # DOM Level 3 (WD 9 April 2002) 1068  # DOM Level 3 (WD 9 April 2002)
+1067  1069 
View
@@ -0,0 +1,36 @@
+--- a/Lib/test/test_minidom.py Fri Sep 30 08:46:25 2011 +0300
++++ b/Lib/test/test_minidom.py Sat Oct 01 21:02:49 2011 +0300
+@@ -467,6 +467,13 @@
+467  dom.unlink() 467  dom.unlink()
+468  self.confirm(domstr == str.replace("\n", "\r\n")) 468  self.confirm(domstr == str.replace("\n", "\r\n"))
+  469 
+  470  def testPrettyTextNode(self):
+  471  str = '<A>B</A>'
+  472  dom = parseString(str)
+  473  dom2 = parseString(dom.toprettyxml())
+  474  self.confirm(dom.childNodes[0].childNodes[0].toxml()==
+  475  dom2.childNodes[0].childNodes[0].toxml())
+469  476 
+470  def testProcessingInstruction(self): 477  def testProcessingInstruction(self):
+471  dom = parseString('<e><?mypi \t\n data \t\n ?></e>') 478  dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
+472  pi = dom.documentElement.firstChild 479  pi = dom.documentElement.firstChild
+--- a/Lib/xml/dom/minidom.py Fri Sep 30 08:46:25 2011 +0300
++++ b/Lib/xml/dom/minidom.py Sat Oct 01 21:02:49 2011 +0300
+@@ -836,7 +836,9 @@
+ 836  _write_data(writer, attrs[a_name].value)  836  _write_data(writer, attrs[a_name].value)
+ 837  writer.write("\"")  837  writer.write("\"")
+ 838  if self.childNodes:  838  if self.childNodes:
+ 839  writer.write(">%s"%(newl))  839  writer.write(">")
+   840  if self.childNodes[0].nodeType != Node.TEXT_NODE: # S>
+   841  writer.write(newl)
+ 840  for node in self.childNodes:  842  for node in self.childNodes:
+ 841  node.writexml(writer,indent+addindent,addindent,newl)  843  node.writexml(writer,indent+addindent,addindent,newl)
+ 842  writer.write("%s</%s>%s" % (indent,self.tagName,newl))  844  writer.write("%s</%s>%s" % (indent,self.tagName,newl))
+@@ -1061,7 +1063,7 @@
+1061  return newText 1063  return newText
+1062  1064 
+1063  def writexml(self, writer, indent="", addindent="", newl=""): 1065  def writexml(self, writer, indent="", addindent="", newl=""):
+1064  _write_data(writer, "%s%s%s"%(indent, self.data, newl)) 1066  _write_data(writer, self.data)
+1065  1067 
+1066  # DOM Level 3 (WD 9 April 2002) 1068  # DOM Level 3 (WD 9 April 2002)
+1067  1069 

0 comments on commit e7854dd

Please sign in to comment.