職場では、Jinjaを使用してテンプレートをYAMLとしてレンダリングしています。 すべての{{ variable }}
展開をカスタマイズして、 None
(YAMLではnull
)や空の文字列( ''
としてエンコードされる)などのPython値に適した処理を実行したいと思います。 YAMLではjinja2.runtime
Markup
とescape
をモンキーパッチする必要がありました。 たとえば、環境内の何かをオーバーライドすることによって、これを行うための公式に認可された方法があれば、それは素晴らしいことです。
自動エスケープがこれを行う適切な方法であるかどうかはわかりません。
むしろフィルターを使いたいです。 yamlはjsonのスーパーセットであるため、 tojson
は適切な候補のようです。
それ以外に:YAMLを構築するために文字列ベースのテンプレートを使用する理由は何ですか? Python辞書をYAMLとしてシリアル化する方がはるかにクリーンなようです。 これでは不十分な場合は、コメントを含める場合のみです。 ただし、その場合は、非破壊的な読み取り/書き込みを処理するYAMLエンジンを使用できるため、コメントと空のアイテムを含むYAMLテンプレートを読み込んでから、データを更新してYAMLとして再シリアル化します。
自動エスケープを使用するポイントは、すべての変数展開にフィルターを設定する必要がないことです。
私たちのユースケースは、マイクロサービスの構成ファイルを作成することです。 各サービスには「テンプレート」が含まれており、そのテンプレートに固有の環境ごとの構成でそれを埋めたいと考えています。 YAMLを直接使用する方がクリーンであることに同意しますが、場所がまだわからない変数を置き換える方法を知っている一般的な「エンジン」を見つけることができませんでした。 (YAMLエイリアスと参照がありますが、これにはあまり適していません。循環構造をシリアル化するためのものです。)YAMLを使用してこれを機能させる方法はいくつか考えられますが、ちょっとハッキーも。 この設定により、実行できる置換の種類をより細かく制御することもできますが、実際にはそれを何にも使用しません。
jinjaプラグインを使用して、すべての変数にフィルターを適用できます。 i18nブロックを使用する必要がない限り、それほど難しくはありません。 たぶん、私が少し前に書いたものからいくつかのアイデアを取り入れることができます: https :
これはとても役に立ちます、ありがとう! 変数に|safe
フラグを立てることができるので、自動エスケープはこれを解決するためのOKなアプローチであるように感じます。 しかし、概念的には、これは自動エスケープとは異なります。変数が有効なYAMLであることを確認するだけでなく、特定の方法でフォーマットするためです。 拡張機能を作成します。
@glassercこの拡張機能で成功しましたか? 私も同様の問題を抱えていますが、解決策を指摘したり、アイデアを共有したりできますか?
私のソリューションの最初の部分は、フィルターを介してすべての変数呼び出しを実行する拡張機能でした。 これは、それを機能させるために必要な最低限のことです。
import jinja2.ext
from jinja2.lexer import Token
class YAMLEverythingExtension(jinja2.ext.Extension):
"""
Insert a `|yaml` filter at the end of every variable substitution.
This will ensure that all injected values are converted to YAML.
"""
def filter_stream(self, stream):
# This is based on https://github.com/indico/indico/blob/master/indico/web/flask/templating.py.
for token in stream:
if token.type == 'variable_end':
yield Token(token.lineno, 'pipe', '|')
yield Token(token.lineno, 'name', 'yaml')
yield token
これはより賢明かもしれません。たとえば、変数ブロックにsafe
またはyaml
フィルターが明示的に表示されている場合は、フィルターの挿入をスキップしようとする可能性があります。 しかし、私はそのための時間を必要としたり、持ったりすることは決してありませんでした。
2番目の部分はフィルター自体です。 yaml.dump
を使用するだけでは十分に洗練されていなかったため、yamlの内部を少し調べなければなりませんでした。
import cStringIO
import yaml
def yaml_filter(val):
"""Serialize some value in isolation, not as part of any document.
We can't just use yaml.dump because that outputs an entire document, including newlines, which isn't helpful for
inserting into a YAML document."""
if isinstance(val, jinja2.Undefined):
val._fail_with_undefined_error()
stream = cStringIO.StringIO()
dumper = yaml.dumper.Dumper(stream)
dumper.open()
node = dumper.represent_data(val)
dumper.serialize(node)
# The serialized node tends to have a \n at the end. The template might not
# want a \n inserted here, e.g. if two variables are on the same line, so
# strip.
return stream.getvalue().strip()
これにより、指定された名前のフィルターが環境で使用可能であることを確認するなど、すべてが結び付けられます。
from jinja2 import loaders
from jinja2 import Environment, StrictUndefined
def get_environment():
"""Create a standard Jinja environment that has everything in it.
"""
jinja_env = Environment(extensions=(YAMLEverythingExtension,),
# some other options that we use at work
loader=loaders.FileSystemLoader(['.', '/']),
undefined=StrictUndefined)
jinja_env.filters["yaml"] = yaml_filter
return jinja_env
これは、HTML以外のテンプレートをエスケープするのに最適です。 Jinja2を使用してJSONを生成しており、HTMLエスケープの代わりにJSONエスケープを使用したいと考えています。
Jinjaテンプレートやその他の文字列ベースのテンプレート言語や文字列操作を使用してJSONを生成するのは間違っています。 YAMLも人間に優しいことを目的としているため、これを行う理由はわかりますが、JSONは、Jinjaテンプレートからではなく、Pythondictなどから生成する必要があります。
(とはいえ、なぜあなたがそれをしたいのか、そしてあなたが何をしようとしているのか興味があります:p)
私はそれが理にかなっていると思います。 :-)#571を閉じます。
JSONをJinjaで生成するのは良くないかもしれませんが、私はjinjaを使用してLaTeXを生成しているので、この種の機能に非常に興味があります。 block_start_string
とblock_end_string
などをEnvironment
変更することで、ほとんどの方法でそこにたどり着きました。 今のところ、 autoescape = False
を設定しましたが、理想的には、自分自身をエスケープするために何かを渡したいと思います(おそらく正規表現のように)。
これは、平文とマークダウンの生成にも当てはまります。 Jinjaは、これまでHTML以外のドキュメントをテンプレート化するための優れたツールです。
複数の条件付きクエリ部分が必要な場合、 str.format()
使用すると面倒になる可能性があるため、私はjinjaを使用してSQLクエリを生成することを検討していました。
それを行うためのライブラリがすでにあります。つまり、カスタムエスケープ関数とjinjasqlを許可するjinja-vanishです。
最初のものは、コンパイル時に定数評価を無効にする必要がありますが、これは少し悪いようです。 別のもの
フィルタアプローチを使用して、すべての変数式に特別なフィルタを追加します。これは問題ありませんが、正しくないと感じます。
私はそれを深く調べていませんが、それを実装するのはそれほど難しいことではないようです。 これは考慮できますか、それともライブラリの範囲外ですか?