Archive
Flask: linkify a text
Problem
I have a text that I present in a Flask application. The text can contain URLs, and I would like to linkify them, i.e. make them clickable links. Example:
before:
visit http://google.com for...
after:
visit <a href="http://google.com">http://google.com</a> for...
Solution
Before rolling out an own solution, it’s a good idea to check if there is a package for this problem. There is :), and it’s called bleach. Its usage couldn’t be simpler:
>>> import bleach
>>> bleach.linkify('an http://example.com url')
u'an <a href="http://example.com" rel="nofollow">http://example.com</a> url
Flask integration
In your main file (that calls app.run()) add the following filter:
import bleach
@app.template_filter('linkify')
def linkify(s):
return bleach.linkify(s)
Then use it in your jinja2 templates:
Description: {{ entry.description|linkify|safe }}
Warning! Apply the “safe” filter only if you trust the origin of the text you want to present in a linkified format.
[flask] Jinja2: don’t print empty lines
Problem
When using Flask (or Django), I don’t care much about the generated HTMLs. They may be ugly, who cares. However, there is one thing that bothers me. When I write this for instance in a template:
<div>
{% if True %}
yay
{% endif %}
</div>
the generated output looks like this:
<div>
yay
</div>
See? Jinja2 litters the output with empty lines. How to get rid of them?
Solution
The official documentation talks about this here. It says you need to enable both trim_blocks and lstrip_blocks. In Flask, you can do that like this:
... app = Flask(__name__) app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True ...
Done.
Jinja2 example for generating a local file using a template
Here I want to show you how to generate an HTML file (a local file) using a template with the Jinja2 template engine.
Python source (proba.py)
#!/usr/bin/env python
import os
from jinja2 import Environment, FileSystemLoader
PATH = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_ENVIRONMENT = Environment(
autoescape=False,
loader=FileSystemLoader(os.path.join(PATH, 'templates')),
trim_blocks=False)
def render_template(template_filename, context):
return TEMPLATE_ENVIRONMENT.get_template(template_filename).render(context)
def create_index_html():
fname = "output.html"
urls = ['http://example.com/1', 'http://example.com/2', 'http://example.com/3']
context = {
'urls': urls
}
#
with open(fname, 'w') as f:
html = render_template('index.html', context)
f.write(html)
def main():
create_index_html()
########################################
if __name__ == "__main__":
main()
Jinja2 template (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Proba</title>
</head>
<body>
<center>
<h1>Proba</h1>
<p>{{ urls|length }} links</p>
</center>
<ol align="left">
{% set counter = 0 -%}
{% for url in urls -%}
<li><a href="{{ url }}">{{ url }}</a></li>
{% set counter = counter + 1 -%}
{% endfor -%}
</ol>
</body>
</html>
Resulting output
If you execute proba.py, you will get this output:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Proba</title>
</head>
<body>
<center>
<h1>Proba</h1>
<p>3 links</p>
</center>
<ol align="left">
<li><a href="http://example.com/1">http://example.com/1</a></li>
<li><a href="http://example.com/2">http://example.com/2</a></li>
<li><a href="http://example.com/3">http://example.com/3</a></li>
</ol>
</body>
</html>
You can find all these files here (GitHub link).
Using Jinja2 for formatting strings
Example 1
from jinja2 import Environment
config = {
'ffmpeg': '/opt/ffmpeg.static/ffmpeg',
'bitrate': '600k',
'width': '480',
'height': '320',
'threads': '2'
}
command = """{{ ffmpeg }} -i {input} -codec:v libx264 -quality good -cpu-used 0
-b:v {{ bitrate }} -profile:v baseline -level 30 -y -maxrate 2000k
-bufsize 2000k -vf scale={{ width }}:{{ height }} -threads {{ threads }} -codec:a libvo_aacenc
-b:a 128k {output}""".replace('\n', ' ')
command = Environment().from_string(command).render(config)
print command
Output:
/opt/ffmpeg.static/ffmpeg -i {input} -codec:v libx264 -quality good -cpu-used 0 -b:v 600k -profile:v baseline -level 30 -y -maxrate 2000k -bufsize 2000k -vf scale=480:320 -threads 2 -codec:a libvo_aacenc -b:a 128k {output}
Here, command is still a template that can be further formatted, e.g.
print command.format(input="movie.avi", output="movie.mp4")
Example 2
Now let’s see a simpler example:
from jinja2 import Environment
print Environment().from_string("Hello {{ name }}!").render(name="Laci")
Output: “Hello Laci!”.
More examples
See https://gist.github.com/warren-runk/1317933.
Update (20130301)
Here I show you how to avoid using jinja2 :) Let’s take the first example above where jinja2 formatting was combined with Python’s string formatting.
Actually, in this example, jinja2 may be an overkill. We can combine old-style formatting and new-style formatting to have the same result:
config = {
'ffmpeg': '/opt/ffmpeg.static/ffmpeg',
'bitrate': '600k',
'width': '480',
'height': '320',
'threads': '2'
}
command = """{ffmpeg} -i %(input)s -codec:v libx264 -quality good -cpu-used 0
-b:v {bitrate} -profile:v baseline -level 30 -y -maxrate 2000k
-bufsize 2000k -vf scale={width}:{height} -threads {threads} -codec:a libvo_aacenc
-b:a 128k %(output)s""".replace('\n', ' ').format(**config)
Now we have a template that can be further formatted in a loop for instance:
print command % {'input': fname, 'output': output}
Thanks bulkan for the tip.
