Observe o seguinte código de modelo:
set_property verilog_define {
{%- for k, v in vlogdefine.items() %}{{ k }}={{ v|param_value_str }} {% endfor -%}
} [get_filesets sources_1]
Até Jinja2 2.10.3, isso resultaria em algo como
set_property verilog_define {vlogdefine_bool=1 vlogdefine_int=42 vlogdefine_str=hello } [get_filesets sources_1]
O código tem lstrip_blocks = True
.
Agora, com 2.11.0, o código é renderizado assim:
set_property verilog_define {vlogdefine_bool=1vlogdefine_int=42vlogdefine_str=hello} [get_filesets sources_1]
@towoe dividiu a mudança de comportamento em https://github.com/pallets/jinja/commit/7d00a40465c89bee141ab5a3db545a20e7d30509 (por @ petee-d em https://github.com/pallets/jinja/issues/857)
De acordo com a documentação
lstrip_blocks
Se for definido como True, os espaços e tabulações à esquerda são removidos do início de uma linha para um bloco. O padrão é False.
Não acho que o espaço em branco deva ser removido antes de endfor
, já que esse não é o espaço em branco "do início da linha". Estou entendendo mal isso ou a nova versão introduz uma mudança inesperada de comportamento?
Python 3.7.3 (mas também acontece no 3.5 em CI)
Vou dar uma olhada nisso, desculpe por causar aquela sessão de depuração confusa que você vinculou! À primeira vista, parece que o comportamento está mais consistente agora e não tínhamos um teste para o comportamento anterior, mas terei que verificar. Seria possível usar {%+ endfor -%}
para declarar explicitamente a preservação dos espaços em branco?
@davidism, obrigado por dar uma olhada.
Sim, podemos fazer alterações em edalize (a biblioteca onde Jinja é usado) de várias maneiras (adicione o +
, mude para lstrip_blocks = False
, etc.), mas eu gostaria primeiro de garantir que temos uma compreensão compartilhada do comportamento esperado.
Isso também me afeta da mesma forma que o pôster original. A documentação parece muito clara para mim que o comportamento do 2.11 está errado. De https://github.com/pallets/jinja/blob/2.11.x/docs/templates.rst#whitespace -control:
(Nada será removido se houver outros caracteres antes do início do bloco.)
Isso se deve ao # 858, que corrigiu um grande problema de velocidade com a análise do espaço. Posso corrigir o problema que isso causou, mas não quero apenas reverter essa alteração. Se alguém quiser brincar com a regex no lexer e corrigir isso, será um prazer revisá-la.
Desculpe pela correção que quebrou o comportamento pretendido de lstrip_blocks, lembro-me de ficar um pouco confuso sobre o que exatamente ele deveria fazer e de não ser capaz de encontrar a documentação adequada para ele. Não sei por que o Google não me levou para a parte de documentos vinculada ao @kenyon , muito estranho. Então, acabei descobrindo o comportamento pretendido a partir dos testes e da implementação e aparentemente não percebi isso.
Como @davidism disse, reverter a correção causaria outros problemas, então uma nova correção precisa ser desenvolvida. Vou tentar arranjar tempo para isso esta semana.
from jinja2 import Template
t = Template(
"{% if x %}{{ x }} {% endif %}y",
lstrip_blocks=True,
)
out = t.render(x="x")
assert out == "x y"
Aqui está o problema. Quando este modelo está sendo tokenizado, find("\n")
recebe um texto contendo apenas o espaço entre {{ x }}
e {% endif %}
, não vê nova linha e remove o início do texto.
Adicionar "\n" in text
a elif
faz com que vários testes falhem porque a primeira linha de um modelo não contém uma nova linha, mas deve ser removida se houver apenas espaço. Se ficarmos um pouco mais inteligentes e rastrearmos se estamos na primeira linha, ainda falhará se trim_blocks
também estiver habilitado, pois isso remove a nova linha como parte da regex.
Parece também que o comportamento de {%- raw %} {% endraw -%}
foi afetado. Antes de 2.11.0, isso forçará um espaço neste lugar. Agora este espaço foi removido.
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 alguma chance de você ter algum tempo para dar uma olhada nisso de novo? Vou reverter para 2.11.2 caso contrário.
Ai, esqueci totalmente disso. Tentarei realmente reservar um tempo esta semana, caso contrário, reverta se os efeitos colaterais forem tão ruins quanto parecem.
Meu plano provisório é lançar no sábado. Deixe-me saber se você está trabalhando nisso, posso adiar se estiver.
Acho que tenho uma solução. Tentei não ser influenciado em minha correção por seus comentários anteriores @davidism e ainda assim cheguei à mesma solução que você tentou, apenas fui um pouco mais longe.
Em primeiro lugar, acho que originalmente pretendia que minha correção se comportasse de maneira diferente se l_pos = text.rfind("\n") + 1
não encontrasse nenhum caractere de nova linha, mas esquecesse-o. Portanto, não faço mais a remoção se não houver nova linha em text
. Então também tentei resolver o problema com a source
primeira linha que precisava ser removida de qualquer maneira, mas em vez de usar or pos == 0
, usei um sinalizador booleano line_starting
inicializado em True. Então, eu apenas configuro este sinalizador após processar cada token com base em se a string correspondente termina com uma nova linha e também resolve o problema de trim_blocks. Veja a correção em # 1183.
O conjunto de testes atual passa, amanhã também adicionarei novos testes para os casos nesta edição e outros que eu possa pensar. Também executarei a versão original, a primeira correção e a segunda correção sobre meu conjunto de 5 GB recém-coletado de modelos criados pelo usuário que coletei em meu projeto, compararei as árvores de análise e o desempenho.
Testes adicionados, os testes de desempenho não mostraram degradação, então são feitos do meu lado. :)
Acabei de lançar 2.11.2 com isso.
Comentários muito úteis
Acabei de lançar 2.11.2 com isso.