Jinja: lstrip_blocks не сохраняет пространство перед конечным блоком в той же строке

Созданный на 29 янв. 2020  ·  13Комментарии  ·  Источник: pallets/jinja

Посмотрите на следующий код шаблона:

set_property verilog_define {
  {%- for k, v in vlogdefine.items() %}{{ k }}={{ v|param_value_str }} {% endfor -%}
  } [get_filesets sources_1]

https://github.com/olofk/edalize/blob/bdb6c9ccc666e9f60333279ad53ed09cda88b3dc/edalize/templates/vivado/vivado-project.tcl.j2#L27 -L29

До Jinja2 2.10.3 это привело бы к чему-то вроде

set_property verilog_define {vlogdefine_bool=1 vlogdefine_int=42 vlogdefine_str=hello } [get_filesets sources_1]

https://github.com/olofk/edalize/blob/bdb6c9ccc666e9f60333279ad53ed09cda88b3dc/tests/test_vivado/test_vivado_0.tcl#L10

В коде есть lstrip_blocks = True .

Теперь с 2.11.0 код выглядит так:

set_property verilog_define {vlogdefine_bool=1vlogdefine_int=42vlogdefine_str=hello} [get_filesets sources_1]

@towoe разделил изменение поведения пополам до https://github.com/pallets/jinja/commit/7d00a40465c89bee141ab5a3db545a20e7d30509 (от @ petee-d в https://github.com/pallets/jinja/issues/857)

Согласно документации

lstrip_blocks
Если для этого параметра установлено значение True, начальные пробелы и табуляция удаляются от начала строки до блока. По умолчанию False.

Я не думаю, что пробелы следует удалять перед endfor , поскольку это не пробел «от начала строки». Я неправильно понимаю это, или новая версия привносит неожиданные изменения в поведение?

Python 3.7.3 (но также бывает и на 3.5 в CI)

Самый полезный комментарий

Только что выпустил 2.11.2 с этим.

Все 13 Комментарий

Я разберусь с этим, извините за то, что вызвал тот запутанный сеанс отладки, который вы связали! На первый взгляд, похоже, что теперь поведение более последовательное, и у нас не было теста на предыдущее поведение, но мне придется его проверить. Можно ли было бы использовать {%+ endfor -%} для явного объявления сохранения пробелов?

@davidism, спасибо, что посмотрели.

Да, мы можем внести изменения в edalize (библиотеку, в которой используется Jinja) различными способами (добавить + , переключиться на lstrip_blocks = False и т. Д.), Но сначала я хотел бы убедиться, что что у нас есть общее понимание ожидаемого поведения.

Это тоже влияет на меня так же, как и на оригинальный плакат. Документация кажется мне довольно ясной, что поведение 2.11 неверно. Из https://github.com/pallets/jinja/blob/2.11.x/docs/templates.rst#whitespace -control:

(Ничего не будет удалено, если перед началом блока есть другие символы.)

Это связано с # 858, в котором исправлена ​​довольно большая проблема скорости с пространством для синтаксического анализа. Я могу исправить эту проблему, но я не хочу просто возвращать это изменение. Если кто-то хочет поэкспериментировать с регулярным выражением в лексере и исправить это, я с радостью ознакомлюсь с этим.

Извините за исправление, нарушающее предполагаемое поведение lstrip_blocks, я помню, что был немного сбит с толку относительно того, что именно он должен делать, и не смог найти для него надлежащую документацию. Я не уверен, почему Google не привел меня в ссылается @kenyon , очень странно. В итоге я выяснил предполагаемое поведение на основе тестов и реализации и, очевидно, пропустил это.

Как сказал @davidism , возврат исправления вызовет другие проблемы, поэтому необходимо разработать новое исправление. Я постараюсь найти для этого время на этой неделе.

from jinja2 import Template

t = Template(
    "{% if x %}{{ x }} {% endif %}y",
    lstrip_blocks=True,
)
out = t.render(x="x")
assert out == "x y"

https://github.com/pallets/jinja/blob/547e6e39ec3994c9dd0c806ee7bb29353843060e/src/jinja2/lexer.py#L721 -L735

Вот в чем проблема. Когда этот шаблон токенизируется, find("\n") передает текст, содержащий только пробел между {{ x }} и {% endif %} , не видит новой строки, а затем удаляет текст с начала.

Добавление "\n" in text к elif приводит к сбою нескольких тестов, потому что первая строка шаблона не содержит новой строки, но должна быть удалена, если в ней есть только место. Если мы станем немного умнее и отследим, находимся ли мы на первой строке, это все равно не удастся, если trim_blocks также включен, поскольку это удаляет новую строку как часть регулярного выражения.

Также кажется, что это влияет на поведение {%- raw %} {% endraw -%} . До 2.11.0 это заставит пробел в этом месте. Теперь это пространство удалено.

from jinja2 import Template

t = Template(
    "{{x}}\n{%- raw %} {% endraw -%}\n{{ y }}",
    lstrip_blocks=True,
)
out = t.render(x="x", y="y")
assert out == "x y"

@ petee-d есть шанс, что у тебя есть время взглянуть на это еще раз? В противном случае я верну его для версии 2.11.2.

Ой, совсем забыл об этом. Постараюсь на самом деле найти время на этой неделе, в противном случае отмените его, если побочные эффекты будут такими серьезными, как кажется.

Мой предварительный план - выпустить в субботу. Дайте мне знать, если вы работаете над этим, я могу отодвинуть это назад, если так.

Думаю, у меня есть исправление. Я старался не поддаваться влиянию ваших предыдущих комментариев

Прежде всего, я думаю, что изначально я планировал, что мое исправление будет вести себя по-другому, если l_pos = text.rfind("\n") + 1 не нашел ни одного символа новой строки, но затем забыл об этом. Поэтому я больше не делаю зачистку, если в text нет новой строки. Затем я также попытался решить проблему с первой строкой source которую все равно нужно было удалить, но вместо использования or pos == 0 я использовал логический флаг line_starting инициализированный в True. Затем я просто устанавливаю этот флаг после обработки каждого токена в зависимости от того, заканчивается ли соответствующая строка новой строкой, а также решает проблему trim_blocks. Смотрите исправление в # 1183.

Текущий набор тестов проходит, завтра я также добавлю новые тесты для случаев в этой проблеме и других, о которых я могу подумать. Я также запущу исходную версию, первое исправление и второе исправление относительно моего недавно собранного набора 5 ГБ пользовательских шаблонов, которые я собрал из своего проекта, сравню деревья синтаксического анализа и производительность.

Добавлены тесты, тесты производительности не показали деградации, так что это сделано с моей стороны. :)

Только что выпустил 2.11.2 с этим.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги