以前、GAEアプリをGitHubに上げるためのツールとして、以下のものを作ってみた。
GitHubに上げるために、app.yamlのapplicationを変更するツールの作成 - メモ的な思考的な
しかし、GitHubで公開できない情報はapp.yamlのapplicationだけではなく、各種APIのキーもあることに気づいたため、APIのキーも削除するようなツールを作ってみた。
■実装するときに悩んだことと対応
yamlファイルの順番を崩さずに読込→出力する方法はないか?
何もせずにPyYAMLでloadとdumpをすると、loadした段階で辞書型で取り込まれるため、元々のyamlファイルの順番が崩れてしまう(辞書型は順序を保持しない)。
崩さないようにするには、Python2.7であればOrderedDictが使えるため、それとPyYAMLの機能を組み合わせればよい。
方法としては、以下の方法をそのまま利用した。ありがとうございます。
OrderedDictをYAMLに変換する - スコトプリゴニエフスク通信
使用例は、以下。
import sys import yaml from collections import OrderedDict # OrderedDictを出力するための関数 def represent_odict(dumper, instance): return dumper.represent_mapping(u'tag:yaml.org,2002:map', instance.items()) # OrderedDictを読み込むための関数 def construct_odict(loader, node): return OrderedDict(loader.construct_pairs(node)) def main(path, convertedPath): # 記述順で読み込めるようにadd_constructorで設定 yaml.add_constructor(u'tag:yaml.org,2002:map', construct_odict) data = yaml.load(open(path).read().decode('utf-8')) data['application'] = u'<your application id>' # OrderedDictを出力できるように、add_representerにて設定 yaml.add_representer(OrderedDict, represent_odict) yaml.dump( data, file(convertedPath, 'w'), default_flow_style=False, encoding='utf-8', allow_unicode=True )
C#のFuncデリゲートのようなものはないか
app.yamlとapi.yamlの編集方法だけ別で、後は同じ処理であったため、Funcみたいなものはないかを探したが、そもそもPythonでは関数も引数として渡せるとのこと。楽ですね。
初めてのPython(4) 関数でさえオブジェクトであるPython
特定条件でファイルを検索する方法
そのものがありました。
http://www.nishiohirokazu.org/blog/2006/03/python.html
os.walk()については、以下を参照。
145:ディレクトリ内のファイルを再帰的に処理
■ソースコード
gistにもアップ(GAEtoGitHubConverter.py)。
# -*- coding: utf-8 -*- import sys import os import shutil import yaml from collections import OrderedDict # OrderedDictを出力するための関数 def represent_odict(dumper, instance): return dumper.represent_mapping(u'tag:yaml.org,2002:map', instance.items()) # OrderedDictを読み込むための関数 def construct_odict(loader, node): return OrderedDict(loader.construct_pairs(node)) class Converter(object): def __init__(self, dirFrom, dirTo): self._dirFrom = dirFrom self._dirTo = dirTo def convert(self): for (root, dirs, files) in os.walk(self._dirFrom): # コピー先のディレクトリがなければ、作成する self._create_dir(root) for f in files: # .pyc終わりなら、コピーしない if f[-4:] == '.pyc': continue filepathFrom = os.path.join(root, f) if f == 'app.yaml': # app.yamlなら、読み込んで、applicationをリプレイスして、コピー # 編集する関数(self._convert_app_yaml)は、引数に渡して、_convert_yaml内で実行する self._convert_yaml(filepathFrom, self._convert_app_yaml) elif f == 'api.yaml': # api.yamlなら、読み込んで、すべてを''へとリプレイスして、コピー self._convert_yaml(filepathFrom, self._convert_api_yaml) else: # 他はそのままコピー filepathTo = self._create_filepath_to(filepathFrom) shutil.copyfile(filepathFrom, filepathTo) def _create_dir(self, root): rootTo = root.replace(self._dirFrom, self._dirTo) if os.path.isdir(rootTo) == False: os.makedirs(rootTo) def _create_filepath_to(self, filepathFrom): return filepathFrom.replace(self._dirFrom, self._dirTo) def _convert_yaml(self, filepathFrom, func): # 記述順で読み込めるようにadd_constructorで設定 yaml.add_constructor(u'tag:yaml.org,2002:map', construct_odict) load = yaml.load(open(filepathFrom).read().decode('utf-8')) dump = func(load) self._export_yaml(filepathFrom, dump) def _convert_app_yaml(self, data): data['application'] = '<your application id>' return data def _convert_api_yaml(self, data): for key in data: data[key] = '' return data def _export_yaml(self, filepathFrom, data): filepathTo = self._create_filepath_to(filepathFrom) # OrderedDictを出力できるように、add_representerにて設定 yaml.add_representer(OrderedDict, represent_odict) yaml.dump( data, file(filepathTo, 'w'), default_flow_style=False, encoding='utf-8', allow_unicode=True ) if __name__ == '__main__': if len(sys.argv) < 3: print 'Usage: %s [filename]' % sys.argv[0] sys.exit(1) dirFrom = sys.argv[1] dirTo = sys.argv[2] cnv = Converter(dirFrom, dirTo) cnv.convert()