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

Мне интересно, есть ли способ сохранить отступ с jinja при добавлении include или макроса внутри файла. Я хотел бы использовать 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 префиксом отступа перед тегом include для каждой строки в включенном файле? Или есть ли способ настроить jinja для этого?

Ответы

Ответ 1

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

class MyClass:
    def someOp():
        pass

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

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

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

Ответ 2

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

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

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

Ответ 3

Было бы проще, если бы Джинджа предоставил объект, и похоже, что объект находится в стадии разработки, что очень приветствуется.

Следующий код полезен, когда код для отступа не является статическим файлом, а встроен в код. Этот другой ответ может использоваться для включения макросов, но не помогает с автоматическим отступом.

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

Обратите внимание, что вам нужно быть точным в отношении формата вашей переменной в template--, например. {{ variable }} работает, но {{variable}} потерпит неудачу: auto_indent() выполняет простое совпадение строк, чтобы найти переменную-заполнитель в шаблоне хоста.

Для версий Python до 3.6 просто удалите подсказки типа для параметров функции.

Чтобы частично отобразить шаблоны, т.е. использовать несколько проходов приведенного ниже кода для полной визуализации нескольких переменных, взгляните на этот ответ.