11

I want to be able to visit a webpage and it will run a python function and display the progress in the webpage.

So when you visit the webpage you can see the output of the script as if you ran it from the command line and see the output in the command line.

What do I need to do in the function?

What do I need to do in the template?

EDIT:

I am trying to use Markus Unterwaditzer's code with a template.

{% extends "base.html" %}
{% block content %}

{% autoescape false %}

{{word}}

{% endautoescape %}

{% endblock %}

Python code

import flask
from flask import render_template
import subprocess
import time

app = flask.Flask(__name__)

@app.route('/yield')
def index():
    def inner():
        for x in range(1000):
            yield '%s<br/>\n' % x
            time.sleep(1)
    return render_template('simple.html', word=inner())
    #return flask.Response(inner(), mimetype='text/html')  # text/html is required for most browsers to show the partial page immediately

app.run(debug=True, port=8080)

And it runs but I don't see anything in the browser.

Siecje
  • 3,594
  • 10
  • 32
  • 50
  • The problem is in your subprocess.Popen, you're passing the wrong argument, None, as the command. Try playing around with Popen in the interactive shell and read its docs to get a feeling how Popen works. – Lie Ryan Feb 26 '13 at 16:42

5 Answers5

16

Here is a very simple app that streams a process' output with normal HTTP:

import flask
import time

app = flask.Flask(__name__)

@app.route('/yield')
def index():
    def inner():
        for x in range(100):
            time.sleep(1)
            yield '%s<br/>\n' % x
    return flask.Response(inner(), mimetype='text/html')  # text/html is required for most browsers to show the partial page immediately

app.run(debug=True)
Markus Unterwaditzer
  • 7,992
  • 32
  • 60
  • 1
    How can I use a python function? – Siecje Feb 25 '13 at 17:22
  • This actually might not be the best example, since it uses too much code that is unrelated to the question. I've updated it. Hope it's clearer now. – Markus Unterwaditzer Feb 26 '13 at 16:34
  • 1
    Alright I added a delay and it continuously outputs to a webpage!! Great! I am trying to output it to a template that uses the same styling as the rest of my site but I don't see anything. – Siecje Feb 27 '13 at 14:47
  • this works fine, but how to integrate these messages into a template? – Marco L. Jun 02 '13 at 13:35
  • No need type text/html required. More info about stream context can be found in here: http://flask.pocoo.org/docs/0.11/patterns/streaming/ – NamPNQ Sep 27 '16 at 07:02
  • @NamPNQ Not required for streaming in general, but for a demo not every content-type will show immediate results in a browser. – Markus Unterwaditzer Oct 01 '16 at 13:22
10

I had the same problem today, and found a way to adapt Markus Unterwaditzer's answer to work with a template.

The key is to use jinja's Template.generate() instead of Template.render(), which is used by flask's render_template().

import flask
import time

from jinja2 import Environment
from jinja2.loaders import FileSystemLoader

app = flask.Flask(__name__)

@app.route('/yield')
def index():
    def inner():
        for x in range(100):
            time.sleep(1)
            yield '%s<br/>\n' % x
    env = Environment(loader=FileSystemLoader('templates'))
    tmpl = env.get_template('result.html')
    return flask.Response(tmpl.generate(result=inner()))

app.run(debug=True)

Assuming that there is a template result.html that looks like this:

{% extends "layout.html" %}
{% block body %}
<body>
  {% for line in result %}
    {{ line }}
  {% endfor %}
</body>
{% endblock %}
ValarDohaeris
  • 6,064
  • 5
  • 31
  • 43
  • Can I up-vote this post 80 times?! I know this is discouraged, but THANK YOU! – S3DEV Aug 14 '18 at 15:01
  • adding the `env` and `tmpl` variables to my code gives me `jinja2.exceptions.UndefinedError: 'url_for' is undefined` – scrollout Feb 26 '21 at 15:59
3

I would think the most straightforward way to do this is to create your page, and then use AJAX to make periodic requests to the server, and update the content on your page using the results. That might include implementing long polling to do this.

Any way you do this, you'll need to somehow make your function non-blocking, so that you can still serve requests while it operates. You could manually write a function that serves a request if there is one waiting and otherwise continues running the next "chunk" of the function. That would involve decomposing your function into small parts, so that you can dispatch them as needed.

A better approach might be to use threading or multiprocessing, depending on whether your function is bound by IO or CPU usage.

Justin Blank
  • 1,768
  • 1
  • 15
  • 32
1

Do you have any plan to use WebSocket? if so, you can refer:Websocket with Flask and Gevent

Joe
  • 6,460
  • 1
  • 19
  • 15
-2

You can use meta refresh in one of the template

meta http-equiv="refresh" content="5"

forvaidya
  • 3,041
  • 3
  • 26
  • 33