[Python-checkins] r80780 - sandbox/trunk/untabify/untabify.py

antoine.pitrou python-checkins at python.org
Wed May 5 14:58:30 CEST 2010


Author: antoine.pitrou
Date: Wed May  5 14:58:30 2010
New Revision: 80780

Log:
Add an option to rewrite patches



Modified:
   sandbox/trunk/untabify/untabify.py

Modified: sandbox/trunk/untabify/untabify.py
==============================================================================
--- sandbox/trunk/untabify/untabify.py	(original)
+++ sandbox/trunk/untabify/untabify.py	Wed May  5 14:58:30 2010
@@ -36,6 +36,9 @@
   $ svn revert Modules/_cursesmodule.c
     # Done!
 
+You can also rewrite your old patches with the -p option; if there are
+still conflicts when applying, use `patch -l`.
+
 """
 
 import os
@@ -48,8 +51,8 @@
 
 
 def untabify(lines, write):
-    last_indent = 0
-    last_outdent = 0
+    last_indent = -1
+    last_outdent = -1
     last_outline = ""
     for line in lines:
         cr = line.find('\r')
@@ -84,7 +87,7 @@
             inpos = (inpos // 8 + 1) * 8
             outpos += 4
         # Continuation line?
-        if (inpos > last_indent + 8
+        if (last_indent >= 0 and inpos > last_indent + 8
             # labels and end-of-comments can't be continued
             and not last_outline.endswith(':')
             and not last_outline.endswith('*/')):
@@ -114,8 +117,7 @@
             last_indent = indent
             last_outdent = outdent
             last_outline = outline
-        write(outline)
-        write(eol)
+        write(outline + eol)
 
 def replace_tabs(lines, write):
     """Simply replace tabs with 8-spaces without attempting to fix/change
@@ -146,8 +148,64 @@
             outpos = inpos
         output.append(chunks[-1])
         outline = "".join(output).rstrip()
-        write(outline)
-        write(eol)
+        write(outline + eol)
+
+def passthrough(lines, write):
+    for l in lines:
+        write(l)
+
+def untabify_hunk(lines, write, transform_func):
+    if not lines:
+        return
+    chars, lines = zip(*((l[:1], l[1:]) for l in lines))
+    outlines = []
+    transform_func(lines, outlines.append)
+    for c, l in zip(chars, outlines):
+        write(c + l)
+
+def untabify_patch_fragment(lines, write, transform_func):
+    lines = list(lines)
+    if '\t' not in ''.join(lines):
+        passthrough(lines, write)
+        return
+    hunk_chars = ' +-'
+    hunk_lines = []
+    for line in lines:
+        c = line[:1]
+        if c not in hunk_chars:
+            untabify_hunk(hunk_lines, write, transform_func)
+            hunk_lines = []
+            write(line)
+            continue
+        hunk_lines.append(line)
+    untabify_hunk(hunk_lines, write, transform_func)
+
+def untabify_patch(lines, write, transform_func):
+    patch_chars = ' +-<>!@'
+    lines = iter(lines)
+    line = next(lines)
+    while True:
+        patch_transform_func = passthrough
+        for line in itertools.chain([line], lines):
+            c = line[:1]
+            if line[:4] in ('--- ', '+++ '):
+                filepath = line.split()[1]
+                if is_c_file(filepath):
+                    patch_transform_func = transform_func
+            elif c in patch_chars:
+                break
+            write(line)
+        else:
+            break
+        fragment_lines = []
+        for line in itertools.chain([line], lines):
+            c = line[:1]
+            if c not in patch_chars:
+                break
+            fragment_lines.append(line)
+        untabify_patch_fragment(fragment_lines, write, transform_func)
+        if c in patch_chars:
+            break
 
 
 def needs_untabifying(filepath):
@@ -160,13 +218,16 @@
     finally:
         f.close()
 
+def is_c_file(filepath):
+    return filepath.endswith('.h') or filepath.endswith('.c')
+
 def walk_c_files(paths):
     for p in paths:
         if os.path.isfile(p):
             yield p
         for dirpath, dirnames, filenames in os.walk(p):
             for fn in sorted(filenames):
-                if fn.endswith('.h') or fn.endswith('.c'):
+                if is_c_file(fn):
                     yield os.path.join(dirpath, fn)
 
 @contextlib.contextmanager
@@ -195,6 +256,9 @@
     parser.add_option("-u", "--untabify", dest="do_untab",
                       action="store_true", default=False,
                       help="untabify stdin to stdout")
+    parser.add_option("-p", "--untabify-patch", dest="do_untab_patch",
+                      action="store_true", default=False,
+                      help="untabify patch (stdin to stdout)")
     parser.add_option("-b", "--batch", dest="do_batch",
                       action="store_true", default=False,
                       help="untabify specified files and dirs")
@@ -203,8 +267,9 @@
                       help="simply replace tabs with 8-spaces (for files with mixed 4-spaces and tabs)")
     options, args = parser.parse_args()
     if (options.do_list_true + options.do_list_false +
-        options.do_untab + options.do_batch + options.do_replace != 1):
-        parser.error("you must specify exactly one of -l, -n, -b and -u")
+        options.do_untab + options.do_untab_patch +
+        options.do_batch + options.do_replace != 1):
+        parser.error("you must specify exactly one of -l, -n, -b, -u and -p")
 
     if options.do_list_true or options.do_list_false:
         if not args:
@@ -236,6 +301,9 @@
 
     if options.do_untab:
         untabify(sys.stdin, sys.stdout.write)
+    
+    if options.do_untab_patch:
+        untabify_patch(sys.stdin, sys.stdout.write, untabify)
 
 if __name__ == '__main__':
     main()


More information about the Python-checkins mailing list