Jinja keep indentation on include or macro

爷,独闯天下 提交于 2019-12-20 11:52:52

问题


I am wondering if there is any way to keep the indentation with jinja when adding a include or macro inside a file. I would like to use jinja to generating a code file. An example would be

File: class.html

class MyClass:
     def someOp():
         pass

     {% include "someOp.html" %}

File: someOp.html

def someOp2():
    pass

The result of the template should be:

class MyClass:
     def someOp():
         pass

     def someOp2():
         pass

If there any way to make jinja prepend the indent before the include tag for each line in the included file? Or is there any way to customize jinja to do this?


回答1:


One way is to wrap the include in a macro, then because the macro is a function, its output can be passed through the indent filter:

class MyClass:
    def someOp():
        pass

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

By default 'indent' indents 4 spaces and does not indent the first line, you can use e.g. 'indent(8)' to indent further, see http://jinja.pocoo.org/docs/templates/#list-of-builtin-filters for more details.

If what you're including is defined as a macro to begin with then the further wrapper macro is not needed, and you can jump straight to using the indent filter.




回答2:


I was looking in Jinja2 to achieve the same and got to conclusion aligning multi-line block indentation with the originating Jinja statement is not possible currently.

I've posted a small PR to Jinja to add a new syntax {%* ... %} and {{* ... }} that does exactly this. See the PR for details:

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




回答3:


It would be easier if Jinja provided the facility. It looks like the facility was in the pipeline but the issue is currently closed (20 Nov 2019) and the pull request hasn't yet been merged, some technical issues still needing to be resolved.

The following is a solution in the mean time.

auto_indent() detects the indent level of a variable in a host template, then applies that indent to a piece of text:

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)

Example:

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))

Example output:

>>> 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

For versions of Python before 3.6, remove the type hints for function parameters.



来源:https://stackoverflow.com/questions/10821539/jinja-keep-indentation-on-include-or-macro

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!