LoginSignup
1
1

More than 3 years have passed since last update.

pythonでバッチスクリプトを書くときの雛形(python 2系)

Posted at

仕事上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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1