Skip to content

Commit 384823d

Browse files
author
gustavo.niemeyer
committed
Patch #1413711: Certain patterns of differences were making difflib
touch the recursion limit. The applied patch inlines the recursive __helper method in a non-recursive way. git-svn-id: http://svn.python.org/projects/python/trunk@42212 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent b1e489c commit 384823d

3 files changed

Lines changed: 34 additions & 17 deletions

File tree

Lib/difflib.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -473,27 +473,32 @@ def get_matching_blocks(self):
473473

474474
if self.matching_blocks is not None:
475475
return self.matching_blocks
476-
self.matching_blocks = []
477476
la, lb = len(self.a), len(self.b)
478-
self.__helper(0, la, 0, lb, self.matching_blocks)
477+
478+
indexed_blocks = []
479+
queue = [(0, la, 0, lb)]
480+
while queue:
481+
# builds list of matching blocks covering a[alo:ahi] and
482+
# b[blo:bhi], appending them in increasing order to answer
483+
alo, ahi, blo, bhi = queue.pop()
484+
485+
# a[alo:i] vs b[blo:j] unknown
486+
# a[i:i+k] same as b[j:j+k]
487+
# a[i+k:ahi] vs b[j+k:bhi] unknown
488+
i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)
489+
490+
if k:
491+
if alo < i and blo < j:
492+
queue.append((alo, i, blo, j))
493+
indexed_blocks.append((i, x))
494+
if i+k < ahi and j+k < bhi:
495+
queue.append((i+k, ahi, j+k, bhi))
496+
indexed_blocks.sort()
497+
498+
self.matching_blocks = [elem[1] for elem in indexed_blocks]
479499
self.matching_blocks.append( (la, lb, 0) )
480500
return self.matching_blocks
481501

482-
# builds list of matching blocks covering a[alo:ahi] and
483-
# b[blo:bhi], appending them in increasing order to answer
484-
485-
def __helper(self, alo, ahi, blo, bhi, answer):
486-
i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)
487-
# a[alo:i] vs b[blo:j] unknown
488-
# a[i:i+k] same as b[j:j+k]
489-
# a[i+k:ahi] vs b[j+k:bhi] unknown
490-
if k:
491-
if alo < i and blo < j:
492-
self.__helper(alo, i, blo, j, answer)
493-
answer.append(x)
494-
if i+k < ahi and j+k < bhi:
495-
self.__helper(i+k, ahi, j+k, bhi, answer)
496-
497502
def get_opcodes(self):
498503
"""Return list of 5-tuples describing how to turn a into b.
499504

Lib/test/test_difflib.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from test.test_support import run_unittest, findfile
33
import unittest
44
import doctest
5+
import sys
56

67
class TestSFbugs(unittest.TestCase):
78

@@ -143,6 +144,14 @@ def test_html_diff(self):
143144

144145
self.assertEqual(actual,expect)
145146

147+
def test_recursion_limit(self):
148+
# Check if the problem described in patch #1413711 exists.
149+
limit = sys.getrecursionlimit()
150+
old = [(i%2 and "K:%d" or "V:A:%d") % i for i in range(limit*2)]
151+
new = [(i%2 and "K:%d" or "V:B:%d") % i for i in range(limit*2)]
152+
difflib.SequenceMatcher(None, old, new).get_opcodes()
153+
154+
146155
Doctests = doctest.DocTestSuite(difflib)
147156

148157
run_unittest(TestSFpatches, TestSFbugs, Doctests)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,9 @@ Library
676676

677677
- ` uu.encode()`` and ``uu.decode()`` now support unicode filenames.
678678

679+
- Patch #1413711: Certain patterns of differences were making difflib
680+
touch the recursion limit.
681+
679682
Build
680683
-----
681684

0 commit comments

Comments
 (0)