如何在 jinja 中获取过滤器的调用代码

How to get the calling code for a filter in jinja

tl;dr:是否有可能找出 jinja 模板中从过滤器内部调用 jinja 过滤器的位置?

背景

所以我在名为 get_image_path() 的神社模板中使用了一个过滤器,本质上它采用文件路径 and/or url 并将其放入正确的格式对于应用程序。

我正在尝试格式化回溯输出,以便在有人出错时我可以了解它们的来源。基本上,当有人传递一些不是可用路径的东西时,我希望能够抛出错误并提供一条消息,其中包含模板中调用过滤器的行,以便人们知道要查找哪个变量。

那么我是否可以在调用过滤器的模板中获取行,或者我是否必须进行手动回溯格式化?

get_image_path()

的当前代码
def get_image_path(path:str) -> str:
    # I omitted the docstring
    try:
        if path.startswith("http"):
            return path

        elif path.startswith("images"):
            return f"{path}"

        else:
            return f"images/{path}"
    except AttributeError as e:
        traceback.print_tb(e.__traceback__, limit=7)
        ValueError(f"ValueError: Could not get image path: {path}")

我的解决方案

对我来说,问题是由 AttributeError:

def get_image_path(path:str) -> str:
    try:
        if path.startswith("http"):
            return path

        elif path.startswith("images"):
            return f"{path}"

        else:
            return f"images/{path}"
    except AttributeError:
        for frameInfo in stack(): # Get the frame for the error raised
            if frameInfo.frame.f_globals.get("__jinja_template__") is not None: # Find the jinja template namespace if it exists
                template = frameInfo.frame.f_globals.get("__jinja_template__")
                break
        if not path: # If the image path is False (usually because a required image wasn't provided)
            raise ValueError(f"\n\nNo path provided for required image in {template.filename} #line {template.get_corresponding_lineno(currentframe().f_back.f_lineno)}")
        else: # If it's just an invalid image path
            raise ValueError(f"\n\nCould not get image path: {path}\n Error occured on \n{template.filename} #line {template.get_corresponding_lineno(currentframe().f_back.f_lineno)}")

当过滤器因传递 boolean 而不是图像路径而失败时,现在提供此消息:

ValueError: No path provided for required image in c:\users\kieran\appdata\local\programs\python\python39\lib\site-packages\ezcv\themes\ethereal\index.jinja #line 45

背景及工作原理

所以当在 except 语句中这些行时:

for frameInfo in stack(): # Get the frame for the error raised
            if frameInfo.frame.f_globals.get("__jinja_template__") is not None: # Find the jinja template namespace if it exists
                template = frameInfo.frame.f_globals.get("__jinja_template__")
                break

正在使用inspect module to check if a value in the global namespace called __jinja_template__ is found. If this value is found it means the error was raised in a template and from that we can get the Jinja Template Object。该对象包含有关我想要的行号和文件名的信息,因此它被分配给 template 变量。

从那里template.filename会给你源文件路径,template.get_corresponding_lineno(currentframe().f_back.f_lineno)(文档here)会给你行号,剩下的代码只是格式化ValueError 将我想要的信息传回给用户。

感谢@β.εηοιτ.βε 建议查看他们对 的回答以弄清楚这一切