Jinja: Conservación del prefijo de espacio en blanco en cadenas de varias líneas

Creado en 16 feb. 2013  ·  12Comentarios  ·  Fuente: pallets/jinja

En el motor StringTemplate, que he usado en algunos proyectos para emitir código C, los prefijos de espacios en blanco se agregan automáticamente en las líneas de salida:

PrintCFunction(linesGlobal, linesLocal) ::= <<
void foo() {
    if (someRuntimeFlag) {
        <linesGlobal>
        if (anotherRuntimeFlag) {
            <linesLocal>
        }
    }
}
>>

Cuando esta plantilla se representa en StringTemplate, el espacio en blanco que precede a las cadenas linesGlobal y linesLocal de líneas múltiples, se copia para todas las líneas emitidas. El código C generado es, por ejemplo:

void foo() {
    if (someRuntimeFlag) {
        int i;
        i=1;   // <=== whitespace prefix copied in 2nd
        i++;   // <=== and 3rd line
        if (anotherRuntimeFlag) {
            int j=i;
            j++; //  <=== ditto
        }
    }
}

Soy nuevo en Jinja2 e intenté replicar esto:

#!/usr/bin/env python
from jinja2 import Template

linesGlobal='\n'.join(['int i;', 'i=1;'])
linesLocal='\n'.join(['int j=i;', 'j++;'])

tmpl = Template(u'''\
void foo() {
    if (someRuntimeFlag) {
        {{linesGlobal}}
        if (anotherRuntimeFlag) {
            {{linesLocal}}
        }
    }
}
''')

print tmpl.render(
    linesGlobal=linesGlobal,
    linesLocal=linesLocal)

... pero lo vi producir esto:

void foo() {
    if (someRuntimeFlag) {
        int i;
i=1;
        if (anotherRuntimeFlag) {
            int j=i;
j++;
        }
    }
}

... que no es lo que quiero. Logré hacer que la salida emitiera prefijos de espacios en blanco adecuados con esto:

...
if (someRuntimeFlag) {
    {{linesGlobal|indent(8)}}
    if (anotherRuntimeFlag) {
        {{linesLocal|indent(12)}}
    }
}

... pero podría decirse que esto es malo, ya que necesito contar manualmente los espacios en blanco para cada cadena que emito ...

¿Hay una forma mejor de la que me estoy perdiendo?

Comentario más útil

Al emitir YAML o Python, esto se vuelve bastante crucial.

Todos 12 comentarios

Me interesa básicamente lo mismo. El problema también ha surgido en stackoverflow: http://stackoverflow.com/questions/10821539/jinja-keep-indentation-on-include-or-macro

+1

También es cierto cuando se usa el formulario:

    {{linesGlobal|join('\n')}}

Esta forma no se comporta como cabría esperar, dado que jinja2 es el que emite las nuevas líneas, debe asegurarse de que permanezcan alineadas con el último nivel de sangría.

¡Sería muy bueno tenerlo! Conduciría a plantillas mucho más agradables y resultados renderizados al mismo tiempo.

