"""
Iterate over input lines in files.
Can be read up to eof, on blocking inputs, or polled with a timeout (see: pvl.syslog.syslog.SyslogSource)
"""
import os
import logging; log = logging.getLogger('pvl.syslog.tail')
class Tail (object) :
"""
Follow a file-like object, reading lines until EOF.
Works with python file objects that buffer readlines() when using e.g. `tail -f ... | python -u ...`.
"""
def __init__ (self, file) :
self.file = file
def readline (self) :
"""
Reads a line from the file, without trailing \n
Raises EOF on end-of-file.
"""
line = self.file.readline()
if not line :
raise EOFError()
else :
return line.rstrip('\n')
def readlines (self) :
"""
Reads any available lines from the file.
"""
while True :
try :
line = self.readline()
except EOFError :
log.debug("EOF")
break
else :
yield line
def skip (self) :
"""
Skip any available lines.
"""
for line in self.readlines() :
pass
__iter__ = readlines
class TailFile (Tail) :
"""
Follow a file on the filesystem, reading lines until EOF, and re-opening if replaced.
"""
def __init__ (self, path) :
self.path = path
self.reopen()
def stat (cls) :
"""
Return a key identifying the file at our path.
"""
st = os.stat(self.path)
return st.st_dev, st.st_ino
def open (cls) :
return open(self.path, 'r')
def reopen (cls) :
self.file = self.open()
self._stat = self.stat()
def changed (self) :
"""
Has the underlying file changed?
"""
return self.stat() != self._stat
def readlines (self, eof_mark=False) :
"""
Reads any available lines from the file.
Reopens the file on EOF if the underlying file changed.
eof_mark: yields a special None line when this happens.
"""
while True :
try :
line = self.readline()
except EOFError :
if self.changed() :
log.debug("EOF: reopen")
self.reopen()
if eof_mark :
yield None # special token
# keep going
continue
else :
log.debug("EOF")
break # wait
else :
yield line
__iter__ = readlines