環境毎の設定ファイルを管理・出力
Python勉強中!何か作りたいものがないかと思い、アプリのリリースで環境毎に設定値に差分があり、毎回手作業で確認しながらやるのがつまらなすぎてしんどい、ということでbottleのSimpleTemplateを使って環境毎の設定ファイルを生成できないかやってみた。
下記のソースはyamlを使うので、lib_yamlにruamelをpipでインストールする必要がある。なぜこうしたのかはJAVAと違って、Pythonはwindowsで作ったものをzipで固めて、そのままLinux(インターネット接続なし)で展開して使うことができなそうだから。
bottle.pyもダウンロードしておく必要あり。
変更履歴
[v02]
・bottleのtemplate()ではshift_jisでエラーとなってしまうため、引数で色々指定できるSimpleTemplateに変更。
・config.ymlでテンプレートファイルに対してencodingをオプションで指定できるように変更。指定する値はshift_jis、utf-8など。
[v03]
・以下のようにxml: “on”を指定した場合、xml部分をフォーマットで改行した形でtemplateを書き換えるように対応。
val: “<note><to>a</to><from>b</from><heading>no title</heading><body>test</body></note>”
xml: “on”
構成
# tree
├── bottle.py
├── config.yml
├── createConfigTile.py
├── lib_yaml
├── log
├── loggerConfig.yml
└── template
└── appA
├── config1.txt
└── config2.txt
from bottle import SimpleTemplate, template
import os
import sys
from logging import Logger, getLogger, config
from typing import Dict
#import xml.dom.minidom
import xml.etree.ElementTree as ET
import re
class const:
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if name in self.__dict__:
raise self.ConstError("Can't rebind const (%s)" % name)
self.__dict__[name] = value
class Main:
def __init__(self):
# 相対パスでも動くよう、カレントディレクトリを変更する
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# PYTHONPATHにzipファイルを追加
basepath = os.path.split(os.path.realpath(__file__))[0]
sys.path.insert(0, os.path.join(basepath, 'lib_yaml/'))
import ruamel.yaml
import ruamel
yaml = ruamel.yaml.YAML()
with open('./loggerConfig.yml') as stream:
logconfig = yaml.load(stream)
os.makedirs("./log/", exist_ok=True)
config.dictConfig(logconfig)
self.logger = getLogger(self.__class__.__name__)
self.logger.info("Main class init")
def exe(self):
self.logger.info("execute exe()")
self.yyy = ConfigCreate()
self.yyy.exe()
class ConfigCreate:
C_ = const()
C_.NEWLINE_CRLF = 0
C_.NEWLINE_LF = 1
C_.NEWLINE_CR = 2
C_.NEWLINE_UNKNOWN = -1
BB=3
def __init__(self):
self.logger = getLogger(self.__class__.__name__)
self.logger.info("ConfigCreate class init")
def gainkaiGyo(self, filepath : str, encoding : str) -> int:
kcrlf = '\r\n'
klf = '^([^\r]*)\n'
kcr = '\r(?!\n)'
re_crlf = re.compile(kcrlf)
re_lf = re.compile(klf)
re_cr = re.compile(kcr)
with open(filepath, mode='r', encoding=encoding, newline='') as fco:
data = fco.readline()
while data:
#print("content:" + data)
if re_crlf.search(data) is not None:
#print("crlf")
return self.C_.NEWLINE_CRLF #crlf
elif re_lf.search(data) is not None:
#print("lf")
return self.C_.NEWLINE_LF #lf
elif re_cr.search(data) is not None:
#print("cr")
return self.C_.NEWLINE_CR #cr
else:
#print("不明")
return self.C_.NEWLINE_UNKNOWN #不明
return -1 #不明
def gainTemplateVal(self, ooo: dict, new_line: int) -> dict:
"""
bottleライブラリのテンプレートに渡すパラメータを作成する。
以下のようにxml: "on"設定がある場合は、引数(new_line)に従って改行を付与する
var:
x1:
val: "<note><to>a</to><from>b</from><heading>no title</heading><body>test</body></note>"
xml: "on"
"""
if new_line == self.C_.NEWLINE_CRLF :
self.logger.debug( '改行=>CRLF' )
elif new_line == self.C_.NEWLINE_LF :
self.logger.debug( '改行=>LF' )
elif new_line == self.C_.NEWLINE_CR :
self.logger.debug( '改行=>CR' )
elif new_line == self.C_.NEWLINE_UNKNOWN :
self.logger.debug( '改行=>不明' )
templateVal: Dict = {}
for kk, vv in ooo.items():
if isinstance(vv, dict):
self.logger.info("dict =========")
self.logger.info(vv)
for key, vvv in vv.items():
self.logger.info(key + "," + vvv)
# templateVal[key] = vvv
if "val" in vv and "xml" in vv:
if vv['xml'] == 'on':
#dom = xml.dom.minidom.parseString(vv['val'])
#templateVal[kk] = dom.toxml()
ooo = ET.fromstring(vv['val'])
ET.indent(ooo)
#改行を一旦\nに統一
templateVal[kk] = ET.tostring(element=ooo, encoding='unicode').replace('\r\n', '\n').replace('\r', '\n')
if new_line == self.C_.NEWLINE_CRLF :
templateVal[kk] = templateVal[kk].replace('\n', '\r\n')
elif new_line == self.C_.NEWLINE_LF :
#上の処理で\nにしたので実施する必要ないけど一応実施
templateVal[kk] = templateVal[kk].replace('\n', '\n')
elif new_line == self.C_.NEWLINE_CR :
templateVal[kk] = templateVal[kk].replace('\n', '\r')
elif new_line == self.C_.NEWLINE_UNKNOWN :
#NEWLINE_UNKNOWNの場合は改行をLFにする
templateVal[kk] = templateVal[kk].replace('\n', '\r')
else:
templateVal[kk] = vv
return templateVal
def exe(self):
import ruamel.yaml
import ruamel
yaml = ruamel.yaml.YAML()
with open('./config.yml') as stream:
data = yaml.load(stream)
for file_ in data['files']:
self.logger.info('-----------------------------------------------')
self.logger.info('テンプレートファイル名:' + file_['filename'])
#デフォルトエンコード:utf-8
enc = "utf-8"
if "encoding" in file_:
self.logger.info('encoding:' + file_['encoding'])
enc = file_['encoding']
for kankyo in file_['kankyo']:
self.logger.info(kankyo)
for app in kankyo['apps']:
self.logger.info("---設定ファイル作成開始 " + "環境=" + kankyo['kankyoname'] + ", アプリ=" + app['appname'] + "---")
self.logger.info("設定=>" + str(app))
#作成する設定ファイルのディレクトリを決定する
index = file_['filename'].find('./template/')
inifile_excludePathTemplate = file_['filename'][slice(index + len('./template/'), len(file_['filename']))]
templateFolderPath = os.path.dirname(inifile_excludePathTemplate)
self.logger.info("templateFolderPath:" + templateFolderPath)
folderpath = './app_config/' + kankyo['kankyoname'] + "/" + app['appname'] + "/" + templateFolderPath
self.logger.info("出力フォルダ:" + folderpath)
os.makedirs(folderpath, exist_ok=True)
#設定ファイルのファイル名を取得
basename = os.path.basename(inifile_excludePathTemplate)
template_newline = self.gainkaiGyo(file_['filename'], enc)
templateVal: Dict = self.gainTemplateVal(app['var'], template_newline)
self.logger.info(templateVal)
self.logger.info("テンプレートファイルより設定ファイル作成実施")
with open(folderpath + "/" + basename, mode='w', encoding=enc, newline='') as f:
with open(file_['filename'], mode='r', encoding=enc, newline='') as fco:
tpl = SimpleTemplate(source=fco)
f.write( tpl.render(**templateVal) )
f.close()
self.logger.info("---設定ファイル作成完了---")
if __name__ == '__main__':
testClass2 = Main()
testClass2.exe()
files:
- filename: "./template/appA/config1.txt"
kankyo:
- kankyoname: "kankyo1"
apps:
- appname: "appA1"
var:
x1: "aiueo1"
x2: "ueo1"
- appname: "appA2"
var:
x1: "aiueo2"
x2: "ueo2"
- kankyoname: "kankyo2"
apps:
- appname: "appA1"
var:
x1: "aiueo3"
x2: "ueo3"
- appname: "appA2"
var:
x1: "aiueo4"
x2: "ueo4"
- filename: "./template/appA/config2.txt"
#オプションでencodingを指定可能
encoding: "shift_jis"
kankyo:
- kankyoname: "kankyo1"
apps:
- appname: "appA1"
var:
x1: "aiueo5"
x2: "ueo5"
- appname: "appA2"
var:
x1: "aiueo6"
x2: "ueo6"
- kankyoname: "kankyo2"
apps:
- appname: "appA1"
var:
x1:
val: "<note><to>a</to><from>b</from><heading>no title</heading><body>test</body></note>"
xml: "on"
x2: "ueo7"
- appname: "appA2"
var:
x1: "aiueo8"
x2: "ueo8"
aaaa.path={{x1}}
bbbbb.count={{x2}}