$importでpythonのmoduleをimport出来るようにしてみた
今回はimport出来るようにしてみた。例えば以下の様にして使うことが出来る。そう言えば、前回の記事では使い方を説明していなかった。使い方は単純で zenmai.compile(d, module)
という感じで呼び出すだけ。注意点として順序は保持してもらう必要がある(dictknifeというのはyamlのloadに便利なutilityというだけなのでなくてもどうにかなる)。
from zenmai.actions import import_, from_ # これ if __name__ == "__main__": import zenmai import sys from dictknife import loading loading.setup() # xxx d = loading.loadfile(None) d = zenmai.compile(d, sys.modules[__name__]) # ここに渡したmoduleが触れる loading.dumpfile(d)
このようなコードを書いてあげると以下のようなyamlに対して
code: $import: math nums: ceil: $math.ceil: 1.5 floor: $math.floor: 1.5
以下のような出力を返すようになる。
nums: ceil: 2 floor: 1
微妙な点
ただ微妙な点が残っている。それは戻り値が出力として不要な階層の部分に不格好な記述が必要になる点。例えば、上の例で言うと、importを行う部分のところに code
と書いているが。これは実は何でも良い。呼ばれるactionで戻り値が何も返さなかった場合にはその階層自体がまるっと消えて無くなる。内部的には missing = object()
みたいなものを利用してあれこれしている。すごく汚い。
fromにも対応
実は import
の他に from
も対応している。例えばこういう感じも書ける。
code: $from: math import: - ceil - floor nums: ceil: $ceil: 1.5 floor: $floor: 1.5
予約語
ちなみに、from
や import
は予約語なので、pythonのコード上でこのような予約語のメソッドなどを定義できないかと思いきや実は普通にできる。ただしsyntax errorになるのでそれを迂回する必要はある。こういう感じ。
A = type("A", (), {"import": lambda self: "import!!"}) print(getattr(A(), "import")()) # import!! # print(A().import) syntax error
なんとなくの方針として、今回は、こういう汚い感じのコードは書かなくてすむようにしよう、ただし予約語のものもそのまま書けるようにしようという感じにすることにしたので。実装では keyword.iskeyword
を使って分岐している。from
が自動的にfrom_
を探して呼び出すということにした。予約語を見つけてくれるkeyword packageはたまに便利だったりする。
yaml上で動くtoy言語を作り始めた
yaml上で動くtoy言語を作り始めた。 これ。
今回はコードを書く以外になるべく文章によるアウトプットを増やしてみようという試みも裏で行うことにした(そんなわけでこの記事を書いている)。
yaml上で動く言語
現在のところはlispのマクロのようなものをイメージしている。コードを読み込み読み込んだコード自体が変換され出力されるというようなもの。上手くいくかはわからない。
どこまで真面目にやるかというのもけっこう悩みどころではあるけれどとりあえず行き当たりばったりで進めてみることにする。
すごく簡単に書くと以下のような記述になる。$で始まるものが関数(action)として実行される(個人的には $
のprefixは好きではないのだけれど。yamlの構文との兼ね合いで今のところこれが一番無難だろうという考えになっている)。
$<action>: <body> <kwargs>
上のようなyamlが <action>(<body>, **<kwargs>)
という形の関数呼び出しに変換されるようなイメージ。
例えば、以下のようなyamlは、suffix({"name": "foo"}, suffix="+")
という形に変換される。
$suffix: name: foo suffix: +
actionが存在した階層の同一階層のものはキーワード引数として扱われる。また、actionの戻り値がそのまま出力になる。
ネストした呼び出し
ネストしたactionの呼び出しは以下の様になる。
definitions: $suffix: $suffix: name: foo suffix: + suffix: "-"
関数の呼び出しは内から外に行われる。上の例では suffix(suffix({"name": "foo"}, suffix="+"), suffix="-")
の意味。従って以下のような結果になる。
name+-: foo
ネストした呼び出しがまだ読みづらい気がする。そしてactionとその引数の間が離れていくのがわりとつらい。
readmeを書く
今回はそれなりに早い段階でreadmeを書くことにした。毎回readmeはテキトウになってしまうのでreadmeを充実させるというのも今回の目標の1つにしてみることにした。とりあえず、実行例を載せようということで、トップレベルで make readme
を呼ぶとreadmeが生成されるというようにした。markdownに比べてReSTはコードブロックにインデントが必要になるのが面倒に感じる。