`--driver` オプションでカスタマイズ出来るように直した

github.com

--driver オプションでカスタマイズ出来るように直した。例えば前回の記事で @foo{$get: foo} に変更するためのmonkey patchを試していたけれど。これをまともな形で行うようにするときには --driver 経由で設定するのが正しい。

driverについてはこのあたりに書いていた。

@foo に対応する方法

以下の様なdriver.pyを作る。

# driver.py
from zenmai.core import Evaluator as _Evaluator
from zenmai.driver import Driver as _Driver


## 元々はこういうコード
# class Evaluator:
#     def eval(self, context, d):
#         if hasattr(d, "keys"):
#             return self.eval_dict(context, d)
#         elif isinstance(d, (list, tuple)):
#             return self.eval_list(context, d)
#         else:
#             return d


class Evaluator(_Evaluator):
    def eval(self, context, d):
        if hasattr(d, "keys"):
            return self.eval_dict(context, d)
        elif isinstance(d, (list, tuple)):
            return self.eval_list(context, d)
        elif str(d).startswith("@"):
            return self.eval(context, {"$get": d[1:]})
        else:
            return d


class Driver(_Driver):
    evaluator_factory = Evaluator

evalだけ書き換えて @ に対応したクラスを使うように変えている。これを以下の様にして使う。

zenmai --driver="./driver.py:Driver" main.yaml
title: foo
age: 20

ここで main.yamlは以下のようなもの。

# main.yaml

$let:
  ob: {$load: ./ob.yaml}
body:
  title: "@ob#/name"
  age: "@ob#/age"
name:
  foo
age:
  20

builtinsの関数をactionとして使えるように

他にもカスタマイズの例としてpythonの組み込みの操作をactionとして使えるようにしても良いかも知れない。例えば、(pythonの組み込みの関数と言っているのはbool,int,max,lenなどの関数のこと。builtinsモジュールに入っている)。

-m に渡す方法

defaultで利用する関数の中に組み込みのものも使えるようにしてみる。1つは -m に渡す方法。基底で利用するmoduleの指定なのでzenmai.actionsをimportし忘れると$loadなどの組み込みの操作も使えなくなってしまうので注意。

# actions.py
from zenmai.actions import *  # NOQA
bool = bool
# main2.yaml
ok: {$bool: 1}
ng: {$bool: 0}
$ zenmai -m ./actions.py main2.yaml
ok: true
ng: false

--driver に渡す方法

@foo対応と同様にdriverを追加してあげる事もできる。

import sys
from zenmai.driver import Driver as _Driver
from zenmai.core.context import Scope


class Driver(_Driver):
    def context_factory(self, module, *args, **kwargs):
        scope = Scope.mergewith(module, parent=sys.modules["builtins"])
        return super().context_factory(scope, *args, **kwargs)
$ zenmai --driver="./driver2.py:Driver" main2.yaml
ok: true
ng: false