4

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.

4
  • Is it not possible to pre format your values? Commented Dec 11, 2015 at 2: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. Commented Dec 11, 2015 at 2:38
  • Why not have multiple files? Or just store your templates separately. Commented Dec 11, 2015 at 23:54
  • See also: custom placholder syntax Commented Oct 29, 2017 at 10:18

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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.
In that case you are going to want to write your own templating engine.
* See also: stackoverflow.com/a/41231617/42223 ;; for a solution that extends the str.format() method with a custom class.
-1

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

1 Comment

I would like to avoid splitting the template, because it comes from a file. I will update the question to clarify this.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.