This question already has answers here:
Are global variables thread-safe in Flask? How do I share data between requests?
(4 answers)
Closed 3 years ago.
I have a small flask API setup as following,
from flask import Flask, request, jsonify, Response
import json
import subprocess
import os
app = Flask(__name__)
shellScripts = {
'redeploy': ['/bin/bash', 'redeploy.sh'],
'script-exec': ['/bin/bash', 'script-exec.sh']
}
def prepareShellCommand(json_data, scriptKey):
script=shellScripts[scriptKey]
print('script is')
print(script)
for key in json_data:
if scriptKey == 'redeploy':
script.append("-{0}".format(key[0]))
script.append(json_data[key])
return script
#app.route('/redeploy', methods=['POST'])
def setup_redeploy():
branches_data_json = request.get_json()
if ('frontendBranch' not in branches_data_json and 'backendBranch' not in branches_data_json):
return jsonify({'error': 'Need to provide at least one branch'}), 400
command = prepareShellCommand(branches_data_json, 'redeploy')
sp = subprocess.Popen(command)
return jsonify({'message': 'Redeployment under process'}), 201
#app.route('/execute', methods=['POST'])
def execute_script():
script_data_json = request.get_json()
if ('scriptPath' not in script_data_json):
return jsonify({'error': 'Need to provide script path'}), 400
command = prepareShellCommand(script_data_json, 'script-exec')
sp = subprocess.Popen(command)
return jsonify({'message': 'Script execution under process'}), 201
What's happening is, say I initiate an API endpoint, /execute with some data as {scriptPath: 'some-file'}, and it runs successfully. However, sometimes, regardless of change in the request body data, the API seems to work with the old data, {scriptPath: 'some-file'}, even if I am initiating the API with something like {scriptPath: 'new-file'}. And it doesn't change until I kill the python process, and restart it.
What could be the reason for this? I am running this as a development server, on a google cloud instance.
It's happening with both the endpoints, and I have a gut feeling that it's got something to do with either the subprocess or the dictionary that contains the boilerplate.
Can anyone help me with this?
This is almost certainly because you have defined shellScripts at module level but modify it from your handlers. The changes to the values of that dictionary will persist for the lifetime of the server process.
You should copy the value and modify that instead:
def prepareShellCommand(json_data, scriptKey):
script = shellScripts[scriptKey].copy()
Related
I'm creating a Rest API using Flask (2.0.1), Flask-Restful (0.3.9), and Python 3.9, and ran into an interesting issue/bug(?).
I was able to replicate this using the basic Flask Rest API code below
my_api.py
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
app = Flask(__name__)
rest_api = Api(app)
#rest_api.resource('/my/route')
class GetData(Resource):
#staticmethod
def get():
return jsonify({'hello': 'world'})
if __name__ == '__main__':
app.run(port=12345)
my_requests.py
import requests
url = r'http://127.0.0.1:12345/my/route'
rsp = requests.get(url)
print(rsp.json())
When I run the code above, my_requests.py returns the expected output. I get {'hello': 'world'} as my response
The issue I am running into is when I pass in form data into the get request to pull back data based on different parameters:
import requests
url = r'http://127.0.0.1:12345/my/route'
rsp = requests.get(url, data={'my': 'data'})
By adding in data, I get the following error:
ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)
After much troubleshooting, I figured out that my Rest API requires me to access the form data by calling request.form, as such
def get():
data = request.form
return jsonify({'hello': 'world'})
By calling request.form, only then does my_requests.py returns the expected output like before.
So my question is why does flask/flask-restful require me to access request.form to perform properly when passing in data? I won't always being accessing the data that's being passed in, so it seems weird that it would be designed this why
I am new to twilio python and flask. I tried to follow the twilio example for tracking sms status, but as I mentionned I get none as the return for this statement status=request.values.get('callstatus', None).
I want to track the call progress status and see its different status. I follow all the documentation but I am blocked. Thanks for your help.
from flask import Flask,request
from twilio.rest import Client
from twilio.twiml.voice_response import Dial, VoiceResponse
from pprint import pprint
import logging
logging.basicConfig(level=logging.INFO)
app = Flask(__name__)
twilioClient = Client('*******************', '*****************************')
#app.route("/voice")
def voice():
call=twilioClient.calls.create(
method='GET',
status_callback='http://*****.ngrok.io/response',
status_callback_event='initiated ringing answered completed',
status_callback_method='POST',
from_='+**********',
to='+*********',
url='http://demo.twilio.com/docs/voice.xml''
)
return call.sid
#app.route('/response', methods=['POST'])
def outbound():
status=request.values.get('callstatus', None)
logging.info('Status: {}'.format(status))
return ('', 204)
if __name__ == "__main__":
app.run(debug=true)
I understand that this is a relatively old post and that you might have figured out the solution as of now, but still I am posting my answer so that it will benefit other users who may face the same issue in future.
There are 2 issues that I can see in the code posted above:
Inside the function voice()
When you are making a request to the twilio server by using the API twilioClient.calls.create(), you are feeding the status_callback_event parameter with a single string.
Instead of passing a single string, you have to pass a list of strings like:
['initiated', 'answered', 'ringing', 'completed']
Inside the function outbound()
The parameter sent by twilio to the call back url is CallStatus and not callstatus. They are case sensitive.
status=request.values.get('CallStatus', None)
You can refer this link to check out all the call resources
I am testing a simple slack command, which is supposed to return a response upon invocation and then start a new thread where it will calculate new response and post it back. However, it seems like there is no way to call jsonify inside the thread, because if do that, it throws : RuntimeError: Working outside of application context.
from flask import abort, Flask, jsonify, request
import requests
import json
from dateutil.parser import parse
from threading import Thread
def backgroundworker(somedata,response_url):
payload = jsonify(
response_type='in_channel',
text="test"
)
requests.post(response_url,data=json.dumps(payload))
#app.route('/appmethodaddress',methods=['POST','GET'])
def receptionist():
response_url = request.form.get("response_url")
somedata = {}
thr = Thread(target=backgroundworker, args=[somedata,response_url])
thr.start()
return jsonify(message= "working on your request")
Is there an easy way to call jsonify inside backgroundworker ?
If you have to make a json out of it, use json.dumps() method (https://kite.com/python/docs/json.dumps).
This question already has answers here:
Are global variables thread-safe in Flask? How do I share data between requests?
(4 answers)
Closed 4 years ago.
I'm trying to build reply bot by using line api and python 2.7.15 .
this is my code
from flask import Flask, request, abort
from linebot import (LineBotApi, WebhookHandler)
from linebot.exceptions import (InvalidSignatureError)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage,)
app = Flask(__name__)
line_bot_api = LineBotApi('UIXwXWExxxxxxxxxxxxxxxxxxxxxxxxxxxx')
handler = WebhookHandler('458033a97c124xxxxxxxxxxxxxxxxxxxxx')
number = 1
#app.route("/")
def hello():
return str(number)
#app.route("/webhook", methods=['POST'])
def webhook():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
#handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
text = event.message.text
user_id = event.source.user_id
if(text == 'inc'):
#set global variable here
number += 1
line_bot_api.push_message(user_id,TextSendMessage(text=str(number)))
if __name__ == "__main__":
app.run()
then i implement this on heroku and i try chatting with my bot,
first i send messageinc, he reply me 2
then i try send inc once again ,he reply me 3 and i try again he replies me 1
i try send inc once again he reply me 4
what wrong? why my number doesn't continue?
Python backend apps are typically deployed in multiprocess configuration (the front server - apache, ngnix or whatever - runs multiple parallel processes to handle incoming requests), and any request can be served by any process. Global variables are per-process, so which state you will find your global depends on which process served the request. Conclusion: don't use global variables to store application state - use a shared database (of any kind, it doesn't have to be a SQL database, it just have to be shared amongst all processes).
This question already has an answer here:
Testing code that requires a Flask app or request context
(1 answer)
Closed 5 years ago.
First of all, I am very new at programming.
I am trying to save a variable from bash shell
>curl http://169.254.169.254/latest/meta-data/
this line would return data such as local-ipv4. And I am trying to use phython and flask to save those variables. I wrote
from flask import Flask, request
app = Flask(__name__)
#app.route('/')
def testRequest():
url1 = "http://169.254.169.254/latest/meta-data/"
name1 = request.get(url1)
nameText = name1.text
return nameText
testOutput = testRequest()
print testOutput
But this gives me runtime error : working outside of the request context.
can someone guide me to where to look for possible solution?
To things to comment here:
First, it seems that you'll be just fine by using requests, a highly recommended library for HTTP requests. With it you could do:
import requests
url = "http://169.254.169.254/latest/meta-data/"
resp = requests.get(url)
print resp.text
Regards to the error you're getting runtime error : working outside of the request context, is because by testOutput = testRequest() you're calling a method that's part of the Flask app app. Another thing related to the error is that you never ran the Flask app. To do this, include this at the end of your code.
if __name__ == '__main__':
app.run()
But again, Flask is rather a web framework that it's useful to create web sites, APIs, web apps, etc. It's very useful, but I don't think you may need it for what you're trying to achieve.
Further info about requests and Flask:
http://docs.python-requests.org/
http://flask.pocoo.org/
Since you only need to make an HTTP GET request and print the response, you don't need Flask. You can use the urllib standard library to send the GET request (https://docs.python.org/3/library/urllib.request.html):
import urllib.request
def testRequest():
url1 = "http://169.254.169.254/latest/meta-data/"
response = urllib.request.urlopen(url1)
nameText = response.read().decode('utf-8')
return nameText
testOutput = testRequest()
print testOutput