Python Skeleton Scripts

From PeformIQ Upgrade
Jump to navigation Jump to search

Also check out - Implementing a Standard Library - and - Skel.py


Get CLI Arguments and Read a Config File - Based on a server

Also incorporates use of Python logging module.

#!/usr/bin/env python
#
#  Purpose: 
#
#  $Id:$
#
#-------------------------------------------------------------------------------

"""
  Skeleton Python script

  ...
"""
#-------------------------------------------------------------------------------

import os
import sys
import getopt
import logging

#-------------------------------------------------------------------------------

from datetime import datetime

#-------------------------------------------------------------------------------

__version__   = "1.0.0"
__id__        = "@(#)  skel.py  [%s]  05/03/2008"

verbose_flg   = False

debug_level   = 0

LOGFILE       = "xxxx.log"
PIDFILE       = "xxxx.pid"

tables        = []

log           = None
pid           = None

#===============================================================================

def INFO(msg):
   if log: log.info(' ' + msg)
   if verbose_flg: print "[xxxx]  %s" % msg

#-------------------------------------------------------------------------------

def ERROR(msg):
   if log: log.error(msg)
   sys.stderr.write('[xxxx]  %s\n' % msg)

#-------------------------------------------------------------------------------

def WARNING(msg):
   if log: log.warning('*****' + msg + '*****')
   if verbose_flg: print "[xxxx]  %s" % msg

#===============================================================================

def init():
   global log
   global pid

   pid = os.getpid()

   log  = logging.getLogger('xxx')
   hdlr = logging.FileHandler(LOGFILE)
   fmtr = logging.Formatter('%(asctime)s %(levelname)s %(message)s')

   hdlr.setFormatter(fmtr)
   log.addHandler(hdlr)
   log.setLevel(logging.INFO)

   INFO("Started processing")

   if (not verbose_flg):
      INFO("PID is %d" % pid)

#===============================================================================

def main():
   global verbose_flg
   global debug_level

   try:
      opts, args = getopt.getopt(sys.argv[1:], "dD:vVw?")
   except getopt.error, msg:
      print __doc__
      return 1

   try:
      terminal_type = os.environ["TERM"]
   except KeyError, e:
      print "[xxxx]  Set TERM environment variable and rerun!"
      return 1

   wrk_path  = os.getcwd()
   wrk_dir   = os.path.basename(wrk_path)

   data_dir = wrk_dir  + '/DATA/'
   pid_path = data_dir + PIDFILE

   # os.chdir(data_dir)

   for o, a in opts:
      if o == '-d':
         debug_level   += 1
      elif o == '-D':
         debug_level    = int(a)
      elif o == '-v':
         verbose_flg    = True
      elif o == '-V':
         print "[xxxx]  Version: %s" % __version__
         return 1
      elif o == '-?':
         print __doc__
         return 1

   print "[xxxx]  Working directory is %s" % os.getcwd()

   if (debug_level > 0): print "[xxxx]  Debugging level set to %d" % debug_level

   if args:
      for arg in args:
         print arg

   init()

   return 0

#-------------------------------------------------------------------------------

if __name__ == '__main__' or __name__ == sys.argv[0]:
   try:
      sys.exit(main())
   except KeyboardInterrupt, e:
      print "[xxxx]  Interrupted!"

#-------------------------------------------------------------------------------

"""
Revision History:

     Date     Who   Description
   --------   ---   ------------------------------------------------------------
   20031014   plh   Initial implementation

Problems to fix:

To Do:

Issues:

"""

Reading CSV Files


Daemonized Server

Features:

  • Threaded model.
  • Serevr runs as daemon.
  • Reads config file.
  • Incorporates use of Python logging module.
  • Start, Shutdown, Check methods.
#!/usr/bin/env python
#
#  Purpose: Threaded data server implementation
#
#  $Id:$
#
#---------------------------------------------------------------------

"""
  Uses threaded server model

  Server side: open a socket on a port, listen for
  a message from a client, and accept a request and
  service it.

  The server spawns a thread to handle each client connection.
  Threads share global memory space with main thread;
  This is more portable than fork -- not yet on Windows;

  This version has been extended to use the standard Python
  logging module.

  Add the delimiter to the INI file to allow use of alternate
  delimiters in transmitted data - so data with embedded commas
  can be used.
"""
#---------------------------------------------------------------------

import os
import csv
import sys
import getopt
import thread
import time
import signal
import logging

#---------------------------------------------------------------------

from socket   import *          # get socket constructor and constants
from datetime import datetime

#---------------------------------------------------------------------

__version__   = "1.1.1"
__id__        = "@(#)  server.py  [%s]  05/03/2008"

