$importでpythonのmoduleをimport出来るようにしてみた

github.com

今回は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

予約語

ちなみに、fromimport予約語なので、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はたまに便利だったりする。