¿Por qué no crear otra opción de espacio en blanco similar a {% + y {% - que antepone la sangría actual a lo que sea que evalúe? Podría ser {% = o {% |

+1

+1

necesario aquí para crear plantillas de documentación de planos de API:

{% macro entity_one() -%}
{
    "one": 1,
    "two": 2
}
{% endmacro -%}

+ Response 200 (application/json):

        {
            "entity": [
                {{ entity_one() }}
            ]
        }

se renderiza ahora:

+ Response 200 (application/json):

        {
            "entity": [
                {
    "one": 1,
    "two": 2

}

            ]
        }

Al emitir YAML o Python, esto se vuelve bastante crucial.

Me encontré con el mismo problema.

¿Existe una solución alternativa por ahora que no sea definir una macro para cada tempkate incluido e ingresar manualmente la sangría?

Perdón por revivir este problema anterior, me encontré con el mismo problema y buscar en Google me trajo aquí. Después de mirar un poco más a mi alrededor, descubrí que a estas alturas hay una buena manera de lograr esto a través del filtro de sangría

@kaikuchn ¡ Gracias, amigo! Funciona.

@kaikuchn , @ Cigizmoond-Vyhuholev Chicos, no estoy seguro de seguirlos ... como pueden ver en mi informe original en la parte superior, menciono una solución con el filtro indent , pero también afirmo claramente que no aborda el problema de la manera simple y poderosa que, por ejemplo, StringTemplate lo hace, porque te obliga a contar los espacios de sangría cada vez que necesitas emitir un bloque de líneas ... Si tienes que hacer eso y estás generando código de cualquier forma (C, Python, lo que sea), rápidamente abandonará el proceso por completo ...

Por otra parte, es posible que haya entendido mal lo que quiso decir ... ¿Puede compartir exactamente cómo implementaría mi requisito original que se muestra en la parte superior? es decir, generar el mismo tipo de salida con la sintaxis Jinja2? Esto es lo que no me gusta ...

if (someRuntimeFlag) {
    {{linesGlobal|indent(8)}}
    if (anotherRuntimeFlag) {
        {{linesLocal|indent(12)}}
    }
}

... porque necesito contar el "8" y el "12" en todas y cada una de las plantillas donde emito código. En comparación, en StringTemplate ...

PrintCFunction(linesGlobal, linesLocal) ::= <<
void foo() {
    if (someRuntimeFlag) {
        <linesGlobal>
        if (anotherRuntimeFlag) {
            <linesLocal>
        }
    }
}
>>

Noté que el # 919 se cerró debido a un cambio de código que aparentemente requeriría una refactorización importante del PR. Sin embargo, realmente me gustaría ver esta función implementada en mi motor de plantillas favorito.

Si esto todavía es algo que a los desarrolladores centrales les gustaría ver implementado (la comunidad seguramente lo quiere, ya que es el tema de relaciones públicas y abierto con la mayor cantidad de aprobación), estaría ansioso por ayudar y tal vez incluso tratar de implementar esto por mi cuenta.

También me encantaría ver esta función. Observaré que la solución alternativa indent no se puede utilizar cuando se desconoce el nivel de sangría. p.ej:

{{field.type}} {{field.name}}; {{field.comment|indent(??)}}

Donde el nivel de sangría que se conservará depende de la longitud de los dos primeros valores.

Tengo una hipótesis. ¿Qué pasa con el primer nivel de sangría del recorte a la izquierda en la declaración de bloque?

Ejemplo:

{%- macro some_yaml_block(sub_block) ~%}
  label indented with how many spaces: 2
  amount of spaces that will be trimmed due to that "~" char above: 2
  {%- if sub_block ~%}
    "yay! we can indent ifs!": true
    the minimal indent in this if block is 2, so:
      this extra-indented value is still indented properly
  {%- endif %}
{%- endmacro -%}

now we invoke that here:
  {{ some_yaml_block(true) }}

La representación sería:

now we invoke that here:
  label indented with how many spaces: 2
  amount of spaces that will be trimmed due to that "~" char above: 2
  "yay! we can indent ifs!": true
  the minimal indent in this if block is 2, so:
    this extra-indented value is still indented properly

Básicamente, al terminar un bloque con ~%} , Jinja:

  1. Elimine los primeros caracteres si es una secuencia EOL, como se indica en el parámetro de entorno newline_sequence .
  2. Renderiza el contenido internamente.
  3. Cuente cuántos caracteres de espacios en blanco comunes prefieren esas líneas renderizadas.
  4. Quítelos del principio de cada línea.

Si, más adelante, necesita incluir este bloque con alguna sangría específica, todo lo que necesita es llamarlo como some_yaml_block|indent . Dado que la sangría se normalizó en la declaración del bloque, luego puede especificarla sin problemas. Y el bloque se comportaría de forma coherente en todas las llamadas.

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