-1

I have a flask server running and a multiprocessing loop working. i want to be able to change a variable in the flask server and have it be viewed by the loop in a if statement. here is my code, i have removed a lot of things that i thought was not important to show.

the variable that needs to be changed is toggle (only needs to be 1 or 0) it gets changed in sms() and used with a if statement in the loop

# I removed a lot of stuff that i dont think was needed 

import time
import math
from pyicloud import PyiCloudService
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
from math import sin, cos, sqrt, atan2, radians
from twilio.rest import TwilioRestClient 
from multiprocessing import Process, Value
from flask import Flask, request
from twilio import twiml
import os
#os.system('clear')

app = Flask(__name__)

sent = 0 #only needed for loop

toggle = 0 # this is the varable that needs to be changed and viewed



def distance(x):
    #returns distance in miles or km

@app.route('/sms', methods=['POST'])
def sms():
    global toggle

    #command replys
    error = 'You did not enter a valid command'
    on = 'Automatic tracker has been turned on.'
    already_on = 'The automatic tracker is already on.'
    off = 'Automatic tracker has been turned off.'
    already_off = 'The automatic tracker is already off.'
    toggle_error = 'There was a changing the status automatic tracker.'
    status0 = 'The automatic tracker is currently off.'
    status1 = 'The automatic tracker is currently on.'
    status_error = 'There was a error checking the status of the automatic tracker.'

    message_body = request.form['Body'] # message you get when sending a text to twilio number ex. i send "On" to the number and message_body will = "On"
    resp = twiml.Response() # what twilio will send back to your number

    if message_body == "on" or message_body == "On": #turn on automatic tracker
        if toggle == 0: #set toggle to 1
            toggle = 1
            resp.message(on)
            print on
            time.sleep(3)
        elif toggle == 1: #say toggle is on
            resp.message(already_on)
            print already_on
            time.sleep(3)
        else: #say toggle_error
            resp.message(toggle_error)
            print toggle_error
            time.sleep(3)
    elif message_body == "off" or message_body == "Off": #turn off automatic tracker
        if toggle == 1: #set toggle to 0
            toggle = 0
            resp.message(off)
            print off
            time.sleep(3)
        elif toggle == 0: #say toggle is off
            resp.message(already_off)
            print already_off
            time.sleep(3)
        else: #say toggle_error
            resp.message(toggle_error)
            print toggle_error
            time.sleep(3)
    elif message_body == "status" or message_body == "Status": #return status of automatic tracker
        if toggle == 1: #say toggle is on
            resp.message(status1)
            print status1
            time.sleep(3)
        elif toggle == 0: #say toggle is off
            resp.message(status0)
            print status0
            time.sleep(3)
        else: #say status_error
            resp.message(status_error)
            print status_error
            time.sleep(3)
    else: #say invalid command
        resp.message(error)
        print error
        print " "
        time.sleep(3)
    return str(resp)
    print " "

def record_loop(loop_on):
    while True:
        global sent
        global toggle
        if toggle == 1: #toggle does not read as 1 when changed in sms()
            if distance(2) < 2.5:
                if sent == 0:
                    print "CLOSE"
                    print "sending message"
                    print distance(2)
                    client.messages.create(
                    to="phone number to send to", #I removed the 2 numbers
                    from_="twillio number", #I removed the 2 numbers
                    body= "CLOSE!", 
                    )
                    sent = 1
                else:
                    print "CLOSE"
                    print "not sending"
                    print distance(2)
            else:
                print "not close"
                print distance(2)
                sent = 0
        else:
            print 'toggle is off'
            print toggle
        time.sleep(1)
        print " "
        time.sleep(20)

if __name__ == "__main__":
   recording_on = Value('b', True)
   p = Process(target=record_loop, args=(recording_on,))
   p.start()  
   app.run(use_reloader=False)
   p.join()
alex
  • 3
  • 1
  • 6

1 Answers1

2

Multiprocessing efectivelly runs the target function in another process - which also means that is an entirely new Python program - this otherprogram won't share any variables with the parent program. That is why your use of a global variable to communicate with your secondary loop won't work in this way: the toggle variable available inside your record_loop is independent of the one used by the program's views.

In a well formed application, all you'd need is to use an instance of multiprocessing.Queue to communicate values to code running in a function in another process.

However, "weel behaved" is not what you have there - the use of a multiprocessign.Queue assumes the originating process always has access to the one Queue object that is shared with the sub-process. However, you are using a Flask application, which in turn uses the WSGI Python model - which in turn makes mandatoy that each HTTP request processing (that is, each call to your sms view function) is independent of all other resources in the code - inclusing global variables. That is because in a WSGI server context, each HTTP request could be served by a different proces entirely (that will vary with teh WSGI server configuration).

So, for "real world" cases of HTTP requests that trigger a longer process on the server side, one of the best approaches is to use Celery. With Celery, you explicitly start your workers, that exist independent of the processes used to answer HTTP requests (even if both the view and worker code lies on the same .py file). Your views will call functions in the worker in a transparent way, and they will simply execute in asynchronously in another process.

The multiprocessing approach with Queue can be used, however, if you don't mind have several processes running your record_loop code in parallel, without one knowing anything about the other - since you are just triggering remote API's on this code, it looks like it would not be a problem.

jsbueno
  • 99,910
  • 10
  • 151
  • 209