仕事上Pythonでスクリプトをよく書くので、雛形コードを備忘録も兼ねて載せておきます。
この記事はpython2系を対象としています。python3系はこちら
python2系の雛形
概要
この雛形では以下のことをしています。
- オプションのパース
- 設定ファイル読み込み
- ログ出力
- ライブラリ読み込み
- ライブラリの単体テスト
python3系との違い
- pyhon2系で動作
- コマンドライン引数のパースにclickではなくoptparseを利用
- ファイルの先頭に文字コーディングの宣言
# -*- coding: utf-8 -*-
が必要(ソースコードに日本語があるため) - 文字列のフォーマットに f式ではなく
.format()
を使わなければならない - ConfigParserのパッケージ名が大文字
ファイルの配置
app_home/
├ bin/
│ └ my_batch.py #←実行するスクリプト
├ conf/
│ └ my_batch.conf #←設定ファイル
├ lib/
│ ├ __init__.py #←モジュールをロードするのに必要
│ └ my_lib.py #←ライブラリ
├ tests/
│ └ test_my_lib.py#←単体テストコード
└ log/ #←ログ出力先
内容
my_batch.pyの内容
# -*- coding: utf-8 -*-
import sys
import os
from optparse import OptionParser
from ConfigParser import ConfigParser
import logging
# 親ディレクトリをアプリケーションのホーム(${app_home})に設定
app_home = os.path.abspath(os.path.join( os.path.dirname(os.path.abspath(__file__)) , ".." ))
# ${app_home}/libをライブラリロードパスに追加
sys.path.append(os.path.join(app_home,"lib"))
# 自前のライブラリをロード
from my_lib import MyLib
if __name__ == "__main__" :
# 自身の名前から拡張子を除いてプログラム名(${prog_name})にする
prog_name = os.path.splitext(os.path.basename(__file__))[0]
# オプションのパース
usage = "usage: %prog (Argument-1) [options]"
parser = OptionParser(usage=usage)
parser.add_option("-d", "--debug",dest="debug", action="store_true", help="debug", default=False)
# オプションと引数を格納し分ける
(options, args) = parser.parse_args()
# 引数のチェック
if len(args) != 1:
sys.stderr.write("argument error. use -h or --help option\n")
sys.exit(1)
# 設定ファイルを読む
config = ConfigParser()
conf_path = os.path.join(app_home,"conf", prog_name + ".conf")
config.read(conf_path)
# ロガーの設定
# フォーマット
log_format = logging.Formatter("%(asctime)s [%(levelname)8s] %(message)s")
# レベル
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# 標準出力へのハンドラ
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(log_format)
logger.addHandler(stdout_handler)
# ログファイルへのハンドラ
file_handler = logging.FileHandler(os.path.join(app_home,"log", prog_name + ".log"), "a+")
file_handler.setFormatter(log_format)
logger.addHandler(file_handler)
# 処理開始
try:
# ログ出力
logger.info("start")
logger.error("arg1 = {0}".format(args[0]))
# オプション取得
logger.info(options.debug)
# ライブラリ呼び出し
mylib = MyLib()
logger.info(mylib.get_name())
# 設定値読み込み
logger.info(config.get("section1","key1"))
logger.info(config.getboolean("section2","key2"))
# 例外が発生しても・・・
raise Exception("My Exception")
except Exception as e:
# キャッチして例外をログに記録
logger.exception(e)
sys.exit(1)
my_batch.confの内容
[section1]
key1 = key1_value
[section2]
key2 = true
my_lib.pyの内容
class MyLib(object):
def get_name(self):
return "my_lib"
単体テストコードtest_my_lib.py
の内容
# -*- coding: utf-8 -*-
import sys,os
import unittest
# ../libをロードパスに入れる
app_home = os.path.abspath(os.path.join( os.path.dirname(os.path.abspath(__file__)) , ".." ))
sys.path.append(os.path.join(app_home,"lib"))
# ../テスト対象のライブラリのロード
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
ログ(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
単体テストの実行結果
bash-3.2$ python tests/test_my_lib.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
tests以下の全テストコードtest_*.py
をまとめてテストする場合
bash-3.2$ python -m unittest discover tests "test_*.py"
githubでもこのファイルの内容を公開しています。お好きにどうぞ→ https://github.com/fetaro/python-batch-template