pvl.syslog: simplify SyslogSource, and implement better handling for sys.stdin using tail.Tail
authorTero Marttila <terom@fixme.fi>
Fri, 04 Jan 2013 18:44:58 +0200
changeset 50 0bbe2e7561a1
parent 49 30c615bf7751
child 51 96d204b146b4
pvl.syslog: simplify SyslogSource, and implement better handling for sys.stdin using tail.Tail
pvl/syslog/args.py
pvl/syslog/syslog.py
--- a/pvl/syslog/args.py	Fri Jan 04 18:43:18 2013 +0200
+++ b/pvl/syslog/args.py	Fri Jan 04 18:44:58 2013 +0200
@@ -23,7 +23,7 @@
     syslog.add_option('--syslog-file',          metavar='FILE',
             help="Read syslog messages from given file")
 
-    syslog.add_option('--syslog-tail',          type='float', metavar='POLL',
+    syslog.add_option('--syslog-tail',          type='float', metavar='TIMEOUT',
             help="Continuously poll file")
 
     syslog.add_option('--syslog-raw',           action='store_true',
@@ -42,29 +42,32 @@
     """
 
     if options.syslog_fifo :
-        # read fifo
+        # fifo pipe
         source = fifo.Fifo(options.syslog_fifo)
-        poll = True
+        poll = True # select(source)
 
     elif options.syslog_tail :
         # tail file
-        source = tail.TailFile(options.syslog_file, skip=True)
-        poll = options.syslog_tail # float
+        source = tail.TailFile(options.syslog_file)
+        poll = options.syslog_tail # select(float)
 
     elif options.syslog_file :
         # read file
-        source = open(options.syslog_file)
-        poll = False
+        source = tail.Tail(open(options.syslog_file))
+        poll = False # do not loop, just read up to EOF
 
     elif optional :
-        log.debug("No --syslog source given")
         return None
 
     else :
         # from stdin
-        source = sys.stdin
-        poll = False # XXX: SyslogSource can't handle normal file EOF
-
+        if sys.stdin.isatty() :
+            log.warning("Reading syslog messages from TTY?")
+        
+        source = tail.Tail(sys.stdin)
+        poll = False # XXX: tty vs pipe vs file?
+    
+    # options
     parser = SyslogParser(
         raw     = options.syslog_raw,
     )
@@ -73,9 +76,8 @@
         prog    = options.syslog_prog,
     )
 
-    # build
-    return SyslogSource(source, parser,
-            filter  = filter,
-            poll    = poll,
-    )
+    # chain iterables
+    syslog = filter(parser(source))
 
+    # polling
+    return SyslogSource(syslog, source, poll)
--- a/pvl/syslog/syslog.py	Fri Jan 04 18:43:18 2013 +0200
+++ b/pvl/syslog/syslog.py	Fri Jan 04 18:44:58 2013 +0200
@@ -8,116 +8,91 @@
 
 class SyslogSource (object) :
     """
-        A source of syslog items.
+        Process syslog input from a given source.
+        
+        Implements an iterable mainloop doing continuous polling on the source, using either a timeout or
+        select():able source.
     """
     
-    def __init__ (self, source, parser, filter=None, poll=None) :
+    def __init__ (self, syslog, source, poll=None) :
         """
             Using given underlying line source.
-
-                source          - file-like object with poll() and iter()
-                parser          - process() bytes -> items
-                filter          - filter items
-                poll            - using given polling style:
-                    True            - select() source
-                    float           - given interval
-                    None/False      - single-shot
+                
+                syslog      - iterable
+                source      - source to select() if poll=True
+                poll        - polling behaviour
         """
-
+        
+        self.syslog = syslog
         self.source = source
-        self.parser = parser
-        self.filter = filter
-
-        log.debug("source: %s", source)
-
         self._poll = poll
 
-    def __iter__ (self) :
-        """
-            Read syslog messages from source.
+    def poll (self, poll=None) :
         """
-        
-        # directly iter across source
-        iter = self.source
-        
-        # parse
-        iter = self.parser(iter)
-        
-        # filter ?
-        if self.filter :
-            iter = self.filter(iter)
-        
-        return iter
-
-    def poll (self, timeout=None) :
-        """
-            Poll source for input, with given timeout in seconds (float).
-
-            A timeout of None indicates to block forever, False indicates to never block.
+            Poll our source for input, with given polling behaviour:
+                True    - select() on source
+                False   - peek on source
+                float   - timeout in seconds
 
             Returns True if we have input waiting, False on timeout with no input. None on indeterminate.
         """
 
-        read = write = ()
+        reading = writing = ()
         
-        if timeout is True :
-            timeout = None
-            read += (self.source, )
+        if poll is True :
+            timeout = None # block
+            reading += (self.source, ) # file-like object with fileno()
 
-        elif timeout is False :
-            timeout = 0.0
+        elif not poll :
+            timeout = 0.0 # do not block
+
+        else :
+            timeout = float(poll)
 
         log.debug("%s", timeout)
     
         # select
-        read, write, ex = select.select(read, write, [], timeout)
+        readable, writeable, ex = select.select(reading, writing, [], timeout)
 
-        if read :
+        if readable :
             return True
 
-        else :
+        elif reading :
             # timeout
             return False
 
-    def loop (self) :
+        else :
+            # unknown
+            return None
+
+    def main (self, poll=None) :
         """
-            Continuously read items from syslog source, blocking as suitable.
+            yield items from syslog source, polling as given.
 
             Returns once no more lines are available.
 
-            XXX: reconnect?
+            XXX: reconnect? or source takes care of that..
+            TODO: SIGINT -> finish iteration and return?
         """
+
+        if poll is None :
+            poll = self._poll
         
         # mainloop
         while True :
-            log.debug("tick")
-
             # pull in messages
-            for item in self :
+            for item in self.syslog :
                 log.debug("%s", item)
                 yield item
             
             # poll
-            if self._poll :
+            if poll :
                 # wait
-                self.poll(self._poll)
+                self.poll(poll)
             else :
                 # done
                 break
 
         log.debug("exit")
-    
-    def stop (self) :
-        """
-            Stop loop after current iteration.
-        """
 
-        self._poll = False
-
-    def main (self) :
-        """
-            Mainloop.
-        """
-
-        # TODO: SIGINT -> stop()
-        return loop()
+    __iter__ = main