2

I have the following HTML template in a Python variable:

template = """
    <script>
        function work()
        {
            alert('bla');
        }
    </script>

    Success rate is {value:.2%} percent.
"""

I want to substitute {value:.2%} with some decimal number with the appropriate formatting. Since my template may contain a lot of JavaScript code, I want to avoid escaping the curly braces with {{ and }}, so using template.format(...) directly is not an option.

Using Template(template).substitute(...) also seems impossible because I want advanced formatting for value.

I could probably replace {value:.2%} with a corresponding %(value)... syntax and then use template % .... But I'm not a fan of this syntax because most of the variables in a real-world template won't need advanced formatting, so I want to keep the placeholder syntax simple.

So my question is, is it possible to use custom placeholders in the template that would allow for advanced formatting when necessary, i.e. to have simply {{value}} or [[value]] but also {{value:.2%}} or [[value:.2%]] ?

Edit: Finally, I want to avoid errors that .format(...) would produce when the template contains placeholders, such as {{dont_want_this_substituted}}, for which the passed dictionary doesn't contain a value. That is, I want to only substitute only certain placeholders, not all that appear in the template.

Edit 2: To achieve what I want, I can grab all placeholders with a regular expression first, then format their contents and finally make the replacement in the template. But I wonder if an easier solution exists.

Edit 3: It was suggested that I split the template to avoid issues with format(). I would like to avoid this, however, because the template actually comes from a file.

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Iliyan Georgiev
  • 153
  • 1
  • 7
  • Is it not possible to pre format your values? – Cody Guldner Dec 11 '15 at 02:01
  • @CodyGuldner I could, but I want to specify the formatting in the template itself. I could grab all placeholder with a regular expression and then substitute them by first formatting their contents, but I was wondering if a simpler solution exists. I'll edit the question to clarify this. – Iliyan Georgiev Dec 11 '15 at 02:38
  • Why not have multiple files? Or just store your templates separately. – leewz Dec 11 '15 at 23:54
  • **See also:** [custom placholder syntax](https://stackoverflow.com/questions/35574349/python-format-string-with-custom-delimiters/41231617) – dreftymac Oct 29 '17 at 10:18

2 Answers2

1

Simply preprocess your template. For example, if you want [[...]] to be your template markers and leave single { and } characters alone:

template = """
    <script>
        function work()
        {
            alert('bla');
        }
    </script>

    Success rate is [[value:.2%]] percent.
"""

result = template.replace("{", "{{").replace("}", "}}").replace("[[", "{").replace("]]", "}").format(value=0.8675309)

Trying to do it all with varying numbers of curly brackets is tricksy, so I would definitely use some other characters. Careful, though, things like [[ and ]] could reasonably occur in legitimate JavaScript code. Might be better to use something that never could.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • Thanks, this is an option indeed. Though a bit more tricky if I want to use `{{...}}` as a placeholder, i.e. would probably require some regular expression replacement. Wonder if there's a better approach though? I also edited the original question asking for the ability to only replace certain placeholders. – Iliyan Georgiev Dec 11 '15 at 02:11
  • In that case you are going to want to write your own templating engine. – kindall Dec 11 '15 at 21:59
  • 1
    * **See also:** http://stackoverflow.com/a/41231617/42223 ;; for a solution that extends the str.format() method with a custom class. – dreftymac Dec 19 '16 at 21:55
0

Forget regex and preprocessing. You should break up the template so that the variable parts aren't in the same string as the <script> parts.

head = """
    <script>
        function work()
        {
            alert('bla');
        }
    </script>
"""


template = """
    {head}

    Success rate is {value:.2%} percent.
"""

Finally, I want to avoid errors that .format(...) would produce when the template contains placeholders, such as {{dont_want_this_substituted}}, for which the passed dictionary doesn't contain a value. That is, I want to only substitute only certain placeholders, not all that appear in the template.

It might be hacky, but you can use collections.defaultdict, as shown in this answer. Code sample from that answer (modified).

from collections import defaultdict

my_csv = '{optional[first]},{optional[middle]},{optional[last]}'
print( my_csv.format( optional=defaultdict(str, first='John', last='Doe') ) )
Community
  • 1
  • 1
leewz
  • 3,201
  • 1
  • 18
  • 38