Jinja: autoescape yang dapat ditimpa

Dibuat pada 15 Okt 2015  ·  11Komentar  ·  Sumber: pallets/jinja

Di tempat kerja, kami menggunakan Jinja untuk merender template sebagai YAML. Kami ingin menyesuaikan semua {{ variable }} ekspansi untuk melakukan sesuatu yang masuk akal untuk nilai Python seperti None (yaitu null dalam YAML) dan string kosong (yang dikodekan sebagai '' dalam YAML). Sepertinya mekanisme autoescaping Jinja akan sempurna untuk ini. Sayangnya, saya tidak melihat cara apa pun untuk mengonfigurasi fungsionalitas autoescaping agar tidak autoescape ke HTML. Sebagai gantinya, saya harus monkeypatch Markup dan escape di jinja2.runtime . Alangkah baiknya jika ada metode yang disetujui secara resmi untuk melakukan ini, misalnya dengan mengesampingkan sesuatu di lingkungan.

Semua 11 komentar

Tidak yakin apakah autoescaping adalah cara yang tepat untuk melakukan ini.

Saya lebih suka menggunakan filter. tojson tampaknya merupakan kandidat yang baik karena yaml adalah superset dari json,.


Selain itu: Adakah alasan mengapa Anda membuat template berbasis string untuk membangun YAML? Membuat serialisasi kamus Python sebagai YAML tampaknya jauh lebih bersih. Satu-satunya kasus yang saya lihat di mana ini mungkin tidak cukup adalah ketika Anda ingin memasukkan komentar. Tetapi dalam hal ini Anda dapat menggunakan mesin YAML yang menangani pembacaan/penulisan non-destruktif sehingga Anda cukup memuat template YAML dengan komentar dan item kosong Anda, lalu memperbarui data dan membuat serial ulang sebagai YAML.

Maksud menggunakan autoescaping adalah saya tidak ingin harus memasang filter pada setiap ekspansi variabel tunggal.

Kasus penggunaan kami adalah menulis file konfigurasi untuk layanan mikro kami. Kami memiliki "templat" yang disertakan dengan setiap layanan dan kami ingin mengisinya dengan konfigurasi per-lingkungan yang khusus untuk templat itu. Saya setuju bahwa menggunakan YAML secara langsung lebih bersih, tetapi kami tidak dapat menemukan "mesin" generik yang tahu cara mengganti variabel yang belum diketahui lokasinya. (Ada alias dan referensi YAML, tetapi mereka tidak benar-benar cocok untuk ini -- mereka lebih untuk membuat serialisasi struktur siklik.) Ada beberapa cara yang dapat saya pikirkan untuk membuatnya bekerja menggunakan YAML, tetapi mereka jenis hacky juga. Pengaturan ini juga memberikan kontrol lebih pada jenis pergantian pemain yang dapat kami lakukan, meskipun dalam praktiknya kami tidak benar-benar menggunakannya untuk apa pun.

Anda bisa menggunakan plugin jinja untuk menerapkan filter ke semua variabel. Tidak terlalu sulit asalkan tidak menggunakan blok i18n. Mungkin Anda bisa mengambil beberapa ide dari sesuatu yang saya tulis beberapa waktu lalu: https://github.com/indico/indico/blob/master/indico/web/flask/templating.py#L187

Ini sangat membantu, terima kasih! Saya merasa autoescaping masih merupakan pendekatan yang OK untuk menyelesaikan ini karena Anda dapat misalnya menandai variabel sebagai |safe . Tapi saya akui bahwa secara konseptual, ini tidak benar-benar seperti autoescaping, karena kami tidak hanya memastikan bahwa variabel adalah YAML yang valid, tetapi memformatnya dengan cara tertentu. Saya akan menulis ekstensi.

@glasserc apakah Anda berhasil dengan ekstensi ini? Saya memiliki masalah yang sama, dapatkah Anda menunjukkan solusi/berbagi beberapa ide?

Bagian pertama dari solusi saya adalah ekstensi yang menjalankan semua panggilan variabel melalui filter. Ini adalah minimal yang saya butuhkan untuk membuatnya bekerja.

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

Ini bisa lebih pintar, misalnya ia bisa mencoba melewati penyisipan filter jika melihat filter safe atau yaml secara eksplisit di blok variabel. Tetapi saya tidak pernah membutuhkan/memiliki waktu untuk itu.

Bagian kedua adalah filter itu sendiri. Hanya menggunakan yaml.dump tidak cukup canggih, jadi saya harus melihat-lihat internal yaml sedikit.

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()

Ini mengikat semuanya, termasuk memastikan bahwa filter dari nama yang diberikan tersedia di lingkungan:

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

Ini masih bagus untuk keluar dari templat non-HTML. Saya menggunakan Jinja2 untuk menghasilkan JSON, dan ingin menggunakan pelolosan JSON alih-alih pelolosan HTML.

Menggunakan templat Jinja atau bahasa templat berbasis string lainnya atau operasi string sama sekali untuk menghasilkan JSON adalah salah. Saya dapat melihat mengapa Anda melakukan ini untuk YAML karena ini juga dimaksudkan untuk ramah-manusia, tetapi JSON harus dihasilkan dari misalnya dict Python dan bukan dari template Jinja.

(yang mengatakan, saya ingin tahu mengapa Anda ingin melakukan itu dan apa yang Anda coba lakukan: p)

Saya rasa itu masuk akal. :-) Saya akan menutup #571.

Mungkin JSON tidak bagus untuk dibuat oleh Jinja, tetapi saya sangat tertarik dengan fitur semacam ini karena saya membuat LaTeX menggunakan jinja. Saya mendapatkan sebagian besar perjalanan ke sana dengan memodifikasi block_start_string dan block_end_string dan lainnya di Environment . Untuk saat ini, saya telah menetapkan autoescape = False , tetapi idealnya saya ingin memberikan sesuatu untuk melakukan pelolosan sendiri (mungkin seperti regex).

Ini juga berlaku untuk menghasilkan plaintext dan penurunan harga. Jinja telah menjadi alat yang hebat sejauh ini untuk templating dokumen non-html.

Saya sedang mencari cara menggunakan jinja untuk menghasilkan kueri SQL karena menggunakan str.format() bisa menjadi berantakan jika Anda memerlukan beberapa bagian kueri bersyarat.

Sudah ada perpustakaan untuk melakukan itu, yaitu jinja-vanish yang memungkinkan fungsi escape kustom dan jinjasql .
Yang pertama harus menonaktifkan evaluasi konstan pada kompilasi yang tampaknya agak buruk. Yang lain
menggunakan pendekatan filter untuk menambahkan filter khusus ke semua ekspresi variabel yang ok tetapi juga terasa tidak benar.

Saya belum melihat secara mendalam tetapi tampaknya tidak terlalu sulit untuk menerapkannya. Bisakah ini dipertimbangkan atau di luar jangkauan perpustakaan?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

humiaozuzu picture humiaozuzu  ·  3Komentar

guettli picture guettli  ·  5Komentar

priestc picture priestc  ·  5Komentar

mitsuhiko picture mitsuhiko  ·  3Komentar

AMDmi3 picture AMDmi3  ·  4Komentar