处理宏时如何获取源文件的当前行?

How to get current line of source file when processing a macro?

我想用 jinja2 预处理 C 源代码,我希望一些宏能够输出 #line 行:

#!/usr/bin/env python3
from jinja2 import *
@pass_context
def mymacro(ctx):
    return '#line ?? "??"'
env = Environment()
env.globals["mymacro"] = mymacro
rr = env.from_string(
    """
// file.h
{{ mymacro() }}
"""
).render()
print(rr)

如何在 mymacro 全局范围内获取当前行?我尝试检查 jinja2.runtime.Context,但找不到任何有用的信息。这可能吗?请注意,当抛出异常时,宏调用行是可见的 - 因此它存储在某处。

这是带来解决方案的线路:

template = tb.tb_frame.f_globals.get("__jinja_template__")

来源:debug.py#L55
在此上下文中,变量 tb 是异常回溯。

然后,进一步观察,我意识到 Jinja 正在使用这一行 __jinja_template__ to frame where the template lines are in the stack of Python

有了这个,以及他们稍后在 debug.py 文件中使用的函数 get_corresponding_lineno

template = tb.tb_frame.f_globals.get("__jinja_template__")

if template is not None:
    lineno = template.get_corresponding_lineno(tb.tb_lineno)

来源:debug.py#L58

现在很清楚如何实现它了:

  • 获取整个 Python 堆栈
  • 循环直到找到模板边界
  • get_corresponding_lineno
  • 的帮助下翻译模板一行中的当前行Python代码

这给出:

#!/usr/bin/env python3
from jinja2 import *
from inspect import stack, currentframe

def mymacro():
    for frameInfo in stack():
        if frameInfo.frame.f_globals.get("__jinja_template__") is not None:
            template = frameInfo.frame.f_globals.get("__jinja_template__")
            break
    return (
        '#line '
        f'{template.get_corresponding_lineno(currentframe().f_back.f_lineno)}'
    )
    
env = Environment()
env.globals["mymacro"] = mymacro

rr = env.from_string(
"""
// file.h

{{ mymacro() }}
"""
).render()

print(rr)

打印我们:


// file.h

#line 4