Jinja: lstrip_blocks no conserva el espacio antes del bloque final en la misma línea

Creado en 29 ene. 2020  ·  13Comentarios  ·  Fuente: pallets/jinja

Mira el siguiente código de plantilla:

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

Hasta Jinja2 2.10.3, esto resultaría en algo como

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

El código tiene lstrip_blocks = True .

Ahora con 2.11.0 el código se representa así:

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

@towoe dividió el cambio de comportamiento en https://github.com/pallets/jinja/commit/7d00a40465c89bee141ab5a3db545a20e7d30509 (por @ petee-d en https://github.com/pallets/jinja/issues/857)

Según la documentación

lstrip_blocks
Si se establece en Verdadero, los espacios iniciales y las tabulaciones se eliminan desde el principio de una línea hasta un bloque. El valor predeterminado es Falso.

No creo que el espacio en blanco deba eliminarse antes de endfor , ya que ese no es un espacio en blanco "desde el principio de la línea". ¿Estoy entendiendo mal eso o la nueva versión introduce un cambio inesperado en el comportamiento?

Python 3.7.3 (pero también ocurre en 3.5 en CI)

bug

Comentario más útil

Recién lanzado 2.11.2 con esto.

Todos 13 comentarios

Lo investigaré, ¡perdón por la causa de esa confusa sesión de depuración que vinculó! A primera vista, parece que el comportamiento es más consistente ahora, y no teníamos una prueba para el comportamiento anterior, pero tendré que comprobarlo. ¿Le sería posible usar {%+ endfor -%} para declarar explícitamente preservar el espacio en blanco?

@davidism gracias por echar un vistazo.

Sí, podemos hacer cambios para edalizar (la biblioteca donde se usa Jinja) de varias maneras (agregue + , cambie a lstrip_blocks = False , etc.), pero primero me gustaría asegurarme que tenemos una comprensión compartida del comportamiento esperado.

Esto también me afecta de la misma forma que el cartel original. La documentación me parece bastante clara de que el comportamiento 2.11 es incorrecto. De https://github.com/pallets/jinja/blob/2.11.x/docs/templates.rst#whitespace -control:

(No se eliminará nada si hay otros caracteres antes del inicio del bloque).

Esto se debe al # 858, que solucionó un problema de velocidad bastante grande con el espacio de análisis. Estoy de acuerdo con solucionar este problema que causó, pero no quiero simplemente revertir ese cambio. Si alguien quiere jugar con la expresión regular en el lexer y solucionar este problema, me complace revisarlo.

Lamento que la solución rompa el comportamiento previsto de lstrip_blocks, recuerdo estar un poco confundido acerca de qué se supone que debe hacer exactamente y no poder encontrar la documentación adecuada para ello. No estoy seguro de por qué Google no me llevó a la parte de documentos a la que @kenyon está vinculada, muy extraño. Así que terminé descubriendo el comportamiento previsto a partir de las pruebas y la implementación y aparentemente me perdí esto.

Como dijo @davidism , revertir la solución causaría otros problemas, por lo que es necesario idear una nueva solución. Trataré de hacer tiempo para eso 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"

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

Aquí está el problema. Cuando esta plantilla está siendo tokenizada, find("\n") recibe texto que contiene solo el espacio entre {{ x }} y {% endif %} , no ve una nueva línea y luego se quita del principio del texto.

Agregar "\n" in text a elif hace que un montón de pruebas fallen porque la primera línea de una plantilla no contiene una nueva línea, pero debe eliminarse si solo tiene espacio. Si nos volvemos un poco más inteligentes y rastreamos si estamos en la primera línea, aún falla si trim_blocks también está habilitado, ya que elimina la nueva línea como parte de la expresión regular.

También parece que el comportamiento de {%- raw %} {% endraw -%} se ve afectado. Antes de la 2.11.0, esto forzará un espacio en este lugar. Ahora este espacio está eliminado.

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 alguna posibilidad de que tengas algo de tiempo para echar un vistazo a esto de nuevo? Voy a revertirlo para 2.11.2 de lo contrario.

Ay, me olvidé por completo de esto. Intentaré aprovechar el tiempo esta semana, de lo contrario, revertirlo si los efectos secundarios son tan graves como parece.

Mi plan tentativo es lanzarlo el sábado. Avísame si estás trabajando en ello, puedo retrasarlo si es así.

Creo que tengo una solución. Intenté no dejarme influir en mi solución por tus comentarios anteriores probaste , solo fui un poco más allá.

En primer lugar, creo que originalmente tenía la intención de que mi solución se comportara de manera diferente si l_pos = text.rfind("\n") + 1 no encontraba ningún carácter de nueva línea pero luego lo olvidé. Así que ya no hago el stripping si no hay una nueva línea en text . Luego también intenté resolver el problema con la primera línea source que necesitaba ser eliminada de todos modos, pero en lugar de usar or pos == 0 , usé una bandera booleana line_starting inicializada en True. Luego, simplemente configuro esta bandera después de procesar cada token en función de si la cadena coincidente termina con una nueva línea y también resuelve el problema de trim_blocks. Vea la corrección en el n. ° 1183.

El conjunto de pruebas actual pasa, mañana también agregaré nuevas pruebas para los casos en este tema y otros que se me ocurran. También ejecutaré la versión original, la primera corrección y la segunda corrección sobre mi conjunto de 5GB recién recopilado de plantillas creadas por el usuario que recopilé de mi proyecto, compararé los árboles de análisis y el rendimiento.

Pruebas agregadas, las pruebas de rendimiento no mostraron degradación, por lo que se hace por mi parte. :)

Recién lanzado 2.11.2 con esto.

¿Fue útil esta página
0 / 5 - 0 calificaciones