check_flg     = False
daemon_flg    = False
silent_flg    = False
terminate_flg = False
verbose_flg   = False
wait_flg      = False

debug_level   = 0

HOST          = ''             #  Host server - '' means localhost
PORT          = 9999           #  Listen on a non-reserved port number

sockobj       = None

dserver_dir   = None
data_dir      = None
pid_path      = None

CONFIGFILE    = "server.ini"
LOGFILE       = "server.log"
PIDFILE       = "server.pid"

tables        = []

INVALID       = "INVALID"

log           = None

#=====================================================================

class Group:
   Name     = None
   Idx      = None
   Data     = None

   def __init__(self, name):
      self.Name = name
      self.Idx  = 0
      self.Data = []

   def __str__(self):
      s = "Grp %s  Len %d" % (self.Name, len(self.Data))
      return s

   def append(self, s):
      self.Data.append(s)

   def set(self):
      if len(self.Data) > 0:
         self.Idx  = 0
      else:
         self.Idx  = -1

#---------------------------------------------------------------------

...

#=====================================================================

def INFO(msg):
   if log: log.info(' ' + msg)
   if verbose_flg: print "[server]  %s" % msg

#---------------------------------------------------------------------

def ERROR(msg):
   if log: log.error(msg)
   sys.stderr.write('[server]  %s\n' % msg)

#---------------------------------------------------------------------

def WARNING(msg):
   if log: log.warning('*****' + msg + '*****')
   if verbose_flg: print "[server]  %s" % msg

#=====================================================================

def read_config():
   config_file = data_dir + CONFIGFILE

   try:
      f = open(config_file, 'r')
   except IOError, e:
      ERROR('Open failed: ' + str(e))
      sys.exit(1)

   definition_flg = False

   while True:
      line = f.readline()

      if not line: break

      line = line[:-1]
      line = line.replace('\r','')

      line = line.strip()

      if (line.find("#") != -1): continue

      if (line.find("[Data]") != -1):
         definition_flg = True

      elif (line.find("Description=") != -1):
          description  = line.split("=")

          (name, type, delimiter) = description[1].split(":")

          t = Table(name, type, delimiter)

          INFO(str(t))

          tables.append(t)

   f.close()

#---------------------------------------------------------------------

def process(str):
   if debug_level > 1: INFO("[server::process] len %d  msg %s" % (l, msg))

   ts    = datetime.now().strftime('%Y%m%d%H%M%S')

   reply = 'XXXX'

   return reply

#---------------------------------------------------------------------

def sig_term(signum, frame):
   "SIGTERM handler"

   shutdown()

#---------------------------------------------------------------------

def shutdown():
   INFO("Server shutdown at %s" % datetime.now())

   for i in range(len(tables)):
      tables[i].flush()

   try:
      os.unlink(pid_path)
   except IOError, e:
      ERROR('Unlink failed: ' + str(e))
      sys.exit(1)

   sys.exit(0)

#---------------------------------------------------------------------

def check_running():
   try:
      pfp = open(pid_path, 'r')
   except IOError, (errno, strerror):
      pfp = None
      # ERROR("I/O error(%s): %s" % (errno, strerror))
   except:
      ERROR("Unexpected error:", sys.exc_info()[0])
      raise

   if pfp:
      line = pfp.readline()
      line = line.strip()

      dserver_pid   = int(line)

      noProcess    = 0

      try:
         os.kill(dserver_pid, 0)
      except OSError, e:
         if e.errno == 3:
            noProcess = 1
         else:
            ERROR("kill() failed:" + str(e))
            sys.exit(0)

      if noProcess:
         INFO("[server]  Stale dserver pid file!")
         pfp.close()
         os.unlink(pid_path)

         return None
      else:
         pfp.close()
         return dserver_pid

      return dserver_pid
   else:
      return None

#---------------------------------------------------------------------

def create_pidfile():
   pid = os.getpid()

   try:
      pfp = open(pid_path, 'w')
   except IOError, e:
      ERROR("Open failed - " + str(e))
      sys.exit(0)

   pfp.write("%d" % pid)

   pfp.close()

   INFO("Running server with PID -> %d" % pid)

   return pid

#---------------------------------------------------------------------

def become_daemon():
   pid = os.fork()

   if pid == 0:                                             # In child
      pid = create_pidfile()
      time.sleep(1)
   elif pid == -1:                                # Should not happen!
      ERROR("fork() failed!")
      time.sleep(1)
      sys.exit(0)
   else:                                                   # In Parent
      time.sleep(1)
      sys.exit(0)

   time.sleep(2)

   os.setsid()

   return pid

