Jinja сохраняет отступ при включении или макросе

Мне интересно, есть ли способ сохранить отступ с помощью jinja при добавлении включения или макроса внутри файла. Я хотел бы использовать jinja для создания файла кода. Примером может быть

Файл: class.html

class MyClass:
     def someOp():
         pass

     {% include "someOp.html" %}

Файл: someOp.html

def someOp2():
    pass

Результат шаблона должен быть:

class MyClass:
     def someOp():
         pass

     def someOp2():
         pass

Есть ли способ заставить jinja добавлять отступ перед тегом включения для каждой строки во включенном файле? Или есть способ настроить jinja для этого?


person Razvi    schedule 30.05.2012    source источник
comment
Это то, что я тоже хотел бы увидеть.   -  person Dr. Jan-Philip Gehrcke    schedule 04.03.2013
comment
Я также нашел бы это полезным, особенно при попытке создать файлы yml (где отступы важны, но их сложнее понять в нескольких файлах) или что-либо, что должно быть удобочитаемым для человека.   -  person hiljusti    schedule 28.10.2018


Ответы (3)


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

class MyClass:
    def someOp():
        pass

    {% macro someop() %}{% include "someOp.html" %}{% endmacro %}
    {{ someop()|indent }}

По умолчанию «отступ» отступает на 4 пробела и не делает отступ в первой строке, вы можете использовать, например. 'indent(8)' для дальнейшего отступа см. http://jinja.pocoo.org/docs/templates/#list-of-builtin-filters для более подробной информации.

Если то, что вы включаете, определено как макрос для начала, то дальнейший макрос-оболочка не нужен, и вы можете сразу перейти к использованию фильтра отступов.

person AmirS    schedule 12.06.2012
comment
Спасибо за ответ, это похоже на то, что мне нужно, но могу ли я заставить его использовать уровень отступа строки, на которой он находится (вместо явного указания уровня)? - person Razvi; 18.06.2012
comment
Могу ли я предоставить этот макрос на стороне Python во время рендеринга, чтобы мне не приходилось повторно реализовывать его в каждом шаблоне? - person bluenote10; 21.06.2019
comment
Я использовал этот метод в качестве основы для рендеринга файла yaml и заметил, что он оставляет пустые строки после того, где был определен макрос, используйте {%- macro someop() -%}{% include "someOp.html" %}{% endmacro %}, чтобы избежать этого. - person LiveWireBT; 15.02.2021

Я искал в Jinja2, чтобы добиться того же, и пришел к выводу, что выравнивание отступа многострочного блока с исходным оператором Jinja в настоящее время невозможно.

Я отправил небольшой PR в Jinja, чтобы добавить новый синтаксис {%* ... %} и {{* ... }}, который делает именно это. Подробности смотрите в ПР:

https://github.com/pallets/jinja/pull/919

person wigwam    schedule 01.11.2018

Было бы проще, если бы Джинджа предоставила возможность. Похоже, что над этим была проделана некоторая работа, но проблема в настоящее время закрыта (20 ноября 2019), а запрос на включение еще не был объединен. Это может быть связано с тем, что с отступами довольно быстро возникают сложности (подумайте, например, о табуляциях и пробелах).

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

auto_indent() определяет уровень отступа переменной в основном шаблоне, а затем применяет этот отступ к фрагменту текста.

import os
import itertools
import jinja2


def indent_lines(text_lines: list, indent: int):
    return [' ' * indent + line for line in text_lines]


def matching_line(s, substring):
    lineno = s[:s.index(substring)].count('\n')
    return s.splitlines()[lineno]


def is_space(c):
    return c == ' '


def indentation(line: str) -> int:
    initial_spaces = ''.join(itertools.takewhile(is_space, line))
    return len(initial_spaces)


def auto_indent(template: str, placeholder: str, content_to_indent: str):
    placeholder_line = matching_line(template, '{{ ' + placeholder + ' }}')
    indent_width = indentation(placeholder_line)
    lines = content_to_indent.splitlines()
    first_line = [lines[0]]  # first line uses placeholder indent-- no added indent
    rest = indent_lines(lines[1:], indent_width)
    return os.linesep.join(first_line + rest)

Пример:

action_class = """\
class Actions:

    def __init__(self):
        pass
    
    def prequel(self):
        pass
    
    {{ methods }}
    
    def sequel(self):
        pass
"""

inserted_methods = """\
def create_branch():
    pass

def merge_branch():
    pass
"""


if __name__ == '__main__':
    indented_methods = auto_indent(action_class, 'methods', inserted_methods)
    print(jinja2.Template(action_class).render(methods=indented_methods))

Пример вывода:

>>> python indent.py
class Actions:

    def __init__(self):
        pass
    
    def prequel(self):
        pass
    
    def create_branch():
        pass
    
    def merge_branch():
        pass
    
    def sequel(self):
        pass
person Nick    schedule 20.09.2019