• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

allura


Commit MetaInfo

Revisiónf7d8b46585e1878ff9a9d1380b053e5981715063 (tree)
Tiempo2012-05-15 22:25:27
Autorbolkimen <bolkimen@yaho...>
Commiterbolkimen

Log Message

ticket:53 replace patience with difflib

Cambiar Resumen

Diferencia incremental

--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -1,6 +1,7 @@
11 import os
22 import json
33 import logging
4+import difflib
45 from urllib import quote, unquote
56 from collections import defaultdict
67
@@ -15,7 +16,6 @@ from ming.base import Object
1516 from ming.orm import ThreadLocalORMSession, session
1617
1718 import allura.tasks
18-from allura.lib import patience
1919 from allura.lib import security
2020 from allura.lib import helpers as h
2121 from allura.lib import widgets as w
@@ -514,7 +514,7 @@ class FileBrowser(BaseController):
514514 b = self._blob
515515 la = list(a)
516516 lb = list(b)
517- diff = ''.join(patience.unified_diff(
517+ diff = ''.join(difflib.unified_diff(
518518 la, lb,
519519 ('a' + apath).encode('utf-8'),
520520 ('b' + b.path()).encode('utf-8')))
--- a/Allura/allura/lib/patience.py
+++ /dev/null
@@ -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()
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -5,6 +5,7 @@ import mimetypes
55 import logging
66 import string
77 import re
8+from difflib import SequenceMatcher
89 from hashlib import sha1
910 from datetime import datetime
1011 from collections import defaultdict
@@ -20,7 +21,6 @@ from ming.utils import LazyProperty
2021 from ming.orm import FieldProperty, session, Mapper
2122 from ming.orm.declarative import MappedClass
2223
23-from allura.lib.patience import SequenceMatcher
2424 from allura.lib import helpers as h
2525 from allura.lib import utils
2626
--- a/Allura/allura/tests/test_patience.py
+++ /dev/null
@@ -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 + '='
--- a/ForgeBlog/forgeblog/model/blog.py
+++ b/ForgeBlog/forgeblog/model/blog.py
@@ -1,3 +1,4 @@
1+import difflib
12 from datetime import datetime
23 from random import randint
34
@@ -11,7 +12,7 @@ from ming import schema
1112 from ming.orm import FieldProperty, ForeignIdProperty, Mapper, session, state
1213 from allura import model as M
1314 from allura.lib import helpers as h
14-from allura.lib import utils, patience
15+from allura.lib import utils
1516
1617 config = utils.ConfigProxy(
1718 common_suffix='forgemail.domain')
@@ -166,7 +167,7 @@ class BlogPost(M.VersionedArtifact):
166167 v2 = self
167168 la = [ line + '\n' for line in v1.text.splitlines() ]
168169 lb = [ line + '\n' for line in v2.text.splitlines() ]
169- diff = ''.join(patience.unified_diff(
170+ diff = ''.join(difflib.unified_diff(
170171 la, lb,
171172 'v%d' % v1.version,
172173 'v%d' % v2.version))
--- a/ForgeTracker/forgetracker/data/ticket_changed_tmpl
+++ b/ForgeTracker/forgetracker/data/ticket_changed_tmpl
@@ -1,4 +1,4 @@
1-{% python from allura.lib import patience %}\
1+{% python import difflib %}\
22 {% python from allura.model import User %}\
33 {% for key, values in changelist %}\
44 {% choose %}\
@@ -9,7 +9,7 @@ Diff:
99
1010 ~~~~
1111
12-${'\n'.join(patience.unified_diff(
12+${'\n'.join(difflib.unified_diff(
1313 a=values[0].splitlines(),
1414 b=values[1].splitlines(),
1515 fromfile='old',
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -1,6 +1,7 @@
11 import logging
22 import urllib
33 import json
4+import difflib
45 from datetime import datetime, timedelta
56
67 import bson
@@ -20,7 +21,6 @@ from ming.orm.declarative import MappedClass
2021 from allura.model import Artifact, VersionedArtifact, Snapshot, project_orm_session, BaseAttachment
2122 from allura.model import User, Feed, Thread, Notification, ProjectRole
2223 from allura.model import ACE, ALL_PERMISSIONS, DENY_ALL
23-from allura.lib import patience
2424 from allura.lib import security
2525 from allura.lib.search import search_artifact
2626 from allura.lib import utils
@@ -392,7 +392,7 @@ class Ticket(VersionedArtifact):
392392 if old.description != self.description:
393393 changes.append('Description updated:')
394394 changes.append('\n'.join(
395- patience.unified_diff(
395+ difflib.unified_diff(
396396 a=old.description.split('\n'),
397397 b=self.description.split('\n'),
398398 fromfile='description-old',
--- a/ForgeWiki/forgewiki/model/wiki.py
+++ b/ForgeWiki/forgewiki/model/wiki.py
@@ -1,4 +1,5 @@
11 import pylons
2+import difflib
23 pylons.c = pylons.tmpl_context
34 pylons.g = pylons.app_globals
45 from pylons import g #g is a namespace for globally accessable app helpers
@@ -11,7 +12,6 @@ from ming.orm.declarative import MappedClass
1112 from allura.model import VersionedArtifact, Snapshot, Feed, Thread, Post, User, BaseAttachment
1213 from allura.model import Notification, project_orm_session
1314 from allura.lib import helpers as h
14-from allura.lib import patience
1515 from allura.lib import utils
1616
1717 config = utils.ConfigProxy(
@@ -86,7 +86,7 @@ class Page(VersionedArtifact):
8686 v2 = self
8787 la = [ line + '\n' for line in v1.text.splitlines() ]
8888 lb = [ line + '\n' for line in v2.text.splitlines() ]
89- diff = ''.join(patience.unified_diff(
89+ diff = ''.join(difflib.unified_diff(
9090 la, lb,
9191 'v%d' % v1.version,
9292 'v%d' % v2.version))