Since I often write scripts in Python for work, I will post the template code as a memorandum.
This template does the following:
app_home/
├ bin/
│ └ my_batch.py #← Script to execute
├ conf/
│ └ my_batch_conf.py #← Setting class
├ lib/
│ ├ __init__.py #← Required to load the module
│ └ my_lib.py #← Library
├ tests/
│ └ test_my_lib.py #← Unit test code
├ log/ #← Log output destination
└ Pipfile #← List the libraries to use
Contents of the main body my_batch.py
import sys
import os
import click
import logging
#The parent directory is the application home(${app_home})Set to
app_home = os.path.abspath(os.path.join( os.path.dirname(os.path.abspath(__file__)) , ".." ))
# ${app_home}To the library load path
sys.path.append(os.path.join(app_home))
#Load your own library
from lib.my_lib import MyLib
#Load configuration class
from conf.my_batch_conf import MyBatchConf
#Command line argument handling. must_arg is a required option, optional_arg is optional
@click.command()
@click.option('--must_arg','-m',required=True)
@click.option('--optional_arg','-o',default="DefaultValue")
def cmd(must_arg,optional_arg):
#Program name without extension from own name(${prog_name})To
prog_name = os.path.splitext(os.path.basename(__file__))[0]
#Logger settings
#format
log_format = logging.Formatter("%(asctime)s [%(levelname)8s] %(message)s")
#level
logger = logging.getLogger()
logger.setLevel(logging.INFO)
#Handler to standard output
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(log_format)
logger.addHandler(stdout_handler)
#Handler to log file
file_handler = logging.FileHandler(os.path.join(app_home,"log", prog_name + ".log"), "a+")
file_handler.setFormatter(log_format)
logger.addHandler(file_handler)
#Start processing
try:
#Log output
logger.info("start")
#Use command line arguments
logger.error(f"must_arg = {must_arg}")
logger.error(f"optional_arg = {optional_arg}")
#Library call
mylib = MyLib()
logger.info(mylib.get_name())
#Use of set values
logger.info(MyBatchConf.key1)
logger.info(MyBatchConf.key2)
#Even if an exception occurs ...
raise Exception("My Exception")
except Exception as e:
#Catch and log exceptions
logger.exception(e)
sys.exit(1)
if __name__ == '__main__':
cmd()
Contents of configuration class conf / my_batch_conf.py
class MyBatchConf(object):
key1 = "key1_value"
key2 = True
*) I used to use configParser before, but the config class doesn't need to be parsed, and the IDE complements it, so I don't use it anymore.
Contents of library my_lib.py
class MyLib(object):
def get_name(self):
return "my_lib"
Contents of the library unit test code test_my_lib.py
import sys,os
import unittest
# ../Put lib in the load path
app_home = os.path.abspath(os.path.join( os.path.dirname(os.path.abspath(__file__)) , ".." ))
sys.path.append(os.path.join(app_home,"lib"))
# ../Loading the library under test
from my_lib import MyLib
class TestMyLib(unittest.TestCase):
def test_get_name(self):
ml = MyLib()
self.assertEqual("my_lib", ml.get_name())
if __name__ == '__main__':
unittest.main()
Execute with no arguments
$ python bin/my_batch.py
Execution result (manual appears by click function)
Usage: my_batch.py [OPTIONS]
Try "my_batch.py --help" for help.
Error: Missing option "--must_arg" / "-m".
Execute with arguments
$ python bin/my_batch.py -m SpecifiedValue
Execution result
2019-06-28 16:42:53,335 [ INFO] start
2019-06-28 16:42:53,335 [ ERROR] must_arg = SpecifiedValue
2019-06-28 16:42:53,335 [ ERROR] optional_arg = DefaultValue
2019-06-28 16:42:53,335 [ INFO] my_lib
2019-06-28 16:42:53,336 [ INFO] key1_value
2019-06-28 16:42:53,336 [ INFO] True
2019-06-28 16:42:53,336 [ ERROR] My Exception
Traceback (most recent call last):
File "bin/my_batch.py", line 62, in cmd
raise Exception("My Exception")
Exception: My Exception
The same content is output to the log (log / my_batch.log)
2019-06-28 16:42:53,335 [ INFO] start
2019-06-28 16:42:53,335 [ ERROR] must_arg = SpecifiedValue
2019-06-28 16:42:53,335 [ ERROR] optional_arg = DefaultValue
2019-06-28 16:42:53,335 [ INFO] my_lib
2019-06-28 16:42:53,336 [ INFO] key1_value
2019-06-28 16:42:53,336 [ INFO] True
2019-06-28 16:42:53,336 [ ERROR] My Exception
Traceback (most recent call last):
File "bin/my_batch.py", line 62, in cmd
raise Exception("My Exception")
Exception: My Exception
Run unit tests
bash-3.2$ python tests/test_my_lib.py
Execution result
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
tests All test code test_ * .py
below is executed at once
$ python -m unittest discover tests "test_*.py"
This code is also available on github → https://github.com/fetaro/python-batch-template-for-v3
This template does the following:
Difference from python3 series
#-*-coding: utf-8-*-
is required at the beginning of the file (because the source code has Japanese).format ()
to format the string instead of the f expressionapp_home/
├ bin/
│ └ my_batch.py #← Script to execute
├ conf/
│ └ my_batch.conf #← Configuration file
├ lib/
│ ├ __init__.py #← Required to load the module
│ └ my_lib.py #← Library
├ tests/
│ └ test_my_lib.py#← Unit test code
└ log/ #← Log output destination
Contents of my_batch.py
# -*- coding: utf-8 -*-
import sys
import os
from optparse import OptionParser
from ConfigParser import ConfigParser
import logging
#The parent directory is the application home(${app_home})Set to
app_home = os.path.abspath(os.path.join( os.path.dirname(os.path.abspath(__file__)) , ".." ))
# ${app_home}/Add lib to library load path
sys.path.append(os.path.join(app_home,"lib"))
#Load your own library
from my_lib import MyLib
if __name__ == "__main__" :
#Program name without extension from own name(${prog_name})To
prog_name = os.path.splitext(os.path.basename(__file__))[0]
#Optional perspective
usage = "usage: %prog (Argument-1) [options]"
parser = OptionParser(usage=usage)
parser.add_option("-d", "--debug",dest="debug", action="store_true", help="debug", default=False)
#Store options and arguments separately
(options, args) = parser.parse_args()
#Argument check
if len(args) != 1:
sys.stderr.write("argument error. use -h or --help option\n")
sys.exit(1)
#Read the config file
config = ConfigParser()
conf_path = os.path.join(app_home,"conf", prog_name + ".conf")
config.read(conf_path)
#Logger settings
#format
log_format = logging.Formatter("%(asctime)s [%(levelname)8s] %(message)s")
#level
logger = logging.getLogger()
logger.setLevel(logging.INFO)
#Handler to standard output
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(log_format)
logger.addHandler(stdout_handler)
#Handler to log file
file_handler = logging.FileHandler(os.path.join(app_home,"log", prog_name + ".log"), "a+")
file_handler.setFormatter(log_format)
logger.addHandler(file_handler)
#Start processing
try:
#Log output
logger.info("start")
logger.error("arg1 = {0}".format(args[0]))
#Get options
logger.info(options.debug)
#Library call
mylib = MyLib()
logger.info(mylib.get_name())
#Read setting value
logger.info(config.get("section1","key1"))
logger.info(config.getboolean("section2","key2"))
#Even if an exception occurs ...
raise Exception("My Exception")
except Exception as e:
#Catch and log exceptions
logger.exception(e)
sys.exit(1)
Contents of my_batch.conf
[section1]
key1 = key1_value
[section2]
key2 = true
Contents of my_lib.py
class MyLib(object):
def get_name(self):
return "my_lib"
Contents of unit test code test_my_lib.py
# -*- coding: utf-8 -*-
import sys,os
import unittest
# ../Put lib in the load path
app_home = os.path.abspath(os.path.join( os.path.dirname(os.path.abspath(__file__)) , ".." ))
sys.path.append(os.path.join(app_home,"lib"))
# ../Loading the library under test
from my_lib import MyLib
class TestMyLib(unittest.TestCase):
def test_get_name(self):
ml = MyLib()
self.assertEqual("my_lib", ml.get_name())
if __name__ == '__main__':
unittest.main()
bash-3.2$ python bin/my_batch.py argument1
2016-08-16 23:25:03,492 [ INFO] start
2016-08-16 23:25:03,492 [ ERROR] arg1 = argument1
2016-08-16 23:25:03,492 [ INFO] False
2016-08-16 23:25:03,492 [ INFO] my_lib
2016-08-16 23:25:03,492 [ INFO] key1_value
2016-08-16 23:25:03,492 [ INFO] True
2016-08-16 23:25:03,492 [ ERROR] My Exception
Traceback (most recent call last):
File "bin/my_batch.py", line 73, in <module>
raise Exception("My Exception")
Exception: My Exception
Contents of the log (log / my_batch.log)
2016-08-16 23:25:03,492 [ INFO] start
2016-08-16 23:25:03,492 [ ERROR] arg1 = argument1
2016-08-16 23:25:03,492 [ INFO] False
2016-08-16 23:25:03,492 [ INFO] my_lib
2016-08-16 23:25:03,492 [ INFO] key1_value
2016-08-16 23:25:03,492 [ INFO] True
2016-08-16 23:25:03,492 [ ERROR] My Exception
Traceback (most recent call last):
File "bin/my_batch.py", line 73, in <module>
raise Exception("My Exception")
Exception: My Exception
Unit test execution results
bash-3.2$ python tests/test_my_lib.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
tests When testing all the test code test_ *. Py
below at once
bash-3.2$ python -m unittest discover tests "test_*.py"
The contents of this file are also published on github. Please do as you like → https://github.com/fetaro/python-batch-template
Recommended Posts