Python 支持白名单的模板替换?

Python 支持白名单的模板替换?,python,string,templates,Python,String,Templates,是否可以使用string.Template强制替换白名单中提到的一组变量($placeholder*)和跳过其余部分($data等) substitute为所有不存在的占位符抛出异常safe\u substitute忽略所有不存在的占位符——那么中间有什么东西吗 from string import Template whitelist = {"placeholder1", "placeholder2", "placeholder3"

是否可以使用
string.Template
强制替换白名单中提到的一组变量(
$placeholder*
跳过其余部分(
$data
等)

substitute
为所有不存在的占位符抛出异常
safe\u substitute
忽略所有不存在的占位符——那么中间有什么东西吗

from string import Template

whitelist = {"placeholder1", "placeholder2", "placeholder3", "placeholder4"}

query = Template("select * from t where c1= $data and c1 = $placeholder1 and c2 = $placeholder2")

print(query.safe_substitute({"placeholder1": "c1"}))  # Not safe.

query.substitute({"placeholder1": "c1"})  # Extra safe.
最后,我可以通过循环白名单来进行验证,但这似乎效率低下。另外,我看不到有人支持这一点


如果没有好的解决方案,我是否可以将子类
模板
和重载
替换为以下内容:

def substitute(self, mapping=_sentinel_dict, /, **kws, whitelist):
    if mapping is _sentinel_dict:
        mapping = kws
    elif kws:
        mapping = _ChainMap(kws, mapping)
    # Helper function for .sub()
    def convert(mo):
        # Check the most common path first.
        named = mo.group('named') or mo.group('braced')
        if named is not None:
            # ====> Suggested change. <====
            if named in whitelist:
                return str(mapping[named])
            else:
                return mo.group()  # ====> Taken from safe_substitute.
        if mo.group('escaped') is not None:
            return self.delimiter
        if mo.group('invalid') is not None:
            self._invalid(mo)
        raise ValueError('Unrecognized named group in pattern',
                         self.pattern)
    return self.pattern.sub(convert, self.template)
def替换(self,映射=_sentinel_dict,/,**kws,白名单):
如果映射是_sentinel_dict:
映射=kws
elif kws:
映射=\链映射(kws,映射)
#.sub()的助手函数
def转换(mo):
#首先检查最常见的路径。
named=mo.group('named')或mo.group('bracked')
如果“命名”不是“无”:
#=>建议的更改。从保险箱中取出。
如果mo.group(‘转义’)不是无:
返回自分隔符
如果mo.group('invalid')不是无:
自身失效(mo)
raise VALUERROR('模式中无法识别的命名组',
自我评价(模式)
返回self.pattern.sub(转换,self.template)

我想说,您提议的实现几乎完美无瑕

  • 首先,请注意,此实现不允许您执行类似于
    MyTemplate(“$whitelist”).substitute(whitelist='a')
    并因此获得
    'a'
    (因为您不能为参数
    whitelist
    传递多个值)
  • 然后,如果在白名单中命名,则需要将
    :return str(mapping[named])
    更改为稍微复杂一点的内容。事实上,如果
    命名
    不在
    白名单
    中,而是属于
    映射
    的一部分,那么您就无法正确处理这种情况。因此,您需要执行以下操作
以下是一个工作实现:

from collections import ChainMap
from string import Template

class MyTemplate(Template):
    def substitute(self, mapping=None, whitelist=None, **kws):
        mapping = kws if mapping is None else ChainMap(kws, mapping)
        whitelist = {} if whitelist is None else whitelist
        def convert(mo):
            named = mo.group('named') or mo.group('braced')
            if named is not None:
                if named in mapping:
                    return str(mapping[named])
                if named in whitelist:
                    raise KeyError(named)
                return mo.group()
            if mo.group('escaped') is not None:
                return self.delimiter
            if mo.group('invalid') is not None:
                self._invalid(mo)
            raise ValueError('Unrecognized named group in pattern', self.pattern)
        return self.pattern.sub(convert, self.template)
以下是一些例子:

>>> whitelist = {'a', 'b'}
>>> temp = MyTemplate('Lorem $a $b sit ${c}')
>>> temp.substitute({'a': 'ipsum', 'b': 'dolor', 'c': 'amet'}, whitelist)
'Lorem ipsum dolor sit amet'
>>> temp.substitute({'a': 'ipsum', 'b': 'dolor'}, whitelist)
'Lorem ipsum dolor sit ${c}'
>>> temp.substitute({'a': 'ipsum', 'c': 'amet'})
'Lorem ipsum $b sit amet'
>>> temp.substitute({'a': 'ipsum', 'c': 'amet'}, whitelist)
...
KeyError: 'b'

子类化
模板
(而不是实现另一个解决方案)的优点是,子类附带了
模板
的所有其他功能(例如,您可以将分隔符“$”更改为其他内容,或者您可以定义自己的规则来标识占位符等).

我想将
白名单
保留为一个全局
变量(或每个模板),但在提交问题时将其更改为一个参数。我会仔细看看。谢谢@Nishant的一个选项是在创建MyTemplate时将其传递给MyTemplate,但每次要更改
白名单时都需要创建一个新的template对象。它实际上取决于您的需要,例如是
白名单
常量还是随时间而变化?除非业务发生变化,否则它是常量:-)
>>> whitelist = {'a', 'b'}
>>> temp = MyTemplate('Lorem $a $b sit ${c}')
>>> temp.substitute({'a': 'ipsum', 'b': 'dolor', 'c': 'amet'}, whitelist)
'Lorem ipsum dolor sit amet'
>>> temp.substitute({'a': 'ipsum', 'b': 'dolor'}, whitelist)
'Lorem ipsum dolor sit ${c}'
>>> temp.substitute({'a': 'ipsum', 'c': 'amet'})
'Lorem ipsum $b sit amet'
>>> temp.substitute({'a': 'ipsum', 'c': 'amet'}, whitelist)
...
KeyError: 'b'