diff -r 6842794c20e8 -r ed410776effd pvl/syslog/tail.py --- a/pvl/syslog/tail.py Tue Feb 19 19:28:40 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -""" - Iterate over input lines in filesystem files. -""" - -import os - -import logging; log = logging.getLogger('pvl.syslog.tail') - -class Tail (object) : - """ - Follow a file on the filesystem, reading lines until EOF, and re-opening if replaced. - - Never blocks, no fileno() to poll. Just poll(timeout=POLL). - - Not writable. - """ - - POLL = 2.0 - - def __init__ (self, path, skip=None, **opts) : - log.debug("%s", path) - - self.path = path - self.file = self.stat = None # closed - - self.open() - - if skip : - self.skip() - - def _stat (self) : - """ - Return a key identifying the file at our path. - """ - - st = os.stat(self.path) - - stat = st.st_dev, st.st_ino - - return stat - - def _open (self) : - """ - Return the opened file. - """ - - return open(self.path, 'r') - - def open (self) : - """ - Re-opens our file when closed. - - Raises ValueError if already open. - """ - - if self.file is None : - # XXX: use fstat for "atomic" open+stat? - self.file = self._open() - self.stat = self._stat() - - log.debug("%s: %s: %s", self, self.file, self.stat) - - else : - raise ValueError("%s: open already open tail" % (self, )) - - def changed (self) : - """ - Has the underlying file changed? - """ - - return self._stat() != self.stat - - def readline (self) : - """ - Reads a line from the file, without trailing \n. - - Returns None on EOF. - """ - - line = self.file.readline() - - if not line : - line = None - else : - line = line.rstrip('\r\n') - - log.debug("%s", line) - - return line - - 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 : - line = self.readline() - - if line is not None : - yield line - - elif self.changed() : - log.debug("EOF: reopen") - - self.close() - self.open() - - if eof_mark : - yield None # special token - - # keep going - continue - - else : - log.debug("EOF: wait") - break - - __iter__ = readlines - - def skip (self) : - """ - Skip any available lines. - """ - - log.debug("%s", self) - - for line in self.readlines() : - pass - - def close (self) : - """ - Close our file, if open. Further operations raise ValueError. - - log.warn's if already closed. - """ - - if self.file : - log.debug("%s", self) - self.file.close() - self.file = None - else : - log.warn("%s: close on already closed tail", self) - - def __str__ (self) : - return self.path -