如何通过编程获取Jinja2命名空间中所有一级键的列表?
如何通过编程获取Jinja2命名空间中所有一级键的列表?
嘿,我之前开发Jinja2自定义扩展时也碰到过一模一样的困惑,刚好能给你指个明路!
其实你已经离答案很近了——Jinja2的Namespace对象内部是用__dict__来存储我们动态添加的属性的,只是你之前没在运行时直接访问它而已。你用dir(value)拿到的是对象的所有属性(包括内置方法和内部私有属性),但我们要的一级键其实都藏在value.__dict__里,只要过滤掉那些下划线开头的内部属性就行。
先给你修改一下你现有的InspectExtension代码,重点调整_render方法:
import pprint from jinja2 import nodes from jinja2.ext import Extension class InspectExtension(Extension): tags = {"inspect"} def __init__(self, environment): super().__init__(environment) def parse(self, parser): lineno = parser.stream.expect("name:inspect").lineno expr = parser.parse_expression() context = nodes.DerivedContextReference() return nodes.Output([ nodes.Const(expr), # 查看AST节点 nodes.TemplateData('\n'), # 优化输出格式 self.call_method("_render", [expr, context]) # 运行时检查 ]).set_lineno(lineno) def _render(self, value, context): # 提取命名空间的一级键,过滤Jinja2内部的下划线开头属性 namespace_keys = [k for k in value.__dict__ if not k.startswith('_')] result = { "class": value.__class__.__name__, "namespace_keys": namespace_keys, "context": list(context.get_all().keys()) } return pprint.pformat(result, compact=False) inspect = InspectExtension
用这个修改后的代码跑你的测试模板,就能在输出里看到"namespace_keys": ["test"]了,这就是你要的一级键列表。
再聊聊你提到的几个疑问点:
- 关于
NSRef节点:这个确实文档极少,它本质是Jinja2内部用来引用命名空间属性的节点类型(比如模板里写ns.test时底层可能会用到),但对于我们获取所有键的需求来说,完全没必要绕这个弯,运行时直接访问__dict__简单得多。 - 关于AST层面的尝试:如果你想在模板解析阶段(
parse方法里)获取命名空间的键,其实不太现实——因为命名空间的键是运行时动态添加的,解析模板的时候这些键还不存在,所以只能在运行时(也就是_render方法里)去获取。 - 你之前想的
For节点遍历:因为命名空间本身不是可迭代对象(除非你给它手动实现__iter__方法),所以直接遍历不行,但通过__dict__拿到键列表后,就可以在模板里用循环输出了。
要是你还想让命名空间本身支持直接迭代(比如模板里写{% for k in ns %}),也可以给Jinja2环境加个自定义过滤器,或者扩展Namespace类,但对于你的扩展需求来说,上面的方法已经足够解决问题了。
备注:内容来源于stack exchange,提问作者mesr
相关产品推荐
相关产品推荐

