allura
Revisión | f7d8b46585e1878ff9a9d1380b053e5981715063 (tree) |
---|---|
Tiempo | 2012-05-15 22:25:27 |
Autor | bolkimen <bolkimen@yaho...> |
Commiter | bolkimen |
ticket:53 replace patience with difflib
@@ -1,6 +1,7 @@ | ||
1 | 1 | import os |
2 | 2 | import json |
3 | 3 | import logging |
4 | +import difflib | |
4 | 5 | from urllib import quote, unquote |
5 | 6 | from collections import defaultdict |
6 | 7 |
@@ -15,7 +16,6 @@ from ming.base import Object | ||
15 | 16 | from ming.orm import ThreadLocalORMSession, session |
16 | 17 | |
17 | 18 | import allura.tasks |
18 | -from allura.lib import patience | |
19 | 19 | from allura.lib import security |
20 | 20 | from allura.lib import helpers as h |
21 | 21 | from allura.lib import widgets as w |
@@ -514,7 +514,7 @@ class FileBrowser(BaseController): | ||
514 | 514 | b = self._blob |
515 | 515 | la = list(a) |
516 | 516 | lb = list(b) |
517 | - diff = ''.join(patience.unified_diff( | |
517 | + diff = ''.join(difflib.unified_diff( | |
518 | 518 | la, lb, |
519 | 519 | ('a' + apath).encode('utf-8'), |
520 | 520 | ('b' + b.path()).encode('utf-8'))) |
@@ -1,205 +0,0 @@ | ||
1 | -import sys | |
2 | -import difflib | |
3 | -from itertools import chain | |
4 | - | |
5 | -class Region(object): | |
6 | - '''Simple utility class that keeps track of subsequences''' | |
7 | - __slots__=('seq', # sequence | |
8 | - 'l', # lower bound | |
9 | - 'h', # upper bound | |
10 | - ) | |
11 | - def __init__(self, seq, l=0, h=None): | |
12 | - if h is None: h = len(seq) | |
13 | - self.seq, self.l, self.h = seq, l, h | |
14 | - | |
15 | - def __iter__(self): | |
16 | - '''Iterates over the subsequence only''' | |
17 | - for i in xrange(self.l, self.h): | |
18 | - yield self.seq[i] | |
19 | - | |
20 | - def __getitem__(self, i): | |
21 | - '''works like getitem on the subsequence. Slices return new | |
22 | - regions.''' | |
23 | - if isinstance(i, slice): | |
24 | - start, stop, step = i.indices(len(self)) | |
25 | - assert step == 1 | |
26 | - return self.clone(l=self.l+start,h=self.l+stop) | |
27 | - elif i >= 0: | |
28 | - return self.seq[i+self.l] | |
29 | - else: | |
30 | - return self.seq[i+self.h] | |
31 | - | |
32 | - def __len__(self): | |
33 | - return self.h - self.l | |
34 | - | |
35 | - def __repr__(self): | |
36 | - if len(self) > 8: | |
37 | - srepr = '[%s,...]' % (','.join(repr(x) for x in self[:5])) | |
38 | - else: | |
39 | - srepr = repr(list(self)) | |
40 | - return '<Region (%s,%s): %s>' % (self.l, self.h, srepr) | |
41 | - | |
42 | - def clone(self, **kw): | |
43 | - '''Return a new Region based on this one with the | |
44 | - provided kw arguments replaced in the constructor. | |
45 | - ''' | |
46 | - kwargs = dict(seq=self.seq, l=self.l, h=self.h) | |
47 | - kwargs.update(kw) | |
48 | - return Region(**kwargs) | |
49 | - | |
50 | -def region_diff(ra, rb): | |
51 | - '''generator yielding up to two matching blocks, one at the | |
52 | - beginning of the region and one at the end. This function | |
53 | - mutates the a and b regions, removing any matched blocks. | |
54 | - ''' | |
55 | - # Yield match at the beginning | |
56 | - i = 0 | |
57 | - while i < len(ra) and i < len(rb) and ra[i] == rb[i]: | |
58 | - i += 1 | |
59 | - if i: | |
60 | - yield ra.l, rb.l, i | |
61 | - ra.l+=i | |
62 | - rb.l+=i | |
63 | - # Yield match at the end | |
64 | - j = 0 | |
65 | - while j < len(ra) and j < len(rb) and ra[-j-1]==rb[-j-1]: | |
66 | - j+=1 | |
67 | - if j: | |
68 | - yield ra.h-j, rb.h-j, j | |
69 | - ra.h-=j | |
70 | - rb.h-=j | |
71 | - | |
72 | -def unique(a): | |
73 | - '''generator yielding unique lines of a sequence and their positions''' | |
74 | - count = {} | |
75 | - for aa in a: | |
76 | - count[aa] = count.get(aa, 0) + 1 | |
77 | - for i, aa in enumerate(a): | |
78 | - if count[aa] == 1: yield aa, i | |
79 | - | |
80 | -def common_unique(a, b): | |
81 | - '''generator yielding pairs i,j where | |
82 | - a[i] == b[j] and a[i] and b[j] are unique within a and b, | |
83 | - in increasing j order.''' | |
84 | - uq_a = dict(unique(a)) | |
85 | - for bb, j in unique(b): | |
86 | - try: | |
87 | - yield uq_a[bb], j | |
88 | - except KeyError, ke: | |
89 | - continue | |
90 | - | |
91 | -def patience(seq): | |
92 | - stacks = [] | |
93 | - for i, j in seq: | |
94 | - last_top = None | |
95 | - for stack in stacks: | |
96 | - top_i, top_j, top_back = stack[-1] | |
97 | - if top_i > i: | |
98 | - stack.append((i, j, last_top)) | |
99 | - break | |
100 | - last_top = len(stack)-1 | |
101 | - else: | |
102 | - stacks.append([(i, j, last_top)]) | |
103 | - if not stacks: return [] | |
104 | - prev = len(stacks[-1])-1 | |
105 | - seq = [] | |
106 | - for stack in reversed(stacks): | |
107 | - top_i, top_j, top_back = stack[prev] | |
108 | - seq.append((top_i, top_j)) | |
109 | - prev = top_back | |
110 | - return reversed(seq) | |
111 | - | |
112 | -def match_core(a, b): | |
113 | - '''Returns blocks that match between sequences a and b as | |
114 | - (index_a, index_b, size) | |
115 | - ''' | |
116 | - ra = Region(a) | |
117 | - rb = Region(b) | |
118 | - # beginning/end match | |
119 | - for block in region_diff(ra,rb): yield block | |
120 | - # patience core | |
121 | - last_i = last_j = None | |
122 | - for i, j in chain( | |
123 | - patience(common_unique(ra, rb)), | |
124 | - [(ra.h, rb.h)]): | |
125 | - if last_i is not None: | |
126 | - for block in region_diff(ra[last_i:i], rb[last_j:j]): | |
127 | - yield block | |
128 | - last_i = i | |
129 | - last_j = j | |
130 | - | |
131 | -def diff_gen(a, b, opcode_gen): | |
132 | - '''Convert a sequence of SequenceMatcher opcodes to | |
133 | - unified diff-like output | |
134 | - ''' | |
135 | - def _iter(): | |
136 | - for op, i1, i2, j1, j2 in opcode_gen: | |
137 | - if op == 'equal': | |
138 | - yield ' ', Region(a, i1, i2) | |
139 | - if op in ('delete', 'replace'): | |
140 | - yield '- ', Region(a, i1, i2) | |
141 | - if op in ('replace', 'insert'): | |
142 | - yield '+ ', Region(b, j1, j2) | |
143 | - for prefix, rgn in _iter(): | |
144 | - for line in rgn: | |
145 | - yield prefix, line | |
146 | - | |
147 | -def unified_diff( | |
148 | - a, b, fromfile='', tofile='', fromfiledate='', | |
149 | - tofiledate='', n=3, lineterm='\n'): | |
150 | - started = False | |
151 | - for group in difflib.SequenceMatcher(None,a,b).get_grouped_opcodes(n): | |
152 | - if not started: | |
153 | - yield '--- %s %s%s' % (fromfile, fromfiledate, lineterm) | |
154 | - yield '+++ %s %s%s' % (tofile, tofiledate, lineterm) | |
155 | - started = True | |
156 | - i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] | |
157 | - yield "@@ -%d,%d +%d,%d @@%s" % (i1+1, i2-i1, j1+1, j2-j1, lineterm) | |
158 | - for tag, i1, i2, j1, j2 in group: | |
159 | - if tag == 'equal': | |
160 | - for line in a[i1:i2]: | |
161 | - yield ' ' + line | |
162 | - continue | |
163 | - if tag == 'replace' or tag == 'delete': | |
164 | - for line in a[i1:i2]: | |
165 | - yield '-' + line | |
166 | - if tag == 'replace' or tag == 'insert': | |
167 | - for line in b[j1:j2]: | |
168 | - yield '+' + line | |
169 | - | |
170 | -class SequenceMatcher(difflib.SequenceMatcher): | |
171 | - | |
172 | - def get_matching_blocks(self): | |
173 | - '''Uses patience diff algorithm to find matching blocks.''' | |
174 | - if self.matching_blocks is not None: | |
175 | - return self.matching_blocks | |
176 | - matching_blocks = list(match_core(self.a, self.b)) | |
177 | - matching_blocks.append((len(self.a), len(self.b), 0)) | |
178 | - self.matching_blocks = sorted(matching_blocks) | |
179 | - return self.matching_blocks | |
180 | - | |
181 | -def test(): # pragma no cover | |
182 | - if len(sys.argv) == 3: | |
183 | - fn_a = sys.argv[1] | |
184 | - fn_b = sys.argv[2] | |
185 | - else: | |
186 | - fn_a = 'a.c' | |
187 | - fn_b = 'b.c' | |
188 | - a = open(fn_a).readlines() | |
189 | - b = open(fn_b).readlines() | |
190 | - # print '====', fn_a | |
191 | - # sys.stdout.write(''.join(a)) | |
192 | - # print '====', fn_b | |
193 | - # sys.stdout.write(''.join(b)) | |
194 | - sm = SequenceMatcher(None, a, b) | |
195 | - # print 'Patience opcodes:', sm.get_opcodes() | |
196 | - print ''.join(unified_diff(a, b)) #pragma:printok | |
197 | - # for prefix, line in diff_gen(a, b, sm.get_opcodes()): | |
198 | - # sys.stdout.write(''.join((prefix, line))) | |
199 | - # sm = difflib.SequenceMatcher(None, a, b) | |
200 | - # print 'Difflib opcodes:', sm.get_opcodes() | |
201 | - # for prefix, line in diff_gen(a, b, sm.get_opcodes()): | |
202 | - # sys.stdout.write(''.join((prefix, line))) | |
203 | - | |
204 | -if __name__ == '__main__': # pragma no cover | |
205 | - test() |
@@ -5,6 +5,7 @@ import mimetypes | ||
5 | 5 | import logging |
6 | 6 | import string |
7 | 7 | import re |
8 | +from difflib import SequenceMatcher | |
8 | 9 | from hashlib import sha1 |
9 | 10 | from datetime import datetime |
10 | 11 | from collections import defaultdict |
@@ -20,7 +21,6 @@ from ming.utils import LazyProperty | ||
20 | 21 | from ming.orm import FieldProperty, session, Mapper |
21 | 22 | from ming.orm.declarative import MappedClass |
22 | 23 | |
23 | -from allura.lib.patience import SequenceMatcher | |
24 | 24 | from allura.lib import helpers as h |
25 | 25 | from allura.lib import utils |
26 | 26 |
@@ -1,76 +0,0 @@ | ||
1 | -from os import path, environ | |
2 | -from collections import defaultdict | |
3 | - | |
4 | -from allura.lib import patience | |
5 | - | |
6 | -def text2lines(text): | |
7 | - return [l + '\n' for l in text.split('\n')] | |
8 | - | |
9 | -def test_region(): | |
10 | - r = patience.Region('foobar') | |
11 | - r2 = r.clone() | |
12 | - assert id(r) != id(r2) | |
13 | - assert '-'.join(r) == '-'.join(r2) | |
14 | - subr = r[1:5] | |
15 | - assert type(subr) is type(r) | |
16 | - assert ''.join(subr) == ''.join(r)[1:5] | |
17 | - repr(r) | |
18 | - repr(patience.Region('fffffffffffffffffffffffffffffffffffffffff')) | |
19 | - | |
20 | -def test_unified_diff(): | |
21 | - text1 = '''\ | |
22 | -from paste.deploy import loadapp | |
23 | -from paste.deploy import loadapp | |
24 | -from paste.deploy import loadapp | |
25 | -from paste.deploy import loadapp | |
26 | -from paste.script.appinstall import SetupCommand | |
27 | -from paste.script.appinstall import SetupCommand | |
28 | -from paste.script.appinstall import SetupCommand | |
29 | -from paste.script.appinstall import SetupCommand | |
30 | -from paste.deploy import appconfig | |
31 | -''' | |
32 | - text2 = '''\ | |
33 | -from paste.deploy import loadapp | |
34 | -from paste.deploy import loadapp | |
35 | -from paste.deploy import loadapp | |
36 | -from paste.deploy import loadapp | |
37 | -from paste.script.appinstall import SetupCommand2 | |
38 | -from paste.script.appinstall import SetupCommand3 | |
39 | -from paste.script.appinstall import SetupCommand4 | |
40 | -from paste.deploy import appconfig | |
41 | -''' | |
42 | - line_uni_diff = '''\ | |
43 | - from paste.deploy import loadapp | |
44 | - from paste.deploy import loadapp | |
45 | - from paste.deploy import loadapp | |
46 | --from paste.script.appinstall import SetupCommand | |
47 | --from paste.script.appinstall import SetupCommand | |
48 | --from paste.script.appinstall import SetupCommand | |
49 | --from paste.script.appinstall import SetupCommand | |
50 | -+from paste.script.appinstall import SetupCommand2 | |
51 | -+from paste.script.appinstall import SetupCommand3 | |
52 | -+from paste.script.appinstall import SetupCommand4 | |
53 | - from paste.deploy import appconfig''' | |
54 | - | |
55 | - line_diff = '''\ | |
56 | - from paste.deploy import loadapp | |
57 | -''' + line_uni_diff | |
58 | - | |
59 | - lines1 = text2lines(text1) | |
60 | - lines2 = text2lines(text2) | |
61 | - diff = patience.unified_diff(lines1, lines2) | |
62 | - diff = ''.join(diff) | |
63 | - assert diff == '''\ | |
64 | ---- | |
65 | -+++ | |
66 | -@@ -2,9 +2,8 @@ | |
67 | -%s | |
68 | - | |
69 | -''' % line_uni_diff, '=' + diff + '=' | |
70 | - | |
71 | - sm = patience.SequenceMatcher(None, lines1, lines2) | |
72 | - buf = '' | |
73 | - for prefix, line in patience.diff_gen(lines1, lines2, sm.get_opcodes()): | |
74 | - assert prefix[1] == ' ' | |
75 | - buf += prefix[0] + line | |
76 | - assert buf == line_diff + '\n \n', '=' + buf + '=' |
@@ -1,3 +1,4 @@ | ||
1 | +import difflib | |
1 | 2 | from datetime import datetime |
2 | 3 | from random import randint |
3 | 4 |
@@ -11,7 +12,7 @@ from ming import schema | ||
11 | 12 | from ming.orm import FieldProperty, ForeignIdProperty, Mapper, session, state |
12 | 13 | from allura import model as M |
13 | 14 | from allura.lib import helpers as h |
14 | -from allura.lib import utils, patience | |
15 | +from allura.lib import utils | |
15 | 16 | |
16 | 17 | config = utils.ConfigProxy( |
17 | 18 | common_suffix='forgemail.domain') |
@@ -166,7 +167,7 @@ class BlogPost(M.VersionedArtifact): | ||
166 | 167 | v2 = self |
167 | 168 | la = [ line + '\n' for line in v1.text.splitlines() ] |
168 | 169 | lb = [ line + '\n' for line in v2.text.splitlines() ] |
169 | - diff = ''.join(patience.unified_diff( | |
170 | + diff = ''.join(difflib.unified_diff( | |
170 | 171 | la, lb, |
171 | 172 | 'v%d' % v1.version, |
172 | 173 | 'v%d' % v2.version)) |
@@ -1,4 +1,4 @@ | ||
1 | -{% python from allura.lib import patience %}\ | |
1 | +{% python import difflib %}\ | |
2 | 2 | {% python from allura.model import User %}\ |
3 | 3 | {% for key, values in changelist %}\ |
4 | 4 | {% choose %}\ |
@@ -9,7 +9,7 @@ Diff: | ||
9 | 9 | |
10 | 10 | ~~~~ |
11 | 11 | |
12 | -${'\n'.join(patience.unified_diff( | |
12 | +${'\n'.join(difflib.unified_diff( | |
13 | 13 | a=values[0].splitlines(), |
14 | 14 | b=values[1].splitlines(), |
15 | 15 | fromfile='old', |
@@ -1,6 +1,7 @@ | ||
1 | 1 | import logging |
2 | 2 | import urllib |
3 | 3 | import json |
4 | +import difflib | |
4 | 5 | from datetime import datetime, timedelta |
5 | 6 | |
6 | 7 | import bson |
@@ -20,7 +21,6 @@ from ming.orm.declarative import MappedClass | ||
20 | 21 | from allura.model import Artifact, VersionedArtifact, Snapshot, project_orm_session, BaseAttachment |
21 | 22 | from allura.model import User, Feed, Thread, Notification, ProjectRole |
22 | 23 | from allura.model import ACE, ALL_PERMISSIONS, DENY_ALL |
23 | -from allura.lib import patience | |
24 | 24 | from allura.lib import security |
25 | 25 | from allura.lib.search import search_artifact |
26 | 26 | from allura.lib import utils |
@@ -392,7 +392,7 @@ class Ticket(VersionedArtifact): | ||
392 | 392 | if old.description != self.description: |
393 | 393 | changes.append('Description updated:') |
394 | 394 | changes.append('\n'.join( |
395 | - patience.unified_diff( | |
395 | + difflib.unified_diff( | |
396 | 396 | a=old.description.split('\n'), |
397 | 397 | b=self.description.split('\n'), |
398 | 398 | fromfile='description-old', |
@@ -1,4 +1,5 @@ | ||
1 | 1 | import pylons |
2 | +import difflib | |
2 | 3 | pylons.c = pylons.tmpl_context |
3 | 4 | pylons.g = pylons.app_globals |
4 | 5 | from pylons import g #g is a namespace for globally accessable app helpers |
@@ -11,7 +12,6 @@ from ming.orm.declarative import MappedClass | ||
11 | 12 | from allura.model import VersionedArtifact, Snapshot, Feed, Thread, Post, User, BaseAttachment |
12 | 13 | from allura.model import Notification, project_orm_session |
13 | 14 | from allura.lib import helpers as h |
14 | -from allura.lib import patience | |
15 | 15 | from allura.lib import utils |
16 | 16 | |
17 | 17 | config = utils.ConfigProxy( |
@@ -86,7 +86,7 @@ class Page(VersionedArtifact): | ||
86 | 86 | v2 = self |
87 | 87 | la = [ line + '\n' for line in v1.text.splitlines() ] |
88 | 88 | lb = [ line + '\n' for line in v2.text.splitlines() ] |
89 | - diff = ''.join(patience.unified_diff( | |
89 | + diff = ''.join(difflib.unified_diff( | |
90 | 90 | la, lb, |
91 | 91 | 'v%d' % v1.version, |
92 | 92 | 'v%d' % v2.version)) |