Add context options: -A/-B/-C; Fixes #287 (#722)

* Add context options: -A/-B/-C; Fixes #287

* Fix indentation of line continuation.

* Fix 'only before context' case.

* Add some tests for the new context options.

* Refactor context code (pass context tuple, instead of before/after variables)

* Get rid of 'remove temp file code' in context test.

* Fix reintroduced error (regarding 'only before context').
This commit is contained in:
Florian Pigorsch
2018-10-30 18:49:05 +01:00
committed by Eric Larson
parent c3e589d066
commit e74f433bb8
2 changed files with 101 additions and 3 deletions

View File

@ -283,6 +283,12 @@ def parse_options(args):
action='store_true', default=False,
help='Check hidden files (those starting with ".") as '
'well.')
parser.add_option('-A', '--after-context', metavar='LINES',
help='print LINES of trailing context', type='int')
parser.add_option('-B', '--before-context', metavar='LINES',
help='print LINES of leading context', type='int')
parser.add_option('-C', '--context', metavar='LINES',
help='print LINES of surrounding context', type='int')
(o, args) = parser.parse_args(list(args))
@ -411,8 +417,15 @@ def ask_for_word_fix(line, wrongword, misspelling, interactivity):
return misspelling.fix, fix_case(wrongword, misspelling.data)
def print_context(lines, index, context):
# context = (context_before, context_after)
for i in range(index - context[0], index + context[1] + 1):
if 0 <= i < len(lines):
print('%s %s' % ('>' if i == index else ':', lines[i].rstrip()))
def parse_file(filename, colors, summary, misspellings, exclude_lines,
file_opener, word_regex, options):
file_opener, word_regex, context, options):
bad_count = 0
lines = None
changed = False
@ -479,10 +492,14 @@ def parse_file(filename, colors, summary, misspellings, exclude_lines,
for word in word_regex.findall(line):
lword = word.lower()
if lword in misspellings:
context_shown = False
fix = misspellings[lword].fix
fixword = fix_case(word, misspellings[lword].data)
if options.interactive and lword not in asked_for:
if context is not None:
context_shown = True
print_context(lines, i, context)
fix, fixword = ask_for_word_fix(
lines[i], word, misspellings[lword],
options.interactive)
@ -527,6 +544,8 @@ def parse_file(filename, colors, summary, misspellings, exclude_lines,
# our bad_count and thus return value
bad_count += 1
if (not context_shown) and (context is not None):
print_context(lines, i, context)
if filename != '-':
print("%(FILENAME)s:%(LINE)s: %(WRONGWORD)s "
" ==> %(RIGHTWORD)s%(REASON)s"
@ -613,6 +632,26 @@ def main(*args):
else:
summary = None
context = None
if options.context is not None:
if (options.before_context is not None) or \
(options.after_context is not None):
print('ERROR: --context/-C cannot be used together with '
'--context-before/-B or --context-after/-A')
parser.print_help()
return 1
context_both = max(0, options.context)
context = (context_both, context_both)
elif (options.before_context is not None) or \
(options.after_context is not None):
context_before = 0
context_after = 0
if options.before_context is not None:
context_before = max(0, options.before_context)
if options.after_context is not None:
context_after = max(0, options.after_context)
context = (context_before, context_after)
exclude_lines = set()
if options.exclude_file:
build_exclude_hashes(options.exclude_file, exclude_lines)
@ -642,7 +681,7 @@ def main(*args):
continue
bad_count += parse_file(
fname, colors, summary, misspellings, exclude_lines,
file_opener, word_regex, options)
file_opener, word_regex, context, options)
# skip (relative) directories
dirs[:] = [dir_ for dir_ in dirs if not glob_match.match(dir_)]
@ -650,7 +689,7 @@ def main(*args):
else:
bad_count += parse_file(
filename, colors, summary, misspellings, exclude_lines,
file_opener, word_regex, options)
file_opener, word_regex, context, options)
if summary:
print("\n-------8<-------\nSUMMARY:")

View File

@ -302,6 +302,65 @@ def test_case_handling(tmpdir, capsys):
os.remove(f.name)
def test_context(tmpdir, capsys):
"""Test context options."""
d = str(tmpdir)
with open(op.join(d, 'context.txt'), 'w') as f:
f.write('line 1\nline 2\nline 3 abandonned\nline 4\nline 5')
# symmetric context, fully within file
cs.main('-C', '1', d)
lines = capsys.readouterr()[0].split('\n')
assert len(lines) == 5
assert lines[0] == ': line 2'
assert lines[1] == '> line 3 abandonned'
assert lines[2] == ': line 4'
# requested context is bigger than the file
cs.main('-C', '10', d)
lines = capsys.readouterr()[0].split('\n')
assert len(lines) == 7
assert lines[0] == ': line 1'
assert lines[1] == ': line 2'
assert lines[2] == '> line 3 abandonned'
assert lines[3] == ': line 4'
assert lines[4] == ': line 5'
# only before context
cs.main('-B', '2', d)
lines = capsys.readouterr()[0].split('\n')
assert len(lines) == 5
assert lines[0] == ': line 1'
assert lines[1] == ': line 2'
assert lines[2] == '> line 3 abandonned'
# only after context
cs.main('-A', '1', d)
lines = capsys.readouterr()[0].split('\n')
assert len(lines) == 4
assert lines[0] == '> line 3 abandonned'
assert lines[1] == ': line 4'
# asymmetric context
cs.main('-B', '2', '-A', '1', d)
lines = capsys.readouterr()[0].split('\n')
assert len(lines) == 6
assert lines[0] == ': line 1'
assert lines[1] == ': line 2'
assert lines[2] == '> line 3 abandonned'
assert lines[3] == ': line 4'
# both '-C' and '-A' on the command line
cs.main('-C', '2', '-A', '1', d)
lines = capsys.readouterr()[0].split('\n')
assert 'ERROR' in lines[0]
# both '-C' and '-B' on the command line
cs.main('-C', '2', '-B', '1', d)
lines = capsys.readouterr()[0].split('\n')
assert 'ERROR' in lines[0]
@contextlib.contextmanager
def FakeStdin(text):
if sys.version[0] == '2':