add --negate flag to pygrep

This commit is contained in:
Marco Gorelli 2020-10-17 14:21:12 +01:00
parent 6ba50f3aa7
commit a0658c06bf
2 changed files with 99 additions and 4 deletions

View file

@ -1,6 +1,7 @@
import argparse import argparse
import re import re
import sys import sys
from typing import NamedTuple
from typing import Optional from typing import Optional
from typing import Pattern from typing import Pattern
from typing import Sequence from typing import Sequence
@ -45,6 +46,46 @@ def _process_filename_at_once(pattern: Pattern[bytes], filename: str) -> int:
return retv return retv
def _process_filename_by_line_negated(
pattern: Pattern[bytes],
filename: str,
) -> int:
with open(filename, 'rb') as f:
for line in f:
if pattern.search(line):
return 0
else:
output.write_line(filename)
return 1
def _process_filename_at_once_negated(
pattern: Pattern[bytes],
filename: str,
) -> int:
with open(filename, 'rb') as f:
contents = f.read()
match = pattern.search(contents)
if match:
return 0
else:
output.write_line(filename)
return 1
class Choice(NamedTuple):
multiline: bool
negate: bool
FNS = {
Choice(multiline=True, negate=True): _process_filename_at_once_negated,
Choice(multiline=True, negate=False): _process_filename_at_once,
Choice(multiline=False, negate=True): _process_filename_by_line_negated,
Choice(multiline=False, negate=False): _process_filename_by_line,
}
def run_hook( def run_hook(
hook: Hook, hook: Hook,
file_args: Sequence[str], file_args: Sequence[str],
@ -64,6 +105,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
) )
parser.add_argument('-i', '--ignore-case', action='store_true') parser.add_argument('-i', '--ignore-case', action='store_true')
parser.add_argument('--multiline', action='store_true') parser.add_argument('--multiline', action='store_true')
parser.add_argument('--negate', action='store_true')
parser.add_argument('pattern', help='python regex pattern.') parser.add_argument('pattern', help='python regex pattern.')
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)
@ -75,11 +117,9 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
pattern = re.compile(args.pattern.encode(), flags) pattern = re.compile(args.pattern.encode(), flags)
retv = 0 retv = 0
process_fn = FNS[Choice(multiline=args.multiline, negate=args.negate)]
for filename in args.filenames: for filename in args.filenames:
if args.multiline: retv |= process_fn(pattern, filename)
retv |= _process_filename_at_once(pattern, filename)
else:
retv |= _process_filename_by_line(pattern, filename)
return retv return retv

View file

@ -8,6 +8,9 @@ def some_files(tmpdir):
tmpdir.join('f1').write_binary(b'foo\nbar\n') tmpdir.join('f1').write_binary(b'foo\nbar\n')
tmpdir.join('f2').write_binary(b'[INFO] hi\n') tmpdir.join('f2').write_binary(b'[INFO] hi\n')
tmpdir.join('f3').write_binary(b"with'quotes\n") tmpdir.join('f3').write_binary(b"with'quotes\n")
tmpdir.join('f4').write_binary(b'foo\npattern\nbar\n')
tmpdir.join('f5').write_binary(b'[INFO] hi\npattern\nbar')
tmpdir.join('f6').write_binary(b"pattern\nbarwith'foo\n")
with tmpdir.as_cwd(): with tmpdir.as_cwd():
yield yield
@ -30,6 +33,58 @@ def test_main(cap_out, pattern, expected_retcode, expected_out):
assert out == expected_out assert out == expected_out
@pytest.mark.usefixtures('some_files')
def test_negate_by_line_no_match(cap_out):
ret = pygrep.main(('pattern\nbar', 'f4', 'f5', 'f6', '--negate'))
out = cap_out.get()
assert ret == 1
assert out == 'f4\nf5\nf6\n'
@pytest.mark.usefixtures('some_files')
def test_negate_by_line_two_match(cap_out):
ret = pygrep.main(('foo', 'f4', 'f5', 'f6', '--negate'))
out = cap_out.get()
assert ret == 1
assert out == 'f5\n'
@pytest.mark.usefixtures('some_files')
def test_negate_by_line_all_match(cap_out):
ret = pygrep.main(('pattern', 'f4', 'f5', 'f6', '--negate'))
out = cap_out.get()
assert ret == 0
assert out == ''
@pytest.mark.usefixtures('some_files')
def test_negate_by_file_no_match(cap_out):
ret = pygrep.main(('baz', 'f4', 'f5', 'f6', '--negate', '--multiline'))
out = cap_out.get()
assert ret == 1
assert out == 'f4\nf5\nf6\n'
@pytest.mark.usefixtures('some_files')
def test_negate_by_file_one_match(cap_out):
ret = pygrep.main(
('foo\npattern', 'f4', 'f5', 'f6', '--negate', '--multiline'),
)
out = cap_out.get()
assert ret == 1
assert out == 'f5\nf6\n'
@pytest.mark.usefixtures('some_files')
def test_negate_by_file_all_match(cap_out):
ret = pygrep.main(
('pattern\nbar', 'f4', 'f5', 'f6', '--negate', '--multiline'),
)
out = cap_out.get()
assert ret == 0
assert out == ''
@pytest.mark.usefixtures('some_files') @pytest.mark.usefixtures('some_files')
def test_ignore_case(cap_out): def test_ignore_case(cap_out):
ret = pygrep.main(('--ignore-case', 'info', 'f1', 'f2', 'f3')) ret = pygrep.main(('--ignore-case', 'info', 'f1', 'f2', 'f3'))