Krupniok
Krupniok

Reputation: 49

How can Python code within html string be evaluated?

I'm trying to create an HTML file, which certains Python variables that have to be evaluated. My code looks like this:

name = ['Nora', 'John', 'Jack', 'Jessica']

html = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Names</title>
</head>
<body>
    <ul>
        <li>Mother: <%= name[0] %></li>
        <li>Father: <%= name[1] %></li>
        <li>Son: <%= name[2] %></li>
        <li>Daughter: <%= name[3] %></li>
    </ul>
</body>
</html>
"""

Html_file = open("names.html","w")
Html_file.write(html)
Html_file.close()

However, the array is not interpreted during output. My HTML source looks like this:

...
<ul>
    <li>Mother: <%= name[0] %></li>
    <li>Father: <%= name[1] %></li>
    <li>Son: <%= name[2] %></li>
    <li>Daughter: <%= name[3] %></li>
</ul>
...

How can I evaluate the python code that's surrounded by <%= %>?

Upvotes: 1

Views: 178

Answers (4)

Ajax1234
Ajax1234

Reputation: 71461

You can use regex to more accurately evaluate the templating:

import re
name = ['Nora', 'John', 'Jack', 'Jessica']
def render_template(html, **kwargs):
  return re.sub('\<%\=\s[a-zA-Z]+\[\d+\]\s%\>', '{}', html).format(*[kwargs.get(re.findall('[a-zA-Z]+', i)[0])[int(re.findall('\d+', i)[0])] for i in re.findall('(?<=\<%\=\s)[a-zA-Z]+\[\d+\](?=\s%)', html)])

print(render_template(html, name = name))

Output:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Names</title>
</head>
<body>
<ul>
    <li>Mother: Nora</li>
    <li>Father: John</li>
    <li>Son: Jack</li>
    <li>Daughter: Jessica</li>
  </ul>
</body>
</html>

This solution will also work if name elements are being accessed in random order:

html = """
<body>
  <ul>
    <li>Mother: <%= name[3] %></li>
    <li>Father: <%= name[1] %></li>
    <li>Son: <%= name[0] %></li>
    <li>Daughter: <%= name[2] %></li>
 </ul>
</body>
"""
print(render_template(html, name = name))

Output:

<body>
  <ul>
   <li>Mother: Jessica</li>
   <li>Father: John</li>
   <li>Son: Nora</li>
   <li>Daughter: Jack</li>
</ul>
</body>

Upvotes: 0

Taku
Taku

Reputation: 33724

There're multiple ways of achieving this

First off, if you're on Python 3.6 or higher, there's a new syntax called f-string, which is basically a method of string formatting at run time.

name = ['Nora', 'John', 'Jack', 'Jessica']

html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Names</title>
</head>
<body>
    <ul>
        <li>Mother: {name[0]}</li>
        <li>Father: {name[1]}</li>
        <li>Son: {name[2]}</li>
        <li>Daughter: {name[3]}</li>
    </ul>
</body>
</html>
"""

print(html)

The way you use f-string is fairly easy, add an f in the beginning of the string, and use { } instead of <%= %>.


If you're on any Python version, or wanted a version-compatible method, there are many other ways of string interpolation (ie. C-style string formatting %, Python string formatting .format(), and string concatenation), one of which (.format()) is in the other answers.


Without changing your HTML: using re and eval

If you don't have control over where you got the "need-to-be-substituted" html, or if you have to use the <%= %> scheme, you can simply use a combination of re and eval:

from re import sub


name = ['Nora', 'John', 'Jack', 'Jessica']

html = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Names</title>
</head>
<body>
    <ul>
        <li>Mother: <%= name[0] %></li>
        <li>Father: <%= name[1] %></li>
        <li>Son: <%= name[2] %></li>
        <li>Daughter: <%= name[3] %></li>
    </ul>
</body>
</html>
"""

html = sub(r"<%=\s*(\S+)\s*%>", lambda l: eval(l.group(1)), html)

print(html)

Upvotes: 1

Veera Balla Deva
Veera Balla Deva

Reputation: 788

    html = """<ul>
        <li>Mother:  {0} </li>
        <li>Father:  {1} </li>
        <li>Son: {2} </li>
        <li>Daughter: {3} </li>
        </ul>"""
    name = ['Nora', 'John', 'Jack', 'Jessica']
    print(html.format(*name))
    >>><ul>
        <li>Mother: Nora </li>
        <li>Father: John </li>
        <li>Son: Jack </li>
        <li>Daughter: Jessica </li>
       </ul>

Upvotes: 0

martinarroyo
martinarroyo

Reputation: 9701

A string won't automatically evaluate code inside, but you can achieve this in a handful of ways:

Introduce placeholders and format your string:

name = ['Nora', 'John', 'Jack', 'Jessica']

html = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Names</title>
</head>
<body>
    <ul>
        <li>Mother: {0}</li>
        <li>Father: {1}</li>
        <li>Son: {2}</li>
        <li>Daughter: {3}</li>
    </ul>
</body>
</html>
"""

Html_file = open("names.html","w")
Html_file.write(html.format(name[0], name[1], name[2], name[3])
Html_file.close()

This is a very simple way to do it. There are more advanced approaches, such as using a template engine. Here you can read more about them.

Upvotes: 0

Related Questions