像魔术一样实现f-string来拉皮条Django的format_html() [英] Implement f-string like magic to pimp Django's format_html()

查看:59
本文介绍了像魔术一样实现f-string来拉皮条Django的format_html()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想拉皮条

AFAIK f弦使用一些魔术重写.

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

相关:使用Django实现类似f-string的语法SafeString支持

这是我当前的实现方式:

  def h(html):"Django在类固醇上的format_html()"def replacer(match):call_frame = sys._getframe(3)返回conditional_escape(eval(match.group(1),call_frame.f_globals,call_frame.f_locals))返回mark_safe(re.sub(r'{(.*?)}',replacer,html)) 

有人提出了安全问题:我不打算创建供用户编辑这些模板的CMS.这些模板H字符串仅供开发人员使用,以方便的方式来创建HTML.

在写答案之前,请确保您了解解决方案

由于您似乎没有在上面使用肮脏的hack,因此这里的hack比问题中的hack更脏:

  class _escaper(dict):def __init __(自己,其他):super().__ init __(其他)def __getitem __(自己,姓名):返回conditional_escape(super().__ getitem __(name))_C = lambda值:(lambda:值).__closure __ [0]_F =类型(_C)尝试:类型(_C(无))(无)除了:经过别的:_C =类型(_C(无))def h(f):如果不是isinstance(f,_F):引发TypeError(f期望关闭,给出了{type(f).__ name__}"))闭包=无如果f .__ closure__:闭包=元组(_C(conditional_escape(cell.cell_contents))对于f .__ closure__中的单元格)fp = _F(f .__ code __,_ escaper(f .__ globals__),f .__ name __,f .__ defaults__,关闭)返回mark_safe(fp()) 

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

您的示例将变为:

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

不过,有一件事.由于这种实现方式,您只能直接引用插值点内部的变量;没有属性访问,没有项目查找,仅此而已.(您也不应该在插入点处放置字符串文字.)但是,否则,它可以在CPython 3.9.2甚至PyPy 7.3.3中使用.我对它在任何其他环境下都无法工作或以任何方式适应未来的情况没有任何要求.

I would like to pimp format_html() of Django.

It already works quite nicely, but my IDE (PyCharm) thinks the variables are not used and paints them in light-gray color:

AFAIK f-strings use some magic rewriting.

Is there a way to implement this, so that the IDE knows that the variables get used?

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

Here is my current implementation:

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))

Somebody raised security concerns: I don't plan to create CMS where a user can edit these templates. These template h-strings are only for developers to have a convenient way to create HTML.

Before writing an answer, be sure you know the magic of conditional_escape()

解决方案

Since you don’t seem above using dirty hacks, here’s a hack even dirtier than the one in the question:

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())

The h takes a closure, and for each variable closed over, it creates another, escaped copy of the variable, and modifies the closure to capture that copy instead. The globals dict is also wrapped to ensure references to globals are likewise escaped. The modified closure is then immediately executed, its return value is marked safe and returned. So you must pass h an ordinary function (no bound methods, for example) which accepts no arguments, preferably a lambda returning an f-string.

Your example then becomes:

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

There’s one thing, though. Due to how this has been implemented, you can only ever refer directly to variables inside interpolation points; no attribute accesses, no item lookups, nothing else. (You shouldn’t put string literals at interpolation points either.) Otherwise though, it works in CPython 3.9.2 and even in PyPy 7.3.3. I make no claims about it ever working in any other environment, or being in any way future-proof.

这篇关于像魔术一样实现f-string来拉皮条Django的format_html()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