Перегрузка Jinja2 автоэкрана для (La) TeX - PullRequest
9 голосов
/ 04 ноября 2011

Возможно ли перегрузить Jinja2 autoescape так, чтобы он ускользал от чего-то заданным пользователем способом (то есть, от HTML, например LaTeX)?

Вот пример, пытающийся сбежать TeX.

import jinja2

class MyEnv(jinja2.Environment):
    def __init__(self, filters={}, globals={}, tests={},
        loader=None, extensions=[], **kwargs):

        super(MyEnv, self).__init__(
            autoescape          = True,
        )

template = MyEnv().from_string("""\documentclass[{{ class }}]
         \\begin{document}
         {{ content }}
         \end{document}
     """)

print template.render({
    'class':'memoir',
    'content': '{bob} <-- is escaped',
    })

Когда вы запускаете выше, он выводит:

 \documentclass[memoir]
     \begin{document}
     {bob} &lt;-- is escaped
     \end{document}

Проблема здесь в том, что используется экранирование HTML. Так что { и } следует экранировать, но это не так, а < преобразуется в &lt;, но это не должно быть.

Я бы хотел перегрузить функцию escape, которую Jinja2 использует для экранирования переменных.

Моя первая мысль - перегрузить finalize и отключить autoescape. например, * 1 021 *

import jinja2

class MyEnv(jinja2.Environment):
    def __init__(self, filters={}, globals={}, tests={},
        loader=None, extensions=[], **kwargs):

        super(MyEnv, self).__init__(
            autoescape          = False, # turn off autoescape
            finalize            = self.finalize,
        )

    def finalize(self, s):
        import re
        if isinstance(s, jinja2.Markup):
            return s
        s = s.replace('\\', '')
        s = s.replace('~', '\\textasciitilde')
        s = re.sub(r'([#|^|$|&|%|{|}])', r'\\\1', s)
        s = re.sub(r'_', r'\\_', s)

        return jinja2.Markup(s)


template = MyEnv().from_string("""\documentclass[{{ class }}]
         \\begin{document}
         {{ content }}
         \end{document}
     """)

print template.render({
    'class':'memoir',
    'content': '{bob} <-- is escaped',
    })

Вывод неправильный, потому что основной текст не превращен в Markup (то есть строка помечена как безопасная):

documentclass[memoir]
     begin\{document\}
     \{bob\} <-- is escaped
     end\{document\}

Если я установлю autoescape в True и оставлю в финализации, то почти работает (и в этом примере это работает):

\documentclass[memoir]
     \begin{document}
     \{bob\} <-- is escaped
     \end{document}

Включение autoescape работает, потому что оно делает основную часть текста для шаблона Markup (т.е. безопасным).

Однако, вот в чем проблема, если я изменю входные данные на список, который join ed:

template = MyEnv().from_string("""\documentclass[{{ class }}]
         \\begin{document}
         {{ content|join("  > a & b > "|safe) }}
         \end{document}
     """)

print template.render({
    'class':'memoir',
    'content': ['A&B', 'C<D'],
    })

Когда я запускаю это, я получаю:

\documentclass[memoir]
     \begin{document}
     A&amp;B  > a & b > C&lt;D
     \end{document}

Казалось бы, HTML autoescape запускается на элементах 'содержимого', а не finalize. Казалось бы, самое простое решение, при условии, что Jinja2 и его автоэкранирование слабо связаны, - перегрузить функцию автоэкрана. Я не могу понять это, и лучшее, что я придумал, это функция finalize.

Есть ли лучший способ обработать экранирование TeX, чем перегрузка функции finalize? Можно ли перегрузить autoescape?

Например, можно ли установить пользовательский пакет разметки? (выбор, который я предпочел бы избежать)

Спасибо за чтение.

...