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.
Related
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.
I would like to know, Is it possible to run a function after response from web.py service, which function takes long time to run?
Lets say some example as below.
file Name: code.py
import web
import time
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def GET(self):
try:
with open('filename.txt', 'a') as file:
for i in range(100):
time.sleep(1)
file.write("No of times: {}".format(i))
return "some json response"
except:
return "Exception occurred"
if __name__ == "__main__":
app.run()
When I run the above code, obviously it will take time because as we are using time module for sleep one sec and then write into file. So, I should wait 100 seconds for get the response from service.
I want to skip this 100 seconds waiting time.
Expected: First return response to client and then run this part in background?
Can somebody provide some solution. Thanks..
Have a look python documentation for Thread.run()
Note:
With background task you won't be able to return "Exception occurred" as you're doing now. I believe you're OK with it.
Here's a small easy solution. There are other ways too but I feel you should explore more by yourself since you're a python beginner. :)
import web
import time
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def writeToFile():
try:
with open('filename.txt', 'a') as file:
for i in range(100):
time.sleep(1)
file.write("No of times: {}".format(i))
# Log completion
except:
# Log error
def GET(self):
thread = Thread(target=writeToFile)
thread.start()
return {<myJSON>}
if __name__ == "__main__":
app.run()
I have two Python scripts:
tnk_flask.py: This creates a flask webserver and two pages.
pitoka.py: this creates a random number.
My goal is the following:
When I run pitonka.py, I always generate a random number which I'd like to pass to tnb_flask.py, but, after refreshing those pages nothing changes.
What can be the mistake?
tnb_flask.py:
from flask import Flask # ezzel importáljuk ki
from pitonka import *
app = Flask(__name__)
#app.route('/')
def index():
denis = alma()
return str(denis)
#app.route('/tuna')
def index__():
return str(numbi)
if __name__ == "__main__":
app.run(debug=True)
pitonka.py:
from flask import Flask
# import tnb_flask
import random
numbi = random.randint(10, 100)
print(numbi)
def alma():
return numbi + 17
In pitonka.py you are assigning the random number to a variable. So it will only get the random variable once. Instead return the random number in the function.
def alma():
return random.randint(10, 100) + 17
You could serve static pages via flask and re-render them from templates in pitonka.py
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.
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()