#---------------------------------------------------------------------

def init():
   pid = check_running()

   if pid:
      print "[server]  Server already running! (pid = %d)" % pid
      sys.exit(0)

   if daemon_flg:
      pid = become_daemon()
   else:
      pid = create_pidfile()

   global log

   log  = logging.getLogger('dserver')
   hdlr = logging.FileHandler(LOGFILE)
   fmtr = logging.Formatter('%(asctime)s %(levelname)s %(message)s')

   hdlr.setFormatter(fmtr)
   log.addHandler(hdlr) 
   log.setLevel(logging.INFO)

   INFO("Started processing")

   read_config()

   if (not silent_flg):
      INFO("Server PID is %d" % pid)

#---------------------------------------------------------------------

def terminate():
   server_pid = check_running()

   if server_pid:
      if (not silent_flg):
         INFO("Terminating server with pid, %d" % server_pid)

      os.kill(server_pid, signal.SIGTERM)

      if (wait_flg):
         while True:
            try:
               kill(server_pid, 0)
            except OSError, e:
               if e.errno == 3:
                  break
               else:
                  ERROR("kill() failed:" + str(e))
                  sys.exit(0)
 
            time.sleep(1)

   return 0

#---------------------------------------------------------------------

def check():
   pid = check_running()

   if pid:
      print "[server]  Server already running! (pid = %d)" % pid
      sys.exit(0)
   else:
      print "[server]  Server not running"

#==== Socket Server ==================================================

def init_connection():
   global sockobj

   sockobj = socket(AF_INET, SOCK_STREAM)  # make a TCP socket object
   sockobj.bind((HOST, PORT))              # bind it to server port number
   sockobj.listen(10)                      # allow upto 10 pending connects

#---------------------------------------------------------------------

def handle_client(connection):             # in spawned thread: reply
   while True:                             # read, write a client socket
      try:
         request = connection.recv(1024)
      except:
         break

      if debug_level > 0: INFO('[server]  Request -> "%s"' % request)

      if not request: break

      reply = process(request)

      if debug_level > 0: INFO('[server]  Reply   -> "%s..."' % reply[0:30])

      connection.send(reply)

   connection.close() 

#---------------------------------------------------------------------

def dispatcher():
   while True:
      # Wait for next connection,
      connection, address = sockobj.accept()

      INFO('Host (%s) - Connected at %s' % (address[0], datetime.now()))

      thread.start_new(handle_client, (connection,))

#=====================================================================

def main():
   global check_flg
   global daemon_flg
   global terminate_flg
   global verbose_flg
   global wait_flg
   global debug_level
   global dserver_dir
   global data_dir
   global pid_path

   try:
      opts, args = getopt.getopt(sys.argv[1:], "cdDsTvVw?")
   except getopt.error, msg:
      print __doc__
      return 1

   try:
      server_dir  = os.environ["SERVER_DIR"]
   except KeyError, e:
      print "[server]  Set SERVER_DIR environment variable and rerun!"
      return 1

   data_dir = server_dir  + '/DATA/'
   pid_path = data_dir + PIDFILE

   wrk_path  = os.getcwd()
   wrk_dir   = os.path.basename(wrk_path)

   os.chdir(data_dir)

   for o, a in opts:
      if o == '-d':
         debug_level   += 1
      elif o == '-c':
         check_flg      = True
      elif o == '-D':
         daemon_flg     = True
      elif o == '-s':
         tsilent_flg    = True
      elif o == '-T':
         terminate_flg  = True
      elif o == '-v':
         verbose_flg    = True
      elif o == '-V':
         print "[dserver]  Version: %s" % __version__
         return 1
      elif o == '-w':
         wait_flg       = True
      elif o == '-?':
         print __doc__
         return 1

   print "[server]  Server working directory is %s" % os.getcwd()

   if check_flg:
      check()
      return 0

   if terminate_flg:
      terminate()
      return 0

   if (debug_level > 0): print "[server]  Debugging level set to %d" % debug_level

   if args:
      for arg in args:
         print arg

   signal.signal(signal.SIGTERM, sig_term)

   init()

   init_connection()

   dispatcher()

   return 0

#---------------------------------------------------------------------

if __name__ == '__main__' or __name__ == sys.argv[0]:
   try:
      sys.exit(main())
   except KeyboardInterrupt, e:
      print "[server]  Interrupted!"
      shutdown()

#---------------------------------------------------------------------

"""
Revision History:

     Date     Who   Description
   --------   ---   --------------------------------------------------
   20031014   plh   Initial implementation

Problems to fix:

To Do:

Issues:

"""