How to change these global variables to non global variables? - python

I have this little program that parrots back whatever a user is saying. Right now I'm using two global variables to store the previous variable state and add 1 to it. I'm wondering if someone can please suggest some ways to do this that doesn't involve using global variables.
Note: There is no UI or front end on this thing. Its a webhook for a google home, so its just sitting server side sending things back and forth.
import random, time, os, atexit
from datetime import datetime
from random import choice
from random import shuffle
from flask import Flask, current_app, jsonify
from flask_assistant import Assistant, ask, tell, event, context_manager, request
from flask_assistant import ApiAi
app = Flask(__name__)
assist = Assistant(app)
api = ApiAi(os.environ['DEV_ACCESS_TOKEN'], os.environ['CLIENT_ACCESS_TOKEN'])
random.seed()
interrupt_count = 0
frustration_level = 0
def reset_things():
global interrupt_count
interrupt_count = 0
print("!reset_things: {0}, {1}".format(interrupt_count, frustration_level))
#assist.action('greeting')
def hello_world():
speech = 'This is the unxepected machine'
return ask(speech)
#assist.action('fallback', is_fallback=True)
def say_fallback():
print(dir(Assistant.request))
resp = request['result']['resolvedQuery']
default_resp = "boogie boo"
# if the user said soemthing
if resp:
# update the global variable
global interrupt_count
interrupt_count+=1
global frustration_level
if not interrupt_count % 3:
frustration_level+=1
print("!fallback: {0}, {1}".format(interrupt_count, frustration_level))
print(parrot)
return ask(parrot)
else:
print(default_resp)
return(default_resp)
#assist.action('help')
def help():
speech = "I am the help section"
return ask(speech)
#assist.action('quit')
def quit():
reset_things()
speech = "Leaving program"
return tell(speech)
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)

The simplest solution would probably be to store the data in a cookie. You don't need a database for the data if it's very small, and it's not important enough to warrant being stored on the server.
Another related possibility is to encode the variable state in the url to your service, and have the service provide the user with an updated url to click on with every reply.

Related

Updating DataFrame while API is running in Python

