How to escape $ on Python string Template class?

后端 未结 3 1192
无人共我
无人共我 2021-01-01 17:56

Introduction

The string module has a Template class, that lets you make substitutions in a string using a mapping object, for instance:

>>>          


        
相关标签:
3条回答
  • 2021-01-01 18:19

    Python will not do string substitution over multiple lines

    If you have this string

    criterion = """
        <criteria>
        <order>{order}</order>
          <body><![CDATA[{code}]]></body>
        </criteria>
    """
    
    criterion.format(dict(order="1",code="Hello")
    

    results in:

    KeyError: 'order'
    

    A solution is to use the string.Template module

    from string import Template
    
    criterion = """
        <criteria>
        <order>$order</order>
          <body><![CDATA[$code]]></body>
        </criteria>
    """
    
    Template(criterion).substitute(dict(order="1",code="hello")
    

    NOTE: you have to prefix the keywords with a $ not wrap them in {}

    output is:

     <criteria>
        <order>1</order>
          <body><![CDATA[hello]]></body>
        </criteria>
    

    Full docs are: https://docs.python.org/2/library/string.html#template-strings

    0 讨论(0)
  • 2021-01-01 18:27

    The docs say that you can replace the pattern as long as it contains all necessary named groups:

    import re
    from string import Template
    
    
    class TemplateIgnoreInvalid(Template):
        # override pattern to make sure `invalid` never matches
        pattern = r"""
        %(delim)s(?:
          (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
          (?P<named>%(id)s)      |   # delimiter and a Python identifier
          {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
          (?P<invalid>^$)            # never matches (the regex is not multilined)
        )
        """ % dict(delim=re.escape(Template.delimiter), id=Template.idpattern)
    
    
    def check_substitution(template, **mapping):
        try:
            TemplateIgnoreInvalid(template).substitute(mapping)
        except KeyError:
            return False
        else:
            return True
    

    Tests

    f = check_substitution
    assert f('var is $var', var=1)
    assert f('$ var is $var', var=1)
    assert     f('var is $var and foo is $foo', var=1, foo=2)
    assert not f('var is $var and foo is $foo', var=1)
    assert     f('$ var is $var and foo is $foo', var=1, foo=2)
    assert not f('$ var is $var and foo is $foo', var=1)
    # support all invalid patterns
    assert f('var is $var and foo is ${foo', var=1)
    assert f('var is $var and foo is ${foo', var=1, foo=2) #NOTE: problematic API
    assert     f('var is $var and foo is ${foo and ${baz}', var=1, baz=3)
    assert not f('var is $var and foo is ${foo and ${baz}', var=1)
    

    It works for all invalid occurences of the delimiter ($).

    The examples show that ignoring invalid patterns conceals simple typos in the template so it is not a good API.

    0 讨论(0)
  • 2021-01-01 18:36

    This is a Quick Fix (Using recursion):

    def check_substitution(tem, m):
        try:
            string.Template(tem).substitute(m)
        except KeyError:
            return False
        except ValueError:
            return check_substitution(tem.replace('$ ', '$'), m) #strip spaces after $
        return True
    

    I Know its take a longer time if there is more than One Space between $ and var , so you may improve it by using Regular Expression.

    EDIT

    escaping $ into $$ makes more sense [ Thanks @Pedro ] so you can catch ValueError by this statement:

    return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$
    
    0 讨论(0)
提交回复
热议问题