Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 36 additions & 13 deletions tail.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@
import os
import sys
import time
from cStringIO import StringIO

class Tail(object):
''' Represents a tail command. '''
def __init__(self, tailed_file):
def __init__(self, tailed_file, max_line_length=float("inf")):
''' Initiate a Tail instance.
Check for file validity, assigns callback function to standard out.

Expand All @@ -38,25 +39,38 @@ def __init__(self, tailed_file):
self.check_file_validity(tailed_file)
self.tailed_file = tailed_file
self.callback = sys.stdout.write
self.max_line_length=max_line_length

def follow(self, s=1):
def follow(self, s=1, poll_time=.01):
''' Do a tail follow. If a callback function is registered it is called with every new line.
Else printed to standard out.

Arguments:
s - Number of seconds to wait between each iteration; Defaults to 1. '''

with open(self.tailed_file) as file_:
# Go to the end of file
file_.seek(0,2)
readBuffer = StringIO()
with open(self.tailed_file, 'rb') as file_:
file_.seek(0, os.SEEK_END)
while True:
curr_position = file_.tell()
line = file_.readline()
if not line:
file_.seek(curr_position)
else:
readBuffer.write(file_.read())
readBuffer.seek(0)
complete = True
for line in readBuffer:
if not line.endswith(os.linesep):
complete = False
break

self.callback(line)
time.sleep(s)
time.sleep(s)

# Catch the slop if the last line isn't complete
readBuffer.truncate(0)
if not complete:
if len(line) > self.max_line_length:
raise TailError("Line exceeds maximum allowed line length")

readBuffer.write(line)

time.sleep(poll_time)

def register_callback(self, func):
''' Overrides default callback function to provided function. '''
Expand All @@ -71,8 +85,17 @@ def check_file_validity(self, file_):
if os.path.isdir(file_):
raise TailError("File '%s' is a directory" % (file_))

class TailError(Exception):
class TailError(IOError):
def __init__(self, msg):
self.message = msg
def __str__(self):
return self.message

if __name__ == '__main__':
if len(sys.argv) < 2:
print "Usage: python tail.py <filename>"
sys.exit(1)

tail = Tail(sys.argv[1])
tail.follow(0)