diff --git a/examples/grinimports.py b/examples/grinimports.py
index b6d48ff..fa4917f 100755
--- a/examples/grinimports.py
+++ b/examples/grinimports.py
@@ -2,10 +2,9 @@
# -*- coding: UTF-8 -*-
""" Transform Python files into normalized import statements for grepping.
"""
-
import compiler
from compiler.visitor import ASTVisitor, walk
-from cStringIO import StringIO
+from io import StringIO
import os
import shlex
import sys
@@ -70,7 +69,7 @@ def normalize_file(filename, *args):
"""
try:
ast = compiler.parseFile(filename)
- except Exception, e:
+ except Exception as e:
return StringIO('')
ip = ImportPuller()
walk(ast, ip)
diff --git a/examples/grinpython.py b/examples/grinpython.py
index 31e23ee..a9e1395 100755
--- a/examples/grinpython.py
+++ b/examples/grinpython.py
@@ -3,7 +3,7 @@
""" Transform Python code by omitting strings, comments, and/or code.
"""
-from cStringIO import StringIO
+from io import BytesIO
import os
import shlex
import string
@@ -55,7 +55,7 @@ class Transformer(object):
""" Open a file and convert it to a filelike object with transformed
contents.
"""
- g = StringIO()
+ g = BytesIO()
f = open(filename, mode)
try:
gen = tokenize.generate_tokens(f.readline)
diff --git a/grin.py b/grin.py
index bf42dc0..9781432 100755
--- a/grin.py
+++ b/grin.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
""" grin searches text files.
"""
+from __future__ import print_function
import bisect
import fnmatch
@@ -11,9 +12,16 @@ import re
import shlex
import stat
import sys
+from io import UnsupportedOperation
import argparse
+if sys.version_info[0] > 2:
+ to_str = lambda s : s.decode('latin1')
+ ints2bytes = bytes
+else:
+ to_str = str
+ ints2bytes = lambda ints : ''.join(map(chr, ints))
#### Constants ####
__version__ = '1.2.1'
@@ -24,8 +32,8 @@ MATCH = 0
POST = 1
# Use file(1)'s choices for what's text and what's not.
-TEXTCHARS = ''.join(map(chr, [7,8,9,10,12,13,27] + range(0x20, 0x100)))
-ALLBYTES = ''.join(map(chr, range(256)))
+TEXTCHARS = ints2bytes([7,8,9,10,12,13,27] + list(range(0x20, 0x100)))
+ALLBYTES = ints2bytes(range(256))
COLOR_TABLE = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
'white', 'default']
@@ -35,12 +43,11 @@ COLOR_STYLE = {
}
# gzip magic header bytes.
-GZIP_MAGIC = '\037\213'
+GZIP_MAGIC = b'\037\213'
# Target amount of data to read into memory at a time.
READ_BLOCKSIZE = 16 * 1024 * 1024
-
def is_binary_string(bytes):
""" Determine if a string is classified as binary rather than text.
@@ -54,7 +61,8 @@ def is_binary_string(bytes):
"""
nontext = bytes.translate(ALLBYTES, TEXTCHARS)
return bool(nontext)
-
+
+
def get_line_offsets(block):
""" Compute the list of offsets in DataBlock 'block' which correspond to
the beginnings of new lines.
@@ -80,7 +88,8 @@ def get_line_offsets(block):
# Keep track of the count of lines within the "current block"
if next_newline >= block.start and next_newline < block.end:
line_count += 1
-
+
+
def colorize(s, fg=None, bg=None, bold=False, underline=False, reverse=False):
""" Wraps a string with ANSI color escape sequences corresponding to the
style parameters given.
@@ -207,7 +216,7 @@ class GrepText(object):
def read_block_with_context(self, prev, fp, fp_size):
""" Read a block of data from the file, along with some surrounding
context.
-
+
Parameters
----------
prev : DataBlock, or None
@@ -216,23 +225,23 @@ class GrepText(object):
fp : filelike object
The source of block data.
-
+
fp_size : int or None
Size of the file in bytes, or None if the size could not be
determined.
-
+
Returns
-------
A DataBlock representing the "current" block along with context.
"""
if fp_size is None:
target_io_size = READ_BLOCKSIZE
- block_main = fp.read(target_io_size)
+ block_main = to_str(fp.read(target_io_size))
is_last_block = len(block_main) < target_io_size
else:
remaining = max(fp_size - fp.tell(), 0)
target_io_size = min(READ_BLOCKSIZE, remaining)
- block_main = fp.read(target_io_size)
+ block_main = to_str(fp.read(target_io_size))
is_last_block = target_io_size == remaining
if prev is None:
@@ -271,12 +280,13 @@ class GrepText(object):
before_lines = prev.data[before_start:prev.end]
# Using readline() to force this block out to a newline boundary...
curr_block = (prev.data[prev.end:] + block_main +
- ('' if is_last_block else fp.readline()))
+ ('' if is_last_block else to_str(fp.readline())))
# Read in some lines of 'after' context.
if is_last_block:
after_lines = ''
else:
- after_lines_list = [fp.readline() for i in range(self.options.after_context)]
+ after_lines_list = [to_str(fp.readline())
+ for i in range(self.options.after_context)]
after_lines = ''.join(after_lines_list)
result = DataBlock(
@@ -308,13 +318,15 @@ class GrepText(object):
fp_size = None # gzipped data is usually longer than the file
else:
try:
- status = os.fstat(fp.fileno())
+ file_no = fp.fileno()
+ except (AttributeError, UnsupportedOperation): # doesn't support fileno()
+ fp_size = None
+ else:
+ status = os.fstat(file_no)
if stat.S_ISREG(status.st_mode):
fp_size = status.st_size
else:
fp_size = None
- except AttributeError: # doesn't support fileno()
- fp_size = None
block = self.read_block_with_context(None, fp, fp_size)
while block.end > block.start:
@@ -457,7 +469,7 @@ class GrepText(object):
color_substring = colorize(old_substring, **style)
line = line[:start] + color_substring + line[end:]
total_offset += len(color_substring) - len(old_substring)
-
+
ns = dict(
lineno = i+1,
sep = {PRE: '-', POST: '+', MATCH: ':'}[kind],
@@ -495,8 +507,8 @@ class GrepText(object):
f = sys.stdin
filename = '<STDIN>'
else:
- # 'r' does the right thing for both open ('rt') and gzip.open ('rb')
- f = opener(filename, 'r')
+ # Always open in binary mode
+ f = opener(filename, 'rb')
try:
unique_context = self.do_grep(f)
finally:
@@ -587,7 +599,7 @@ class FileRecognizer(object):
"""
try:
bytes = f.read(self.binary_bytes)
- except Exception, e:
+ except Exception as e:
# When trying to read from something that looks like a gzipped file,
# it may be corrupt. If we do get an error, assume that the file is binary.
return True
@@ -1032,7 +1044,7 @@ def grin_main(argv=None):
sys.stdout.write(report)
except KeyboardInterrupt:
raise SystemExit(0)
- except IOError, e:
+ except IOError as e:
if 'Broken pipe' in str(e):
# The user is probably piping to a pager like less(1) and has exited
# it. Just exit.
@@ -1040,7 +1052,7 @@ def grin_main(argv=None):
raise
def print_line(filename):
- print filename
+ print(filename)
def print_null(filename):
# Note that the final filename will have a trailing NUL, just like
@@ -1073,7 +1085,7 @@ def grind_main(argv=None):
output(filename)
except KeyboardInterrupt:
raise SystemExit(0)
- except IOError, e:
+ except IOError as e:
if 'Broken pipe' in str(e):
# The user is probably piping to a pager like less(1) and has exited
# it. Just exit.
diff --git a/tests/test_file_recognizer.py b/tests/test_file_recognizer.py
index 70b7f5a..a085ce1 100644
--- a/tests/test_file_recognizer.py
+++ b/tests/test_file_recognizer.py
@@ -1,5 +1,6 @@
""" Test the file recognizer capabilities.
"""
+from __future__ import print_function
import gzip
import os
@@ -9,7 +10,7 @@ import sys
import nose
-from grin import FileRecognizer
+from grin import FileRecognizer, ints2bytes, GZIP_MAGIC
def empty_file(filename, open=open):
f = open(filename, 'wb')
@@ -17,13 +18,13 @@ def empty_file(filename, open=open):
def binary_file(filename, open=open):
f = open(filename, 'wb')
- f.write(''.join(map(chr, range(256))))
+ f.write(ints2bytes(range(255)))
f.close()
def text_file(filename, open=open):
- lines = ['foo\n', 'bar\n'] * 100
- lines.append('baz\n')
- lines.extend(['foo\n', 'bar\n'] * 100)
+ lines = [b'foo\n', b'bar\n'] * 100
+ lines.append(b'baz\n')
+ lines.extend([b'foo\n', b'bar\n'] * 100)
f = open(filename, 'wb')
f.writelines(lines)
f.close()
@@ -32,10 +33,9 @@ def fake_gzip_file(filename, open=open):
""" Write out a binary file that has the gzip magic header bytes, but is not
a gzip file.
"""
- GZIP_MAGIC = '\037\213'
f = open(filename, 'wb')
f.write(GZIP_MAGIC)
- f.write(''.join(map(chr, range(256))))
+ f.write(ints2bytes(range(255)))
f.close()
def binary_middle(filename, open=open):
@@ -43,7 +43,7 @@ def binary_middle(filename, open=open):
bytes, then 100 text bytes to test that the recognizer only reads some of
the file.
"""
- text = 'a'*100 + '\0'*100 + 'b'*100
+ text = b'a'*100 + b'\0'*100 + b'b'*100
f = open(filename, 'wb')
f.write(text)
f.close()
@@ -56,25 +56,25 @@ def unreadable_file(filename):
""" Write a file that does not have read permissions.
"""
text_file(filename)
- os.chmod(filename, 0200)
+ os.chmod(filename, 0o200)
def unreadable_dir(filename):
""" Make a directory that does not have read permissions.
"""
os.mkdir(filename)
- os.chmod(filename, 0300)
+ os.chmod(filename, 0o300)
def unexecutable_dir(filename):
""" Make a directory that does not have execute permissions.
"""
os.mkdir(filename)
- os.chmod(filename, 0600)
+ os.chmod(filename, 0o600)
def totally_unusable_dir(filename):
""" Make a directory that has neither read nor execute permissions.
"""
os.mkdir(filename)
- os.chmod(filename, 0100)
+ os.chmod(filename, 0o100)
def setup():
# Make files to test individual recognizers.
@@ -135,22 +135,14 @@ def setup():
text_file('tree/.skip_hidden_file')
os.mkdir('tree/unreadable_dir')
text_file('tree/unreadable_dir/text')
- os.chmod('tree/unreadable_dir', 0300)
+ os.chmod('tree/unreadable_dir', 0o300)
os.mkdir('tree/unexecutable_dir')
text_file('tree/unexecutable_dir/text')
- os.chmod('tree/unexecutable_dir', 0600)
+ os.chmod('tree/unexecutable_dir', 0o600)
os.mkdir('tree/totally_unusable_dir')
text_file('tree/totally_unusable_dir/text')
- os.chmod('tree/totally_unusable_dir', 0100)
+ os.chmod('tree/totally_unusable_dir', 0o100)
-def ensure_deletability(arg, dirname, fnames):
- """ os.path.walk() callback function which will make sure every directory is
- readable and executable so that it may be easily deleted.
- """
- for fn in fnames:
- fn = os.path.join(dirname, fn)
- if os.path.isdir(fn):
- os.chmod(fn, 0700)
def teardown():
files_to_delete = ['empty', 'binary', 'binary_middle', 'text', 'text~',
@@ -168,10 +160,13 @@ def teardown():
os.unlink(filename)
else:
os.rmdir(filename)
- except Exception, e:
- print >>sys.stderr, 'Could not delete %s: %s' % (filename, e)
+ except Exception as e:
+ print('Could not delete %s: %s' % (filename, e), file=sys.stderr)
os.unlink('socket_test')
- os.path.walk('tree', ensure_deletability, None)
+ for dirpath, dirnames, filenames in os.walk('tree'):
+ # Make sure every directory can be deleted
+ for dirname in dirnames:
+ os.chmod(os.path.join(dirpath, dirname), 0o700)
shutil.rmtree('tree')
diff --git a/tests/test_grep.py b/tests/test_grep.py
index aa367f2..f5ee62f 100644
--- a/tests/test_grep.py
+++ b/tests/test_grep.py
@@ -4,52 +4,52 @@ r'''
Set up
>>> import grin
- >>> from cStringIO import StringIO
+ >>> from io import BytesIO
>>> import re
- >>>
- >>> all_foo = """\
+ >>>
+ >>> all_foo = b"""\
... foo
... foo
... foo
... foo
... foo
... """
- >>> first_foo = """\
+ >>> first_foo = b"""\
... foo
... bar
... bar
... bar
... bar
... """
- >>> last_foo = """\
+ >>> last_foo = b"""\
... bar
... bar
... bar
... bar
... foo
... """
- >>> second_foo = """\
+ >>> second_foo = b"""\
... bar
... foo
... bar
... bar
... bar
... """
- >>> second_last_foo = """\
+ >>> second_last_foo = b"""\
... bar
... bar
... bar
... foo
... bar
... """
- >>> middle_foo = """\
+ >>> middle_foo = b"""\
... bar
... bar
... foo
... bar
... bar
... """
- >>> small_gap = """\
+ >>> small_gap = b"""\
... bar
... bar
... foo
@@ -58,8 +58,8 @@ Set up
... bar
... bar
... """
- >>> no_eol = "foo"
- >>> middle_of_line = """\
+ >>> no_eol = b"foo"
+ >>> middle_of_line = b"""\
... bar
... bar
... barfoobar
@@ -70,111 +70,111 @@ Set up
Test the basic defaults, no context.
>>> gt_default = grin.GrepText(re.compile('foo'))
- >>> gt_default.do_grep(StringIO(all_foo))
+ >>> gt_default.do_grep(BytesIO(all_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 0, 'foo\n', [(0, 3)]), (2, 0, 'foo\n', [(0, 3)]), (3, 0, 'foo\n', [(0, 3)]), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(first_foo))
+ >>> gt_default.do_grep(BytesIO(first_foo))
[(0, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(last_foo))
+ >>> gt_default.do_grep(BytesIO(last_foo))
[(4, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(second_foo))
+ >>> gt_default.do_grep(BytesIO(second_foo))
[(1, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(second_last_foo))
+ >>> gt_default.do_grep(BytesIO(second_last_foo))
[(3, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(middle_foo))
+ >>> gt_default.do_grep(BytesIO(middle_foo))
[(2, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(small_gap))
+ >>> gt_default.do_grep(BytesIO(small_gap))
[(2, 0, 'foo\n', [(0, 3)]), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(no_eol))
+ >>> gt_default.do_grep(BytesIO(no_eol))
[(0, 0, 'foo', [(0, 3)])]
- >>> gt_default.do_grep(StringIO(middle_of_line))
+ >>> gt_default.do_grep(BytesIO(middle_of_line))
[(2, 0, 'barfoobar\n', [(3, 6)])]
Symmetric 1-line context.
>>> gt_context_1 = grin.GrepText(re.compile('foo'), options=grin.Options(before_context=1, after_context=1))
- >>> gt_context_1.do_grep(StringIO(all_foo))
+ >>> gt_context_1.do_grep(BytesIO(all_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 0, 'foo\n', [(0, 3)]), (2, 0, 'foo\n', [(0, 3)]), (3, 0, 'foo\n', [(0, 3)]), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_context_1.do_grep(StringIO(first_foo))
+ >>> gt_context_1.do_grep(BytesIO(first_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 1, 'bar\n', None)]
- >>> gt_context_1.do_grep(StringIO(last_foo))
+ >>> gt_context_1.do_grep(BytesIO(last_foo))
[(3, -1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_context_1.do_grep(StringIO(second_foo))
+ >>> gt_context_1.do_grep(BytesIO(second_foo))
[(0, -1, 'bar\n', None), (1, 0, 'foo\n', [(0, 3)]), (2, 1, 'bar\n', None)]
- >>> gt_context_1.do_grep(StringIO(second_last_foo))
+ >>> gt_context_1.do_grep(BytesIO(second_last_foo))
[(2, -1, 'bar\n', None), (3, 0, 'foo\n', [(0, 3)]), (4, 1, 'bar\n', None)]
- >>> gt_context_1.do_grep(StringIO(middle_foo))
+ >>> gt_context_1.do_grep(BytesIO(middle_foo))
[(1, -1, 'bar\n', None), (2, 0, 'foo\n', [(0, 3)]), (3, 1, 'bar\n', None)]
- >>> gt_context_1.do_grep(StringIO(small_gap))
+ >>> gt_context_1.do_grep(BytesIO(small_gap))
[(1, -1, 'bar\n', None), (2, 0, 'foo\n', [(0, 3)]), (3, 1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)]), (5, 1, 'bar\n', None)]
- >>> gt_context_1.do_grep(StringIO(no_eol))
+ >>> gt_context_1.do_grep(BytesIO(no_eol))
[(0, 0, 'foo', [(0, 3)])]
- >>> gt_context_1.do_grep(StringIO(middle_of_line))
+ >>> gt_context_1.do_grep(BytesIO(middle_of_line))
[(1, -1, 'bar\n', None), (2, 0, 'barfoobar\n', [(3, 6)]), (3, 1, 'bar\n', None)]
Symmetric 2-line context.
>>> gt_context_2 = grin.GrepText(re.compile('foo'), options=grin.Options(before_context=2, after_context=2))
- >>> gt_context_2.do_grep(StringIO(all_foo))
+ >>> gt_context_2.do_grep(BytesIO(all_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 0, 'foo\n', [(0, 3)]), (2, 0, 'foo\n', [(0, 3)]), (3, 0, 'foo\n', [(0, 3)]), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_context_2.do_grep(StringIO(first_foo))
+ >>> gt_context_2.do_grep(BytesIO(first_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 1, 'bar\n', None), (2, 1, 'bar\n', None)]
- >>> gt_context_2.do_grep(StringIO(last_foo))
+ >>> gt_context_2.do_grep(BytesIO(last_foo))
[(2, -1, 'bar\n', None), (3, -1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_context_2.do_grep(StringIO(second_foo))
+ >>> gt_context_2.do_grep(BytesIO(second_foo))
[(0, -1, 'bar\n', None), (1, 0, 'foo\n', [(0, 3)]), (2, 1, 'bar\n', None), (3, 1, 'bar\n', None)]
- >>> gt_context_2.do_grep(StringIO(second_last_foo))
+ >>> gt_context_2.do_grep(BytesIO(second_last_foo))
[(1, -1, 'bar\n', None), (2, -1, 'bar\n', None), (3, 0, 'foo\n', [(0, 3)]), (4, 1, 'bar\n', None)]
- >>> gt_context_2.do_grep(StringIO(middle_foo))
+ >>> gt_context_2.do_grep(BytesIO(middle_foo))
[(0, -1, 'bar\n', None), (1, -1, 'bar\n', None), (2, 0, 'foo\n', [(0, 3)]), (3, 1, 'bar\n', None), (4, 1, 'bar\n', None)]
- >>> gt_context_2.do_grep(StringIO(small_gap))
+ >>> gt_context_2.do_grep(BytesIO(small_gap))
[(0, -1, 'bar\n', None), (1, -1, 'bar\n', None), (2, 0, 'foo\n', [(0, 3)]), (3, 1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)]), (5, 1, 'bar\n', None), (6, 1, 'bar\n', None)]
- >>> gt_context_2.do_grep(StringIO(no_eol))
+ >>> gt_context_2.do_grep(BytesIO(no_eol))
[(0, 0, 'foo', [(0, 3)])]
- >>> gt_context_2.do_grep(StringIO(middle_of_line))
+ >>> gt_context_2.do_grep(BytesIO(middle_of_line))
[(0, -1, 'bar\n', None), (1, -1, 'bar\n', None), (2, 0, 'barfoobar\n', [(3, 6)]), (3, 1, 'bar\n', None), (4, 1, 'bar\n', None)]
1 line of before-context, no lines after.
>>> gt_before_context_1 = grin.GrepText(re.compile('foo'), options=grin.Options(before_context=1, after_context=0))
- >>> gt_before_context_1.do_grep(StringIO(all_foo))
+ >>> gt_before_context_1.do_grep(BytesIO(all_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 0, 'foo\n', [(0, 3)]), (2, 0, 'foo\n', [(0, 3)]), (3, 0, 'foo\n', [(0, 3)]), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(first_foo))
+ >>> gt_before_context_1.do_grep(BytesIO(first_foo))
[(0, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(last_foo))
+ >>> gt_before_context_1.do_grep(BytesIO(last_foo))
[(3, -1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(second_foo))
+ >>> gt_before_context_1.do_grep(BytesIO(second_foo))
[(0, -1, 'bar\n', None), (1, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(second_last_foo))
+ >>> gt_before_context_1.do_grep(BytesIO(second_last_foo))
[(2, -1, 'bar\n', None), (3, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(middle_foo))
+ >>> gt_before_context_1.do_grep(BytesIO(middle_foo))
[(1, -1, 'bar\n', None), (2, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(small_gap))
+ >>> gt_before_context_1.do_grep(BytesIO(small_gap))
[(1, -1, 'bar\n', None), (2, 0, 'foo\n', [(0, 3)]), (3, -1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(no_eol))
+ >>> gt_before_context_1.do_grep(BytesIO(no_eol))
[(0, 0, 'foo', [(0, 3)])]
- >>> gt_before_context_1.do_grep(StringIO(middle_of_line))
+ >>> gt_before_context_1.do_grep(BytesIO(middle_of_line))
[(1, -1, 'bar\n', None), (2, 0, 'barfoobar\n', [(3, 6)])]
1 line of after-context, no lines before.
>>> gt_after_context_1 = grin.GrepText(re.compile('foo'), options=grin.Options(before_context=0, after_context=1))
- >>> gt_after_context_1.do_grep(StringIO(all_foo))
+ >>> gt_after_context_1.do_grep(BytesIO(all_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 0, 'foo\n', [(0, 3)]), (2, 0, 'foo\n', [(0, 3)]), (3, 0, 'foo\n', [(0, 3)]), (4, 0, 'foo\n', [(0, 3)])]
- >>> gt_after_context_1.do_grep(StringIO(first_foo))
+ >>> gt_after_context_1.do_grep(BytesIO(first_foo))
[(0, 0, 'foo\n', [(0, 3)]), (1, 1, 'bar\n', None)]
- >>> gt_after_context_1.do_grep(StringIO(last_foo))
+ >>> gt_after_context_1.do_grep(BytesIO(last_foo))
[(4, 0, 'foo\n', [(0, 3)])]
- >>> gt_after_context_1.do_grep(StringIO(second_foo))
+ >>> gt_after_context_1.do_grep(BytesIO(second_foo))
[(1, 0, 'foo\n', [(0, 3)]), (2, 1, 'bar\n', None)]
- >>> gt_after_context_1.do_grep(StringIO(second_last_foo))
+ >>> gt_after_context_1.do_grep(BytesIO(second_last_foo))
[(3, 0, 'foo\n', [(0, 3)]), (4, 1, 'bar\n', None)]
- >>> gt_after_context_1.do_grep(StringIO(middle_foo))
+ >>> gt_after_context_1.do_grep(BytesIO(middle_foo))
[(2, 0, 'foo\n', [(0, 3)]), (3, 1, 'bar\n', None)]
- >>> gt_after_context_1.do_grep(StringIO(small_gap))
+ >>> gt_after_context_1.do_grep(BytesIO(small_gap))
[(2, 0, 'foo\n', [(0, 3)]), (3, 1, 'bar\n', None), (4, 0, 'foo\n', [(0, 3)]), (5, 1, 'bar\n', None)]
- >>> gt_after_context_1.do_grep(StringIO(no_eol))
+ >>> gt_after_context_1.do_grep(BytesIO(no_eol))
[(0, 0, 'foo', [(0, 3)])]
- >>> gt_after_context_1.do_grep(StringIO(middle_of_line))
+ >>> gt_after_context_1.do_grep(BytesIO(middle_of_line))
[(2, 0, 'barfoobar\n', [(3, 6)]), (3, 1, 'bar\n', None)]
'''