Commits

Brodie Rao committed b9ae995

Cleaning up queue and updating offset-regex

Comments (0)

Files changed (4)

new-regex-syntax

-# HG changeset patch
-# Parent 6ea01f523ff9b7949719739d8992dc00c994be12
-
-diff --git a/cram.py b/cram.py
---- a/cram.py
-+++ b/cram.py
-@@ -85,8 +85,8 @@ def test(path):
-         elif line.startswith('  > '):
-             after.setdefault(prepos, []).append(line)
-             p.stdin.write(line[4:])
--        elif line.startswith('  '):
--            expected.setdefault(pos, []).append(line[2:])
-+        elif line.startswith('  ') or line.startswith(' ^'):
-+            expected.setdefault(pos, []).append(line)
-         else:
-             after.setdefault(pos, []).append(line)
-     p.stdin.write('echo "\\n%s %s $?"\n' % (salt, i + 1))
-@@ -108,10 +108,8 @@ def test(path):
-             if expected.get(pos):
-                 eline = expected[pos].pop(0)
- 
--            if eline == line:
--                postout.append('  ' + line)
--            elif eline and _match(eline, line):
--                postout.append('  ' + eline)
-+            if eline and eline.startswith(' ^') and _match(eline[2:], line):
-+                postout.append(eline)
-             else:
-                 postout.append('  ' + line)
-     postout += after.pop(pos, [])
-diff --git a/examples/env.t b/examples/env.t
---- a/examples/env.t
-+++ b/examples/env.t
-@@ -15,9 +15,9 @@ Check environment variables:
-   $ echo "$GREP_OPTIONS"
-   
-   $ echo "$RUNDIR"
--  .+
-+ ^.+
-   $ echo "$TESTDIR"
--  .+
-+ ^.+
-   $ if [ "$RUNDIR" != "$TESTDIR" ]; then
-   >   ls -a ..
-   > else
-@@ -28,5 +28,5 @@ Check environment variables:
-   > fi
-   .
-   ..
--  env\.t-.*
-+ ^env\.t-.*
-   tmp
-diff --git a/examples/fail.t b/examples/fail.t
---- a/examples/fail.t
-+++ b/examples/fail.t
-@@ -10,4 +10,4 @@ Wrong output:
- Invalid regex:
- 
-   $ echo 1
--  +++
-+ ^+++
-diff --git a/examples/test.t b/examples/test.t
---- a/examples/test.t
-+++ b/examples/test.t
-@@ -17,7 +17,7 @@ Multi-line command:
- Regular expression:
- 
-   $ echo foobarbaz
--  foobar.*
-+ ^foobar.*
- 
- Exit code:
- 
-diff --git a/tests/cram.t b/tests/cram.t
---- a/tests/cram.t
-+++ b/tests/cram.t
-@@ -13,9 +13,9 @@ from Python.
- Usage:
- 
-   $ cram -h
--  [Uu]sage: cram \[OPTIONS\] TESTS\.\.\.
-+ ^[Uu]sage: cram \[OPTIONS\] TESTS\.\.\.
-   
--  [Oo]ptions:
-+ ^[Oo]ptions:
-     -h, --help            show this help message and exit
-     -v, --verbose         show filenames and test status
-     -i, --interactive     interactively merge changed test output
-@@ -25,7 +25,7 @@ Usage:
-     --keep-tmpdir         keep temporary directories
-     -E                    don't reset common environment variables
-   $ cram
--  [Uu]sage: cram \[OPTIONS\] TESTS\.\.\.
-+ ^[Uu]sage: cram \[OPTIONS\] TESTS\.\.\.
-   [1]
-   $ cram -y -n
-   options -y and -n are mutually exclusive
-@@ -35,8 +35,8 @@ Run cram examples:
- 
-   $ cram -D . examples examples/fail.t examples/.hidden.t
-   ...
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -49,12 +49,12 @@ Run cram examples:
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   ..
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+ ^.*\be04a27ebcf2e0487a5d62d11b8d51387\b.*
-+ ^.*\bb2ad57fc6bcf13972901470979859b78\b.*
-   $ rm examples/fail.t.err
- 
- Verbose mode:
-@@ -64,8 +64,8 @@ Verbose mode:
-   examples/empty.t: empty
-   examples/env.t: passed
-   examples/fail.t: failed
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -78,20 +78,20 @@ Verbose mode:
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   examples/test.t: passed
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+ ^.*\be04a27ebcf2e0487a5d62d11b8d51387\b.*
-+ ^.*\bb2ad57fc6bcf13972901470979859b78\b.*
-   $ rm examples/fail.t.err
- 
- Interactive mode (don't merge):
- 
-   $ cram -n -D . -i examples/fail.t
-   
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -104,21 +104,21 @@ Interactive mode (don't merge):
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   Accept this change? [yN] n
-   .
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+ ^.*\be04a27ebcf2e0487a5d62d11b8d51387\b.*
-+ ^.*\bb2ad57fc6bcf13972901470979859b78\b.*
- 
- Interactive mode (merge):
- 
-   $ cp examples/fail.t examples/fail.t.orig
-   $ cram -y -D . -i examples/fail.t
-   
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -131,20 +131,20 @@ Interactive mode (merge):
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   Accept this change? [yN] y
-   .
-   $ md5 examples/fail.t
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+ ^.*\bb2ad57fc6bcf13972901470979859b78\b.*
-   $ mv examples/fail.t.orig examples/fail.t
- 
- Verbose interactive mode (answer manually and don't merge):
- 
-   $ echo 'bad\nn' | cram -v -D . -i examples/fail.t
-   examples/fail.t: failed
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -157,20 +157,20 @@ Verbose interactive mode (answer manuall
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   Accept this change? [yN] Accept this change? [yN] Accept this change? [yN] %
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+ ^.*\be04a27ebcf2e0487a5d62d11b8d51387\b.*
-+ ^.*\bb2ad57fc6bcf13972901470979859b78\b.*
- 
- Verbose interactive mode (answer manually and merge):
- 
-   $ cp examples/fail.t examples/fail.t.orig
-   $ echo 'bad\ny' | cram -v -D . -i examples/fail.t
-   examples/fail.t: failed
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -183,19 +183,19 @@ Verbose interactive mode (answer manuall
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   Accept this change? [yN] Accept this change? [yN] examples/fail.t: merged output
-   $ md5 examples/fail.t
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+ ^.*\bb2ad57fc6bcf13972901470979859b78\b.*
-   $ mv examples/fail.t.orig examples/fail.t
- 
- Use temp dirs:
- 
-   $ cram examples
-   ...
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+ ^\-\-\- .*/examples/fail\.t\s*
-+ ^\+\+\+ .*/examples/fail\.t\.err\s*
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -208,7 +208,7 @@ Use temp dirs:
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  - ^+++
-   +  1
-   ..
- 
-@@ -235,8 +235,8 @@ Don't sterilize environment:
-   $ GREP_OPTIONS=foo; export GREP_OPTIONS
-   $ cram -E examples/env.t
-   
--  \-\-\- .*/examples/env\.t\s*
--  \+\+\+ .*/examples/env\.t\.err\s*
-+ ^\-\-\- .*/examples/env\.t\s*
-+ ^\+\+\+ .*/examples/env\.t\.err\s*
-   @@ -1,19 +1,19 @@
-    Check environment variables:
-    
-@@ -262,7 +262,7 @@ Don't sterilize environment:
-   -  
-   +  foo
-      $ echo "$RUNDIR"
--     .+
-+    ^.+
-      $ echo "$TESTDIR"
-   .
- 

new-regex-syntax-2

-# HG changeset patch
-# Parent 6ea01f523ff9b7949719739d8992dc00c994be12
-Implemented new unambiguous syntax for regular expressions
-
-diff --git a/README.txt b/README.txt
---- a/README.txt
-+++ b/README.txt
-@@ -50,9 +50,11 @@ The format in a nutshell:
- * All other lines beginning with two spaces are considered command
-   output.
- 
--* Command output in the test is first matched literally with the
--  actual output. If it doesn't match, it's then compiled and matched
--  as a `Perl-compatible regular expression`_.
-+* Output lines suffixed with a space and the word ``(re)`` are matched
-+  as `Perl-compatible regular expressions`_. To match command output
-+  ending in "`` (re)``", suffix the output line with a space and the
-+  word ``(plain)``. To match command output ending in "`` (plain)``",
-+  suffix the output line with "`` (plain) (plain)``".
- 
- * Command output in the test that ends with a percent sign will match
-   actual output that doesn't end in a newline.
-@@ -62,7 +64,7 @@ The format in a nutshell:
- .. _Mercurial: http://mercurial.selenic.com/
- .. _unified test format: http://www.selenic.com/blog/?p=663
- .. _Cram's own test suite: http://bitbucket.org/brodie/cram/src/tip/tests/cram.t
--.. _Perl-compatible regular expression: http://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
-+.. _Perl-compatible regular expressions: http://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
- 
- 
- Download
-@@ -153,6 +155,14 @@ Cram also provides the following environ
- News
- ----
- 
-+Version 0.4
-+```````````
-+* **The test format has changed:** To prevent false negatives, output
-+    lines containing regular expressions must now end in "`` (re)``"
-+    or they'll be matched literally. "`` (plain)``" can be used to
-+    disable this behavior when matching actual output ending in "``
-+    (re)``".
-+
- Version 0.3
- ```````````
- * Implemented resetting of common environment variables. This behavior
-diff --git a/cram.py b/cram.py
---- a/cram.py
-+++ b/cram.py
-@@ -104,14 +104,14 @@ def test(path):
-             postout += after.pop(pos, [])
-             pos = int(line.split()[1])
-         else:
--            eline = None
-+            el = None
-             if expected.get(pos):
--                eline = expected[pos].pop(0)
-+                el = expected[pos].pop(0)
- 
--            if eline == line:
--                postout.append('  ' + line)
--            elif eline and _match(eline, line):
--                postout.append('  ' + eline)
-+            if el and el.endswith(' (re)\n') and _match(el[:-6], line):
-+                postout.append('  ' + el)
-+            elif el and el.endswith(' (plain)\n'):
-+                postout.append('  ' + line[:-1] + ' (plain)\n')
-             else:
-                 postout.append('  ' + line)
-     postout += after.pop(pos, [])
-diff --git a/examples/env.t b/examples/env.t
---- a/examples/env.t
-+++ b/examples/env.t
-@@ -15,9 +15,9 @@ Check environment variables:
-   $ echo "$GREP_OPTIONS"
-   
-   $ echo "$RUNDIR"
--  .+
-+  .+ (re)
-   $ echo "$TESTDIR"
--  .+
-+  .+ (re)
-   $ if [ "$RUNDIR" != "$TESTDIR" ]; then
-   >   ls -a ..
-   > else
-@@ -28,5 +28,5 @@ Check environment variables:
-   > fi
-   .
-   ..
--  env\.t-.*
-+  env\.t-.* (re)
-   tmp
-diff --git a/examples/fail.t b/examples/fail.t
---- a/examples/fail.t
-+++ b/examples/fail.t
-@@ -10,4 +10,4 @@ Wrong output:
- Invalid regex:
- 
-   $ echo 1
--  +++
-+  +++ (re)
-diff --git a/examples/test.t b/examples/test.t
---- a/examples/test.t
-+++ b/examples/test.t
-@@ -17,7 +17,14 @@ Multi-line command:
- Regular expression:
- 
-   $ echo foobarbaz
--  foobar.*
-+  foobar.* (re)
-+
-+Forced plain output:
-+
-+  $ echo 'foo... bar (re)'
-+  foo... bar (re) (plain)
-+  $ echo '(plain)'
-+  (plain) (plain)
- 
- Exit code:
- 
-diff --git a/tests/cram.t b/tests/cram.t
---- a/tests/cram.t
-+++ b/tests/cram.t
-@@ -13,9 +13,9 @@ from Python.
- Usage:
- 
-   $ cram -h
--  [Uu]sage: cram \[OPTIONS\] TESTS\.\.\.
-+  [Uu]sage: cram \[OPTIONS\] TESTS\.\.\. (re)
-   
--  [Oo]ptions:
-+  [Oo]ptions: (re)
-     -h, --help            show this help message and exit
-     -v, --verbose         show filenames and test status
-     -i, --interactive     interactively merge changed test output
-@@ -25,7 +25,7 @@ Usage:
-     --keep-tmpdir         keep temporary directories
-     -E                    don't reset common environment variables
-   $ cram
--  [Uu]sage: cram \[OPTIONS\] TESTS\.\.\.
-+  [Uu]sage: cram \[OPTIONS\] TESTS\.\.\. (re)
-   [1]
-   $ cram -y -n
-   options -y and -n are mutually exclusive
-@@ -35,8 +35,8 @@ Run cram examples:
- 
-   $ cram -D . examples examples/fail.t examples/.hidden.t
-   ...
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -49,12 +49,12 @@ Run cram examples:
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   ..
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+  .*\b4ebd1545dd6c8179c57860a2080a01cb\b.* (re)
-+  .*\bb2ad57fc6bcf13972901470979859b78\b.* (re)
-   $ rm examples/fail.t.err
- 
- Verbose mode:
-@@ -64,8 +64,8 @@ Verbose mode:
-   examples/empty.t: empty
-   examples/env.t: passed
-   examples/fail.t: failed
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -78,20 +78,20 @@ Verbose mode:
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   examples/test.t: passed
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+  .*\b4ebd1545dd6c8179c57860a2080a01cb\b.* (re)
-+  .*\bb2ad57fc6bcf13972901470979859b78\b.* (re)
-   $ rm examples/fail.t.err
- 
- Interactive mode (don't merge):
- 
-   $ cram -n -D . -i examples/fail.t
-   
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -104,21 +104,21 @@ Interactive mode (don't merge):
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   Accept this change? [yN] n
-   .
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+  .*\b4ebd1545dd6c8179c57860a2080a01cb\b.* (re)
-+  .*\bb2ad57fc6bcf13972901470979859b78\b.* (re)
- 
- Interactive mode (merge):
- 
-   $ cp examples/fail.t examples/fail.t.orig
-   $ cram -y -D . -i examples/fail.t
-   
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -131,20 +131,20 @@ Interactive mode (merge):
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   Accept this change? [yN] y
-   .
-   $ md5 examples/fail.t
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+  .*\bb2ad57fc6bcf13972901470979859b78\b.* (re)
-   $ mv examples/fail.t.orig examples/fail.t
- 
- Verbose interactive mode (answer manually and don't merge):
- 
-   $ echo 'bad\nn' | cram -v -D . -i examples/fail.t
-   examples/fail.t: failed
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -157,20 +157,20 @@ Verbose interactive mode (answer manuall
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   Accept this change? [yN] Accept this change? [yN] Accept this change? [yN] %
-   $ md5 examples/fail.t examples/fail.t.err
--  .*\b6ed4b99c2184f1bac5afc144f334a115\b.*
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+  .*\b4ebd1545dd6c8179c57860a2080a01cb\b.* (re)
-+  .*\bb2ad57fc6bcf13972901470979859b78\b.* (re)
- 
- Verbose interactive mode (answer manually and merge):
- 
-   $ cp examples/fail.t examples/fail.t.orig
-   $ echo 'bad\ny' | cram -v -D . -i examples/fail.t
-   examples/fail.t: failed
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -183,19 +183,19 @@ Verbose interactive mode (answer manuall
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   Accept this change? [yN] Accept this change? [yN] examples/fail.t: merged output
-   $ md5 examples/fail.t
--  .*\bb2ad57fc6bcf13972901470979859b78\b.*
-+  .*\bb2ad57fc6bcf13972901470979859b78\b.* (re)
-   $ mv examples/fail.t.orig examples/fail.t
- 
- Use temp dirs:
- 
-   $ cram examples
-   ...
--  \-\-\- .*/examples/fail\.t\s*
--  \+\+\+ .*/examples/fail\.t\.err\s*
-+  \-\-\- .*/examples/fail\.t\s* (re)
-+  \+\+\+ .*/examples/fail\.t\.err\s* (re)
-   @@ -3,11 +3,11 @@
-      $ echo 1
-      1
-@@ -208,7 +208,7 @@ Use temp dirs:
-    Invalid regex:
-    
-      $ echo 1
--  -  +++
-+  -  +++ (re)
-   +  1
-   ..
- 
-@@ -235,8 +235,8 @@ Don't sterilize environment:
-   $ GREP_OPTIONS=foo; export GREP_OPTIONS
-   $ cram -E examples/env.t
-   
--  \-\-\- .*/examples/env\.t\s*
--  \+\+\+ .*/examples/env\.t\.err\s*
-+  \-\-\- .*/examples/env\.t\s* (re)
-+  \+\+\+ .*/examples/env\.t\.err\s* (re)
-   @@ -1,19 +1,19 @@
-    Check environment variables:
-    
-@@ -262,7 +262,7 @@ Don't sterilize environment:
-   -  
-   +  foo
-      $ echo "$RUNDIR"
--     .+
-+     .+ (re)
-      $ echo "$TESTDIR"
-   .
- 
+# HG changeset patch
+# Parent 06f8c203bd588517217f855aef0c909812375b8a
+
+diff --git a/cram.py b/cram.py
+--- a/cram.py
++++ b/cram.py
+@@ -79,6 +79,68 @@ def _glob(el, l):
+             res += re.escape(c)
+     return _match(res, l)
+ 
++class SequenceMatcher(difflib.SequenceMatcher, object):
++    def get_opcodes(self):
++        if self.opcodes is not None:
++            return self.opcodes
++        super(SequenceMatcher, self).get_opcodes()
++        handled = []
++        for tag, i1, i2, j1, j2 in self.opcodes:
++            if tag == 'replace':
++                group = (self.a[i1:i2], self.b[j1:j2])
++                matched = False
++                nr = 0
++                for n, (el, line) in enumerate(itertools.izip(*group)):
++                    if ((el.endswith(' (re)\n') and
++                         _match(el[:-6] + '\n', line))
++                        or
++                        (el.endswith(' (glob)\n') and
++                         _glob(el[:-8] + '\n', line))):
++                        matched = True
++                        if nr != 0:
++                            handled.append((tag, i1 + nr, i1 + n - 1, j1 + nr,
++                                            j1 + n - 1))
++                        handled.append(('equal', i1 + n, i1 + n + 1, j1 + n,
++                                        j1 + n + 1))
++                    else:
++                        nr = n
++                if matched and n != 0 and nr == n:
++                    handled.append((tag, i1 + n, i1 + n + 1, j1 + n,
++                                    j1 + n + 1))
++                if not matched:
++                    handled.append((tag, i1, i2, j1, j2))
++            else:
++                handled.append((tag, i1, i2, j1, j2))
++        if not [True for o in handled if o[0] != 'equal']:
++            handled = []
++        self.opcodes = handled
++        return handled
++
++def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
++                 tofiledate='', n=3, lineterm='\n'):
++    started = False
++    for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n):
++        if not started:
++            fromdate = '\t%s' % fromfiledate if fromfiledate else ''
++            todate = '\t%s' % tofiledate if tofiledate else ''
++            yield '--- %s%s%s' % (fromfile, fromdate, lineterm)
++            yield '+++ %s%s%s' % (tofile, todate, lineterm)
++            started = True
++        i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4]
++        yield "@@ -%d,%d +%d,%d @@%s" % (i1 + 1, i2 - i1, j1 + 1, j2 - j1,
++                                         lineterm)
++        for tag, i1, i2, j1, j2 in group:
++            if tag == 'equal':
++                for line in a[i1:i2]:
++                    yield ' ' + line
++                continue
++            if tag == 'replace' or tag == 'delete':
++                for line in a[i1:i2]:
++                    yield '-' + line
++            if tag == 'replace' or tag == 'insert':
++                for line in b[j1:j2]:
++                    yield '+' + line
++
+ def test(path):
+     """Run test at path and return input, output, and diff.
+ 
+@@ -135,16 +197,18 @@ def test(path):
+ 
+             if el == line:
+                 postout.append('  ' + el)
+-            elif (el and
+-                  (el.endswith(" (re)\n") and _match(el[:-6] + '\n', line) or
+-                   el.endswith(" (glob)\n") and _glob(el[:-8] + '\n', line))):
+-                postout.append('  ' + el)
++            # FIXME: We should be able to move this into the differ completely,
++            #        but right now it completely blows up when commented out.
++            #elif (el and
++            #      (el.endswith(" (re)\n") and _match(el[:-6] + '\n', line) or
++            #       el.endswith(" (glob)\n") and _glob(el[:-8] + '\n', line))):
++            #    postout.append('  ' + el)
+             else:
+                 postout.append('  ' + line)
+     postout += after.pop(pos, [])
+ 
+     dpath = os.path.abspath(path)
+-    diff = difflib.unified_diff(refout, postout, dpath, dpath + '.err')
++    diff = unified_diff(refout, postout, dpath, dpath + '.err')
+     for firstline in diff:
+         return refout, postout, itertools.chain([firstline], diff)
+     return refout, postout, []
-new-regex-syntax-2
-new-regex-syntax
+offset-regex #+offset-regex
 jobs