Commit dc5de3ba authored by Tim Peters's avatar Tim Peters

ellipsis_match(): Changed treatment of start- and end-of-string exact

matches to be symmetric.  This makes the algorithm easier to understand.
parent 336e85f5
...@@ -364,47 +364,57 @@ class _SpoofOut(StringIO): ...@@ -364,47 +364,57 @@ class _SpoofOut(StringIO):
# Worst-case linear-time ellipsis matching. # Worst-case linear-time ellipsis matching.
def ellipsis_match(want, got): def ellipsis_match(want, got):
"""
Essentially the only subtle case:
>>> ellipsis_match('aa...aa', 'aaa')
False
"""
if ELLIPSIS_MARKER not in want: if ELLIPSIS_MARKER not in want:
return want == got return want == got
# Remove \n from ...\n, else the newline will be required, # Remove \n from ...\n, else the newline will be required,
# and (for example) ... on a line by itself can't match # and (for example) ... on a line by itself can't match
# nothing gracefully. # nothing gracefully.
want = want.replace(ELLIPSIS_MARKER + '\n', ELLIPSIS_MARKER) want = want.replace(ELLIPSIS_MARKER + '\n', ELLIPSIS_MARKER)
# Find "the real" strings. # Find "the real" strings.
ws = want.split(ELLIPSIS_MARKER) ws = want.split(ELLIPSIS_MARKER)
assert len(ws) >= 2 assert len(ws) >= 2
# Match. In general, we only need to find the leftmost non-overlapping
# match for each piece. "Real strings" at the start or end of `want` # Deal with exact matches possibly needed at one or both ends.
# are special cases. startpos, endpos = 0, len(got)
w = ws[0] w = ws[0]
if w: if w: # starts with exact match
# An ellipsis didn't start `want`. We need to match exactly if got.startswith(w):
# at the start. startpos = len(w)
if not got.startswith(w):
return False
pos = len(w)
del ws[0] del ws[0]
else: else:
pos = 0 return False
w = ws[-1]
if w: # ends with exact match
if got.endswith(w):
endpos -= len(w)
del ws[-1]
else:
return False
if startpos > endpos:
# Exact end matches required more characters than we have, as in
# ellipsis_match('aa...aa', 'aaa')
return False
# For the rest, we only need to find the leftmost non-overlapping
# match for each piece. If there's no overall match that way alone,
# there's no overall match period.
for w in ws: for w in ws:
# w may be '' at times, if there are consecutive ellipses, or # w may be '' at times, if there are consecutive ellipses, or
# due to an ellipsis at the start or end of `want`. That's OK. # due to an ellipsis at the start or end of `want`. That's OK.
# Search for an empty string succeeds, and doesn't change pos. # Search for an empty string succeeds, and doesn't change startpos.
pos = got.find(w, pos) startpos = got.find(w, startpos, endpos)
if pos < 0: if startpos < 0:
return False return False
pos += len(w) startpos += len(w)
# If `want` ended with an ellipsis, the tail matches anything.
if ws[-1] == '':
return True
# Else `want` ended with a real string. If the last real match
# exhausted `got`, we win.
if pos == len(got):
return True return True
# Else maybe we matched the last real string too early.
return got.endswith(ws[-1])
###################################################################### ######################################################################
## 2. Example & DocTest ## 2. Example & DocTest
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment