Google App Engine, start / stop continuously script on button click
<p>I am looking for guidance in how to achieve start / stop functionality of a function, which makes a <code>restAPI</code> to retrieve data in JSON format continuously, by clicking a button on a rendered html page through <code>webapp2</code> and hosted on GAE?</p>

<p>The current behaviour is once the http request is completed the function that was called of course stops (<code>while self._running == True</code>)(normal behaviour according to the GAE documentation).</p>

<p>main.py :</p>

<pre><code>#!/usr/bin/env python
#
import webapp2
from google.appengine.api import urlfetch
from matplotlib.path import Path as mpPath
import json
import base64
import socket
import logging
from threading import Thread
import jinja2

template_dir = os.path.join(os.path.dirname(__file__))
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir),
            extensions=['jinja2.ext.autoescape'], autoescape=True)

# create a UDP socket for sending the commands
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

# create the stop / start functionality of a thread that runs infinite until 
# thread is terminated

class CMX:
    def __init__(self):
        self._running = True

    def terminate(self):
        self._running = False

    def run(self):
        storedCredentials = False
        username = None
        password = None

        ip_address = 'someip'                 # commands are send to the broadcast address for addressing the zones(bays)
        port = someport                       # use of port 50011 because no return packet is send back, could cause the 
                                              # lights not to execute the command when using broadcast.

        # define the boundaries of the different zones
        zone = [[(106.03,141.19),(158.94,141.19),(158.94,194.50),(106.03,194.50)],
              [(103.76,168),(62.26,168),(62.26,77.86),(103.67,77.86)],
              [(106.38,77.86),(191.95,77.86),(191.95,106.52),(106.38,106.52)]]

        flag_zone_1 = False
        flag_zone_2 = False
        flag_zone_3 = False

        while self._running == True:

            restURL = 'http://someurl' 
            print restURL

            if not storedCredentials:
                username = 'username'
                password = 'password'
                storedCredentials = True
            try:
                request = urlfetch.fetch(url = restURL, headers={"Authorization": "Basic %s" % base64.b64encode(username +':'+ password)})

                &lt;perform actions and other function calls&gt;
                 .
                 .

            except urlfetch.Error:
                logging.exception('Caught exception fetching url')


class Handler(webapp2.RequestHandler):
    def write(self, *a, **kw):
        self.response.out.write(*a, **kw)

    def render_str(self, template, **params):
        t = jinja_env.get_template(template)
        return t.render(params)

    def render(self, template, **kw):
        self.write(self.render_str(template, **kw))        

class MainPage(Handler):
    def get(self):
        button = "Start Demo"
        running = False
        self.render('page.html', button = button, run = running)

    def post(self):
        startDemo = CMX()
        t = Thread(target=startDemo.run, args=())
        t.daemon = True

        if self.request.get('button') == "Start Demo":
            button = "Stop Demo"
            running = True
            self.render('page.html', button = button, run = running) 
            t.start() 

        else:
            button = "Start Demo"
            running = False
            self.render('page.html', button = button, run = running) 
            startDemo.terminate()


def which_zone(xcoord, ycoord, zone):
  point = (xcoord, ycoord)
  in_zone_1 = mpPath(zone[0]).contains_point(point)
  in_zone_2 = mpPath(zone[1]).contains_point(point)
  in_zone_3 = mpPath(zone[2]).contains_point(point)

  if in_zone_1 == True:
    return "Zone 1"
  elif in_zone_2 == True:
    return "Zone 2"
  elif in_zone_3 == True:
    return "Zone 3"

def dim_lights(ip_address, port, control_string, sock):
  control_string = control_string + 'S0F10' +'\r'
  #sock.sendto(control_string, (ip_address, port))
  return control_string

def norm_lights(ip_address, port, control_string, sock):
  control_string = control_string + 'S255F10' +'\r'
  #sock.sendto(control_string, (ip_address, port))
  return control_string

app = webapp2.WSGIApplication([('/', MainPage)], debug=True)
</code></pre>

<p>page.html :</p>

<pre><code>{% extends "base.html" %}

{% block comment %}
{% autoescape true %}
    &lt;form method="post"&gt;
            &lt;div&gt;
                &lt;input type="submit" name="button" value="{{button}}"&gt;
                &lt;input type="hidden" name="run" value="{{run}}"&gt;
            &lt;/div&gt; 
            &lt;!-- &lt;div&gt;macAddress: &lt;input type="text" name="macAddress"&gt;&lt;br&gt;
                &lt;input type="submit" value="Submit"&gt;
            &lt;/div&gt; --&gt;

    &lt;/form&gt; 
{% endautoescape %}
{% endblock %}
</code></pre>

<p>The start/stop functionality is simple - just make the button control something like an <code>operation_is_stopped</code> flag persisted across requests (in the datastore, for example).</p>

<p>In case you didn't realize it yet your difficulty really comes from achieving the <strong>continuous</strong> operation that you want to control with that button. That's what's not really compatible with GAE - everything in GAE revolves around responding to requests, <em>in a limited amount of time</em>. You can not really have indefinitely-running proceses/threads in GAE.</p>

<p>But in many cases it's possible to implement a long-running, iterative continuous operation (like yours) as a flow of short-lived operations. In GAE that can be easily achieved using the <a href="https://cloud.google.com/appengine/docs/python/taskqueue/" rel="nofollow">task queues</a> - each iteration (in your case the body of the <code>while self._running == True</code> loop) is implemented as a response to a task queue request. </p>

<p>The flow is started by enqueueing a respective task when the "start" action is triggered. The flow is maintained by enqueueing a respective task after processing of a previous task request. And it's stopped by <strong>not</strong> enqueueing a new task :)</p>

<p>Something along these lines:</p>

<pre><code>def post(self):  # handler for the "long running" task requests

    # need to rebuild/restore your request context every time
    ...

    try:
        request = urlfetch.fetch(...)
        &lt;perform actions and other function calls&gt;
    except:
        ...

    finally:
        if not operation_is_stopped:
            # enqueue another "long running" task
            task = taskqueue.add(...)
</code></pre>

python google-app-engine