Jinja: Preservando o prefixo de espaço em branco em strings de várias linhas

Criado em 16 fev. 2013  ·  12Comentários  ·  Fonte: pallets/jinja

No mecanismo StringTemplate - que usei em alguns projetos para emitir código C - os prefixos de espaço em branco são adicionados automaticamente nas linhas de saída:

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

Quando este modelo é renderizado em StringTemplate, o espaço em branco que prefixa as strings linesGlobal e linesLocal multilinha é copiado para todas as linhas emitidas. O código C gerado é, por exemplo:

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

Eu sou novo no Jinja2 - e tentei replicar isso:

#!/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)

... mas vi produzir isto:

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

... que não é o que eu quero. Consegui fazer com que a saída emitisse prefixos de espaço em branco adequados com isto:

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

... mas isso é indiscutivelmente ruim, já que preciso contar manualmente os espaços em branco para cada string que emito ...

Existe uma maneira melhor que estou perdendo?

Comentários muito úteis

Ao emitir YAML ou Python, isso se torna muito importante.

Todos 12 comentários

Estou interessado basicamente na mesma coisa. O problema também surgiu em stackoverflow: http://stackoverflow.com/questions/10821539/jinja-keep-indentation-on-include-or-macro

+1

Também verdadeiro ao usar o formulário:

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

Esta forma não se comporta como esperado - uma vez que jinja2 é quem emite as novas linhas, ele deve se certificar de que elas permaneçam alinhadas com o último nível de indentação.

Seria muito bom ter isso! Isso levaria a modelos muito melhores e a uma saída renderizada ao mesmo tempo.

Por que não criar outra opção de espaço em branco semelhante a {% + e {% - que precede o recuo atual em tudo o que for avaliado? Pode ser {% = ou {% |

+1

+1

necessário aqui para a documentação do modelo da API:

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

+ Response 200 (application/json):

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

é renderizado agora:

+ Response 200 (application/json):

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

}

            ]
        }

Ao emitir YAML ou Python, isso se torna muito importante.

Encontrou o mesmo problema.

Existe uma solução alternativa agora além de definir uma macro para cada tempkate incluído e inserir manualmente o recuo?

Desculpe por reviver este problema antigo, acabei de encontrar o mesmo problema e pesquisando me trouxe aqui. Depois de olhar mais um pouco, descobri que agora existe uma boa maneira de fazer isso por meio do filtro de indentação

@kaikuchn Obrigado, cara! Funciona.

@kaikuchn , @ Cigizmoond-Vyhuholev Pessoal, não tenho certeza se entendi ... como podem ver no meu relatório original no início, menciono uma solução alternativa com o filtro indent - mas também declaro que não aborda o problema da maneira simples e poderosa que, por exemplo, StringTemplate o faz, porque força você a contar espaços de indentação toda vez que precisar emitir um bloco de linhas ... Se você tiver que fazer isso e estiver gerando código de qualquer forma (C, Python, qualquer que seja), você abandonará muito rapidamente o processo ...

Então, novamente, posso ter entendido mal o que você quis dizer ... Você pode compartilhar exatamente como você implementaria meu requisito original mostrado no topo? ou seja, gerar o mesmo tipo de saída com a sintaxe Jinja2? Isso é o que eu não gosto ...

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

... porque preciso contar o "8" e o "12" em todo e qualquer template onde emito código. Em comparação, em StringTemplate ...

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

Percebi que o # 919 foi fechado devido a uma alteração no código que aparentemente exigiria uma grande refatoração do PR. No entanto, eu realmente gostaria de ver esse recurso implementado em meu mecanismo de modelagem favorito.

Se isso ainda for algo que os desenvolvedores centrais gostariam de ver implementado (a comunidade com certeza quer, pois é o PR e o problema aberto com o maior número de polegares para cima), eu ficaria ansioso para ajudar e talvez até mesmo tentar implementar por conta própria.

Também adoraria ver esse recurso. Observarei que a solução alternativa indent não pode ser usada quando o nível de recuo não é conhecido. por exemplo:

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

Onde o nível de indentação a ser preservado depende do comprimento dos dois primeiros valores.

Eu tenho uma hipótese. E quanto ao primeiro nível de recuo à esquerda na declaração de bloco?

Exemplo:

{%- 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) }}

A renderização seria:

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

Basicamente, ao terminar um bloco com ~%} , Jinja:

  1. Remova os primeiros caracteres se for uma sequência EOL, conforme indicado no parâmetro newline_sequence Environment.
  2. Renderize o conteúdo internamente.
  3. Conte quantos caracteres de espaço em branco comuns prefixam essas linhas renderizadas.
  4. Retire-os desde o início de cada linha.

Se você, posteriormente, precisar incluir este bloco com algum recuo específico, tudo que você precisa é chamá-lo como some_yaml_block|indent . Como o recuo foi normalizado na declaração do bloco, você pode especificá-lo posteriormente sem problemas. E o bloqueio se comportaria de forma consistente nas chamadas.

Esta página foi útil?
0 / 5 - 0 avaliações