Возможно ли перегрузить 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} <-- is escaped
\end{document}
Проблема здесь в том, что используется экранирование HTML. Так что {
и }
следует экранировать, но это не так, а <
преобразуется в <
, но это не должно быть.
Я бы хотел перегрузить функцию 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&B > a & b > C<D
\end{document}
Казалось бы, HTML autoescape
запускается на элементах 'содержимого', а не finalize
. Казалось бы, самое простое решение, при условии, что Jinja2 и его автоэкранирование слабо связаны, - перегрузить функцию автоэкрана. Я не могу понять это, и лучшее, что я придумал, это функция finalize
.
Есть ли лучший способ обработать экранирование TeX
, чем перегрузка функции finalize
? Можно ли перегрузить autoescape
?
Например, можно ли установить пользовательский пакет разметки? (выбор, который я предпочел бы избежать)
Спасибо за чтение.