So I am trying to create an API that constantly reads from a CSV and returns information about it when requested. So far, I have created a flask API that reads the CSV file once and returns correctly. However, I can't seem to make it constantly update. My working code is something like this.
app = flask.Flask(__name__)
app.config["DEBUG"] = True
dfchat = pd.read_csv(path)
escaper = None
# for now, this is just to make sure the program keeps running even if there is an error
def escape_route():
global escaper
while escaper != "Y":
escaper = str(input("Exit now? Enter \'Y\': \n")).strip()
os._exit(os.X_OK)
def sample_function(dfchat):
#app.route('/sample_text', methods=['GET'])
def sample_endpoint():
# this function filters dfchat and returns whatever
def main():
global dfchat
escape_route_thread = threading.Thread(target = escape_route)
escape_route_thread.start()
sample_function(dfchat)
app.run()
main()
I have tried creating another thread that updates the CSV file:
def retrieve_database():
global dfchat
while True:
time.sleep(0.1)
dfchat = pd.read_csv(path)
along with:
escape_route_thread = threading.Thread(target = retrieve_database)
escape_route_thread.start()
in the main function.
But that fails to update the dfchat data frame when the API launches. I have tested the thread by itself and it does update and return an updated data frame.
From what I understand so far, once an API runs, python code cannot change the API itself.
So,
Is there a way to update a running API with just python?
I'm asking for just python because I will not be able to manually enter a link like "/refresh" to do this. It has to be done by python.
Am I missing something?
Thank you very much for helping!
Edit:
I also tried to update the csv file for every API call. But that does but work either:
def sample_function():
dfchat = pd.read_csv(path)
#app.route('/sample_text', methods=['GET'])
def sample_endpoint():
# this function filters dfchat and returns whatever
Code defined at the root of the script (as was dfchat definition in your example) is executed once at the moment you start the flask server.
Code inside a Flask app route (function decorated with #app.route(...)) is executed at each API call to this route.
from flask import Flask
app = Flask(__name__)
path = "path/to/your/csv/file.csv"
#app.route('/sample_text', methods=['GET'])
def sample_endpoint():
dfchat = pd.read_csv(path)
# do what you have to do with the DF
Also note that Flask handles errors without stopping the API, and has great documentation to help you : https://flask.palletsprojects.com/en/2.0.x/quickstart/#a-minimal-application
So I realized that the solution is really simple. I'm new to CS and APIs in general so I did not realize how #app.route worked in Flask. Outside of #app.route cannot change a route but updating a variable inside a route does work. I accidentally kept updating dfchat outside of #app.route.
def sample_function():
# instead of putting dfchat here
#app.route('/sample_text', methods=['GET'])
def sample_endpoint():
dfchat = pd.read_csv(path) # it should go here.
# this function filters dfchat and returns whatever
Thank you yco for helping me realize this.

Single instance of class from another module

I come from Java background and most of my thinking comes from there. Recently started learning Python. I have a case where I want to just create one connection to Redis and use it everywhere in the project. Here is how my structure and code looks.
module: state.domain_objects.py
class MyRedis():
global redis_instance
def __init__(self):
redis_instance = redis.Redis(host='localhost', port=6379, db=0)
print("Redus instance created", redis_instance)
#staticmethod
def get_instance():
return redis_instance
def save_to_redis(self, key, object_to_cache):
pickleObj = pickle.dumps(object_to_cache)
redis_instance.set(key, pickleObj)
def get_from_redis(self, key):
pickled_obj = redis_instance.get(key)
return pickle.loads(pickled_obj)
class ABC():
....
Now I want to use this from other modules.
module service.some_module.py
from state.domain_objects import MyRedis
from flask import Flask, request
#app.route('/chat/v1/', methods=['GET'])
def chat_service():
userid = request.args.get('id')
message_string = request.args.get('message')
message = Message(message_string, datetime.datetime.now())
r = MyRedis.get_instance()
user = r.get(userid)
if __name__ == '__main__':
global redis_instance
MyRedis()
app.run()
When I start the server, MyRedis() __init__ method gets called and the instance gets created which I have declared as global. Still when the service tries to access it when the service is called, it says NameError: name 'redis_instance' is not defined I am sure this is because I am trying to java-fy the approach but not sure how exactly to achieve it. I read about globals and my understanding of it is, it acts like single variable to the module and thus the way I have tried doing it. Please help me clear my confusion. Thanks!

Python - Flask - open two webpages in default browser

The following code is a variation on the answer given here.
Two webbrowser.open() are requested, and they are spaced out slightly in time.
Two web pages are rendered all right, but both show the second (Chris) parameter.
Why is Pat not greeted?
import random, threading, webbrowser
from flask import Flask, render_template_string
app = Flask(__name__)
#app.route('/<name>')
def index(name):
return render_template_string('''<h3>Hello, {{ name }}!</h3>''', name=name)
if __name__ == '__main__':
names = ['Pat', 'Chris']
port = 5000 + random.randint(0, 999)
wait = 1.25
for name in names:
url = "http://127.0.0.1:{0}/{1}".format(port, name)
threading.Timer(wait, lambda: webbrowser.open(url)).start()
wait += 0.5
app.run(port=port, debug=False)
Defining a callback function in a loop is the problem, it's called late binding. Both lambdas ultimately see the last value of url. Replace the lambda with:
functools.partial(webbrowser.open, url)
or scrap the timer and just time.sleep(0.5) in the loop.

How to delay the GPIO when using Flask-ask on Pi

I've followed a tutorial on getting Amazon Echo to talk to my Raspberry and it works beautifully.
The part I have added is the GPIO parts in the yes intent so that it flashed an LED when it receives a yes answer from the Echo. Again, it works perfectly.
Now I want to make it so there is a delay on the GPIO so that the Alexa response speaks then the LED flashes. How can I do this?
I've tried creating a variable in the yes intent that gets set to '1' when she answers. Then I tried to return it at the end by adding , variableName then creating an if command outside of the yes intent function, but the variable never seems to come out of the yesintent. I also tried defining the variable as global but still no joy. I just can't think of anything else to Google to do this and I wondered if anyone could help?
The code is as follows:
from flask import Flask
from flask_ask import Ask, statement, question, session
import json
import requests
import time
import unidecode
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
app = Flask(__name__)
ask = Ask(app, "/shocker")
#app.route('/')
def homepage():
welcome = 'hi there, how the fluff is it?'
return statement(welcome)
#ask.launch
def start_skill():
welcome_message = 'Hello there, would you like me to do something?'
return question(welcome_message)
#ask.intent("YesIntent")
def yes_intent():
GPIO.setwarnings(False)
GPIO.setup(7, GPIO.OUT)
GPIO.output(7,1)
time.sleep(1)
GPIO.output(7,0)
yes_message = 'the thing has been done'
return statement(yes_message)
#ask.intent("NoIntent")
def no_intent():
no_message = 'well then why are you wasting my time?'
return statement(no_message)
if __name__ == '__main__':
app.run()

Global variable is None instead of instance - Python

I'm dealing with global variables in Python. The code should work fine, but there is a problem. I have to use global variable for instance of class Back. When I run the application it says that back is None which should be not true because the second line in setup() function - 'back = Back.Back()'
# -*- coding: utf-8 -*-
from flask import Flask
from flask import request
from flask import render_template
import Search
import Back
app = Flask(__name__)
global back
back = None
#app.route('/')
def my_form():
return render_template('my-form.html')
def setup():
global back
back = Back.Back()
def is_ascii(s):
return all(ord(c) < 128 for c in s)
#app.route('/', methods=['POST'])
def search():
from time import time
pattern = request.form['text']
startTime = time()
pattern=pattern.lower()
arr = []
if len(pattern)<1:
arr.append('Incorrect input')
currentTime = time()-startTime
return render_template('my-form.html', arr=arr, time=currentTime)
arr = []
search = Search.Search(pattern,back)
results = search.getResults()
..................
return render_template('my-form.html', arr=arr, time=currentTime, pattern=pattern)
app.debug=True
if __name__ == '__main__':
setup()
app.run()
Why is the back variable None instead of instance of Back class? Thanks
The Flask development server runs your module twice. Once to run the server itself, and another time in a child process so it can reload your whole script each time you make a change to it. It is that second process that won't run the __main__ guarded code and the global is left as None.
You'll get the same problem if you used another WSGI server; it'd import your file as a module, not as the initial script and the __main__ guard is not executed.
Use a #app.before_first_request function instead; it is guaranteed to be executed when the very first request is handled for this process. This also keeps your global working if you moved to a proper WSGI container that used multiple child processes to scale your site:
#app.before_first_request
def setup():
global back
back = Back.Back()

Categories

Resources