微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

实现 f-string like magic 来拉皮条 Django 的 format_html()

如何解决实现 f-string like magic 来拉皮条 Django 的 format_html()

我想拉皮条客 Django 的 format_html()

它已经很好地工作了,但是我的 IDE (PyCharm) 认为变量没有被使用,并以浅灰色绘制它们:

pycharm-variable-gray

AFAIK f-strings 使用了一些神奇的重写。

有没有办法实现这一点,以便 IDE 知道变量被使用了?

相关:Implement f-string like syntax,with Django SafeString support

这是我目前的实现:

def h(html):
    """
    Django's format_html() on steroids
    """
    def replacer(match):
        call_frame = sys._getframe(3)
        return conditional_escape(
            eval(match.group(1),call_frame.f_globals,call_frame.f_locals))
    return mark_safe(re.sub(r'{(.*?)}',replacer,html))

有人提出安全问题:我不打算创建用户可以编辑这些模板的 CMS。这些模板 h 字符串仅供开发人员方便地创建 HTML。

在写答案之前,请确保您了解conditional_escape()

解决方法

由于您似乎并没有使用肮脏的黑客,这里有一个比问题中的更肮脏的黑客:

class _escaper(dict):
    def __init__(self,other):
        super().__init__(other)
        
    def __getitem__(self,name):
        return conditional_escape(super().__getitem__(name))

_C = lambda value: (lambda: value).__closure__[0]
_F = type(_C)
try:
    type(_C(None))(None)
except:
    pass
else:
    _C = type(_C(None))

def h(f):
    if not isinstance(f,_F):
        raise TypeError(f"expected a closure,a {type(f).__name__} was given")
    closure = None
    if f.__closure__:
        closure = tuple(
            _C(conditional_escape(cell.cell_contents))
            for cell in f.__closure__
        )
    fp = _F(
        f.__code__,_escaper(f.__globals__),f.__name__,f.__defaults__,closure
    )
    return mark_safe(fp())

h 接受一个闭包,对于每个关闭的变量,它会创建另一个变量的转义副本,并修改闭包以捕获该副本。 globals dict 也被包装以确保对 globals 的引用同样被转义。然后立即执行修改后的闭包,其返回值被标记为安全并返回。因此,您必须传递 h 一个不接受任何参数的普通函数(例如,没有绑定方法),最好是返回 f 字符串的 lambda。

你的例子变成了:

foo = '&'
bar = h(lambda: f'<span>{foo}</span>')
assert h(lambda: f'<div>{bar}</div>') == '<div><span>&amp;</span></div>'

不过,有一件事。由于这是如何实现的,您只能直接引用插值点内的变量;没有属性访问,没有项目查找,没有别的。 (你也不应该在插值点放置字符串文字。)否则,它可以在 CPython 3.9.2 甚至 PyPy 7.3.3 中工作。我没有声称它曾经在任何其他环境中工作过,或者以任何方式面向未来。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。