jsonを直接引数としてコマンドラインから渡す方法を考えてた
はじめに
awscliはbotocoreにコマンドラインインターフェイスを持たせるためのラッパーでしかないと考えると色々捗る気がした。 つまり基本的にはJSONを入力として取るAPIが存在してそれにより何でも操作する。 ただし、直接APIを呼ぶコードを書くのは面倒くさいので、シェル上から簡易に使えるコマンドが存在するというイメージ。
しかしJSONを引数に取れるようにと考えてみると以下の2つがあってつらい
jsonの作成
なんとなくワンライナー的な形で作成できるような形にしたかったのでシェル・コマンド的な文法を持つミニ言語を考えてみた。 以下のようなコードが
mkobject person cd person put name foo; put age 20
以下のようなjsonを返す。
{ "person": { "name": "foo", "age": "20" } }
ここで気になるのは、フィールドの順序。もちろんpythonのdictを使った場合にはフィールドの順序は保証されない。 ただ、json出力に限っては記述した順序が保証されても良いのではないかなと思いOrderedDictを使っている。
おおよそシェルのファイル構造に似たような物を利用してjsonを作っていく。相対パス・絶対パスにも対応してみたので以下の様なコードも動く
mkobject address phoneNumber cd address put ./streetAddress "21 2nd Street" put city "New York" put ../phoneNumber/location home put /phoneNumber/code 44
これで出力されるJSONは以下。
{ "address": { "streetAddress": "21 2nd Street", "city": "New York" }, "phoneNumber": { "code": "44", "location": "home" } }
arrayへの対応
困ったのはArrayでappendというコマンドを別途作ったのだけれど。putでもkeyがdigitとみなせるものだったらdigitとして扱うことにした。 ただし新しい要素の追加に使えないのでSelfishListというクラスを別途作った。
class SelfishList(list): def __setitem__(self, i, v): try: super(SelfishList, self).__setitem__(i, v) except IndexError: if len(self) == i: self.append(v) else: raise L = SelfishList() L[0] = 10 # [10] L[100000] # raise IndexError
このおかげで以下の様に書けるようになった。
mkarray favorites append favorites a append favorites b
もしくは
mkarray favorites cd favorites put 0 a put 1 b
結果は
{ "favorites": [ "a", "b" ] }
cpの追加
cpもあると面白いかもしれないと思いcpも作った。以下の様に動く。
mkobject foo cd foo put name "foo" put age 20 cd ../ mkarray members cp foo members/0 cp foo members/1 put members/1/name "bar" rm foo
これは一時的にfooというobjectを作りそれを改変してmembersの要素を作っている。fooは一時的なファイルのようなものだったので不要になったら取り除いている。
{ "members": [ { "name": "foo", "age": "20" }, { "name": "bar", "age": "20" } ] }
これは1行で以下の様に書いても良い。
mkobject foo;cd foo;put name "foo";put age 20;cd ../;mkarray members;cp foo members/0;cp foo members/1;put members/1/name "bar";rm foo