#!/usr/bin/python3
#+
# inotipy example: display content being appended to a file
# as it appears. Using inotify means the process consumes zero
# CPU time while waiting for more data, but is able to respond
# quickly when data does appear. Invoke this script as follows:
#
#     tailing [--timeout=«timeout»] «filename»
#
# where «filename» is the name of the file to watch, and «timeout»
# is an optional timeout in seconds (fractions allowed); if no
# further output appears after this interval has elapsed, the
# script terminates. If no timeout is specified, the script waits
# forever.
#
# The initial content of «filename» at script startup is ignored;
# only new output is displayed.
#
# Copyright 2020 by Lawrence D'Oliveiro <ldo@geek-central.gen.nz>. This
# script is licensed CC0
# <https://creativecommons.org/publicdomain/zero/1.0/>; do with it
# what you will.
#-

import sys
import os
import getopt
import inotify
from inotify import \
    IN

timeout = None
opts, args = getopt.getopt \
  (
    sys.argv[1:],
    "",
    ["timeout="]
  )
for keyword, value in opts :
    if keyword == "--timeout" :
        timeout = float(value)
        if timeout < 0 :
            timeout = None
        #end if
    #end if
#end for
if len(args) != 1 :
    raise getopt.GetoptError("can only watch one file for now")
#end if
the_file, = args

loop = inotify.get_event_loop()

async def mainline() :
    fd = os.open(the_file, os.O_RDONLY)
    os.lseek(fd, 0, os.SEEK_END)
    watcher = inotify.Watcher.create()
    watcher.watch(the_file, IN.MODIFY)
    async for event in watcher.iter_async \
      (
        stop_on = {inotify.STOP_ON.TIMEOUT},
        timeout = timeout
      ) :
        while True :
            data = os.read(fd, 512)
            if len(data) == 0 :
                break
            sys.stdout.write(data.decode())
        #end while
        os.lseek(fd, 0, os.SEEK_END) # in case of truncation
        sys.stdout.flush()
    #end for
#end mainline

try :
    loop.run_until_complete(mainline())
except KeyboardInterrupt :
    sys.stdout.write("\n") # ensure shell prompt appears at start of new line
#end try
