【问题标题】:Jinja keep indentation on include or macroJinja 在包含或宏上保持缩进
【发布时间】:2012-05-30 17:47:22
【问题描述】:

我想知道在文件中添加包含或宏时是否有任何方法可以保持 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 来做到这一点?

【问题讨论】:

  • 这也是我想看到的。
  • 我也觉得这很有用,尤其是在尝试制作 yml 文件(缩进很重要,但跨多个文件更难理解)或任何需要人类可读的东西时跨度>

标签: templates jinja2


【解决方案1】:

我写了一个jinja2 extension 来解决这个长期存在的问题。它通过挂钩到jinja.ext.Extension 提供的preproccess api 来自动化先前提出的使用{% filter indent(...) %} 的解决方案。

如果您在 jinja.Environment 中添加扩展名,您可以使用以下语法将正确缩进的模板包含在模板的其余部分中。注意indent content 指令。

class MyClass:
 def someOp():
     pass

 {% include "someOp.html" indent content %}

然后渲染的结果就变成了

class MyClass:
 def someOp():
     pass

 def someOp2():
     pass

【讨论】:

    【解决方案2】:

    一种方法是将include包裹在一个宏中,然后因为宏是一个函数,它的输出可以通过缩进过滤器传递:

    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

    如果您所包含的内容被定义为开头的宏,则不需要进一步的包装宏,您可以直接跳转到使用缩进过滤器。

    【讨论】:

    • 感谢您的回答,这似乎是我所需要的,但我可以让它使用它所在行的缩进级别(而不是明确说明级别)吗?
    • 能否在渲染过程中在 Python 端提供这个宏,这样我就不必在每个模板中重新实现它了?
    • 我使用此方法作为渲染 yaml 文件的基础,并注意到它在定义宏的位置留下了空行,使用 {%- macro someop() -%}{% include "someOp.html" %}{% endmacro %} 来避免这种情况。
    • 嗨@Razvi 这个答案自动缩进你问:stackoverflow.com/a/58027589/2480947
    【解决方案3】:

    我在 Jinja2 中寻找同样的方法,并得出结论,目前无法将多行块缩进与原始 Jinja 语句对齐。

    我已经向 Jinja 发布了一个小 PR,以添加一个新语法 {%* ... %}{{* ... }} 来实现这一点。详情见 PR:

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

    【讨论】:

      【解决方案4】:

      如果 Jinja 提供设施会更容易。它看起来像some work was done on this,但问题目前已关闭(2019 年 11 月 20 日),拉取请求尚未合并。这可能是因为缩进很快就会变得很棘手(例如,想想制表符和空格。)

      以下是我发现的一个简单的解决方案,它可以有效地生成 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-05
        • 1970-01-01
        相关资源
        最近更新 更多