Sometimes when I write a little python script – with only a few lines of code – it starts growing and I keep adding functionality. When a certain threshold is reached I start thinking that there should be a logger used (instead of print) and maybe a configuration file (instead of all these commandline switches). Since python coding is not something I do on a daily basis python code is not something I come up with just like that. So I either copy & paste code from other scripts or even have to look up how exactly the logger is set up putty download , how a configuration file is loaded or how signal handler is registered. It’s always the same story and as a keen Emacs user I finally made it an end 🙂
Using emacs auto-insert-mode. In my configuration auto-insert-mode inserts the file „template.py“ from my „~/.emacs.d/templates/“ directory and inserts into the buffer whenever I open a yet non-existing file with the .py extension:

(setq auto-insert-directory "~/.emacs.d/templates/"
      auto-insert-query nil)
(add-to-list 'auto-insert-alist '(".*\\.py[3]?$" . [ "template.py" ]))

This works pretty well and of course template.py hosts my nitty gritty python boiler plate that I want to see in my scripts (no matter how small they are 🙂 ):

#!/usr/bin/python3

import logging
import logging.handlers
import signal
import sys
import os
import time
import argparse

# Author     : Matthias
# Description: Python script template

class Application:

    name               = ''
    version            = ''
    log                = None
    properties         = None
    parser             = None
    args               = None

    def __init__(self):
        signal.signal(signal.SIGINT, Application.signal_int_handler)        
        parser = argparse.ArgumentParser(description="", epilog="")
        parser.add_argument("-v", "--verbose", help="Be more verbose when logging", action="store_true")
        parser.add_argument("-P", "--properties", help="A properties file for use by the application", type=str)
        parser.add_argument("-l", "--loghost", help="Name of host to receive log messages", default="127.0.0.1")
        parser.add_argument("-p" puttygen download , "--logport", help="Port of service to receive log messages", type=int, default=logging.handlers.DEFAULT_TCP_LOGGING_PORT)
        parser.add_argument("-d", "--logdomain", help="Domain for logging", default="this-script")
        parser.add_argument("-r", "--remotelog", help="Enable remote logging with default host and port", action="store_true")
        self.args = parser.parse_args()
        self.parser = parser
        self.setup_logging()
        self.read_properties(self.args.properties)
        
    def setup_logging(self):
        self.log = logging.getLogger(self.args.logdomain)
        rootlogger = logging.getLogger()
        formatstring='%(asctime)s %(levelname)-15s %(name)s # %(message)s'
        formatter = logging.Formatter(fmt=formatstring, datefmt='%d.%m.%y %I:%M:%S')
        handler = None
        if self.args.remotelog:
            handler = logging.handlers.SocketHandler(self.loghost_name, self.loghost_port)
        else:
            handler = logging.StreamHandler(sys.stderr)
        handler.setFormatter(formatter)
        rootlogger.addHandler(handler)
        level = logging.INFO
        if self.args.verbose:
            level = logging.DEBUG
        self.log.setLevel(level)
        rootlogger.setLevel(level)
        self.log.propagate=1
        
    def read_properties(self, filename):
        """ Read the file passed as parameter as a properties file. """
        if filename:
            properties = {}
            comment_char = "#"
            seperator = ":"
            with open(filename, "rt") as f:
                for line in f:
                    l = line.strip()
                    if l and not l.startswith(comment_char):
                        key_value = l.split(seperator)
                        key = key_value[0].strip()
                        value = seperator.join(key_value[1:]).strip().strip('"') 
                        properties[key] = value 
            self.properties = properties

    @staticmethod
    def signal_int_handler(signal, frame):
        interrupt_msg = '\r\n\r\n{} {} terminated by keyboard interrupt'.format(Application.name, Application.version)
        print(interrupt_msg)
        exit(0)

    def run(self):
        pass

def main():
    app = Application()
    app.log.info('{} {} is starting'.format(app.name, app.version))
    app.run()
    app.log.info('{} {} is done'.format(app.name, app.version))

if __name__ == '__main__':
    main()

#
# Done
#
# # # end of script