FastAPI parallel requests not working in Python - python

from fastapi import FastAPI
import uvicorn
app = FastAPI()
#Some long processing code, with async declaration
async def doBTask():
print ("Request started")
#asyncio.sleep(20)
sum = 0
for n in range (1, 10000):
for m in range(1,10000):
if m%n ==0:
pass
else:
sum = sum + n
print ("Completed")
return sum
#app.get("/")
async def read_root():
result = await doBTask()
return {"Result": result}
uvicorn.run(app, host="0.0.0.0", port=7000)
When we call that URL in different tabs, the output is coming one after another (even print also). It is clearly single processing but not parallel request processing.
Can someone guide where is the mistake, and why FastAPI with async and await not working?
Edit
Here is the response I am getting if tried in different browsers and dummy params (you can see, it is processing one after another, even though I sent all at a time)
Request started
Completed
INFO: 127.0.0.1:58998 - "GET /?n=10 HTTP/1.1" 200 OK
Request started
Completed
INFO: 127.0.0.1:59012 - "GET /?m=30 HTTP/1.1" 200 OK
Request started
Completed
INFO: 127.0.0.1:59288 - "GET / HTTP/1.1" 200 OK

Related

Why does my python HttpServer dump all received requests on close?

I have the following request handler:
class MyHandler(BaseHTTPRequestHandler):
def do_POST(self) -> None:
if self.path == "/myRoute":
self.handle_route()
def handle_route(self):
print("Handling /myRoute")
received_bytes = self.rfile.read(int(self.headers['Content-Length']))
data = json.loads(received_bytes.decode('utf8').replace("'", '"'))
logger.info(json.dumps(data, indent=4, sort_keys=True))
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(bytes("{\"Success\":true,\"Exception\":\"\"}", "utf-8"))
I am successfully handling POSTs to the route /myRoute. However, when I close after the following usage:
my_server = server.HTTPServer(("localhost", 8080), MyHandler)
my_thread = threading.Thread(target=my_server.serve_forever)
my_thread.daemon = True
my_thread.start()
# do some work
my_server.server_close()
I see a "dump" of all the POSTs received. This makes me think I am not properly flushing my response(s) back to the client:
127.0.0.1 - - [04/Aug/2020 12:01:58] "POST /myRoute HTTP/1.1" 200 -
127.0.0.1 - - [04/Aug/2020 12:02:35] "POST /myRoute HTTP/1.1" 200 -
127.0.0.1 - - [04/Aug/2020 12:03:11] "POST /myRoute HTTP/1.1" 200 -
.
.
.
What makes me think this even more is that it is the same number of requests I received during the execution of my application.
Am I properly sending my responses back to the client?
To fix this, I simply overrided the BaseHttpRequestHandler.log_message() to do nothing:
def log_message(self, format: str, *args: Any) -> None:
"""
Override of BaseHTTPRequestHandler to skip logging of messages.
Parameters
----------
format: str
The str format.
args: Any
The values to log.
"""
Then I only logged what I was interested in via the typical logger = logging.getLogger(__name__) in my route handling methods.

Multiple POST request handling concurrently in Python 2.7

I have a tornado server listening to port 6789 for POST requests on "/train" and "/predict". train method might take upto 3 hrs to complete, while predict might return in 2 minutes. I want them to be handled concurrently. So even when "/train" is running, if a POST request for "/predict" arrives, it can handle that concurrently and return its output without waiting for "/train" to complete.
I have tried using ThreadPool but it still doesn't run concurrently.
My present code is as follows. It functions but if request to train is made and then request to predict is made. It waits for train to complete before handling predict. Assume train and predict functions are present and don't take any parameters.
import logging
import time
import threading
from multiprocessing.pool import ThreadPool
import flask
from tornado import wsgi, httpserver, ioloop
from flask import Flask
from train_script import train
from predict_script import predict
app = Flask(__name__)
#app.route("/train", methods=['POST'])
def train_run():
payload = flask.request.get_json(silent=True)
if payload is not None:
try:
async_result = pool.apply_async(train)
response = async_result.get()
resp = flask.jsonify(response)
resp.status_code = 200
except Exception as ex:
resp = flask.jsonify({"status": "Failure"})
resp.status_code = 500
else:
resp = flask.jsonify({"status": "Failure"})
resp.status_code = 500
return resp
#app.route("/predict", methods=['POST'])
def predict_run():
payload = flask.request.get_json(silent=True)
if payload is not None:
try:
async_result = pool.apply_async(predict)
response = async_result.get()
resp = flask.jsonify(response)
resp.status_code = 200
except Exception as ex:
resp = flask.jsonify({"status": "Failure"})
resp.status_code = 500
else:
resp = flask.jsonify({"status": "Failure"})
resp.status_code = 500
return resp
if __name__ == "__main__":
port = 6789
http_server = httpserver.HTTPServer(wsgi.WSGIContainer(app))
pool = ThreadPool(processes=10)# Expects max concurrent requests to be 10
http_server.listen(port)
logging.info("Tornado server starting on port {}".format(port))
ioloop.IOLoop.instance().start()
Tornado's WSGIContainer does not support any kind of concurrency. Either use Tornado's RequestHandler interfaces without Flask or WSGI, or use Flask with gunicorn or uwsgi. You gain almost nothing and lose a lot by combining Tornado with WSGI frameworks, so this is only useful in certain specialized situations.

Why does Flask redirect to "GET /? HTTP/1.1"

I'm writing a simple Flask application in which I want to send some data from the front-end to the Flask app, have it perform some operations, and return new data to the front-end for display. I have made similar applications before, and by returning the POST response object, instead of render_template(), I'm able to simply return the data and do what I want with it on the front-end. However, this time I'm having problems.
I make a POST request from the Jquery in the front-end. Everything seems to work fine, I can see the data being returned in the browser console, except the page reloads before I can display the new data. It reloads to http://xxx.x.x.x:5000/?.
I can see the get request for /? in the Flask console. I'd like to know why it is doing this, and how I can get it to stop.
(I've found this difficult to research because most search engines will silently ignore any question marks in a query.)
Flask app:
import json
from flask import Flask, Response, render_template, request
from src.simple_lookup import analogy_lookup
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def hello_world():
return render_template('index.html', results=['a', 'b'])
#app.route('/get_words_simple', methods=['POST'])
def get_words_simple():
print('request.form:', request.form.keys)
data = analogy_lookup(request.form['keyword'], request.form['base'], request.form['goal'])
resp = Response(json.dumps(data), status=200, mimetype='application/json')
print('data:', data)
print('resp:', resp)
return resp
if __name__ == "__main__":
app.run()
Jquery:
$.post('get_words_simple?', data, function(json, status) {
console.log('response:', json);
if (json.hasOwnProperty('error')) {
$('.results').append('<p>' + json.error);
return;
}
var words = json.words;
$.each(words, function(i, text) {
var p = $("<p>");
p.append(text);
$('.results').append(p);
});
});
Flask console:
127.0.0.1 - - [27/Dec/2018 11:12:21] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Dec/2018 11:12:21] "GET /static/js/main.js HTTP/1.1" 200 -
request.form: <bound method MultiDict.keys of ImmutableMultiDict([('keyword', ''), ('base', ''), ('goal', '')])>
127.0.0.1 - - [27/Dec/2018 11:12:23] "GET /? HTTP/1.1" 200 -
127.0.0.1 - - [27/Dec/2018 11:12:23] "GET /static/js/main.js HTTP/1.1" 200 -
data: ['obtuvo', 'takata', 'stadshypotek', 'kriwet', 'shafiee', 'martorell', 'collum', '29,400', 'muteesa', 'patzek']
resp: <Response 111 bytes [200 OK]>
127.0.0.1 - - [27/Dec/2018 11:12:23] "POST /get_words_simple? HTTP/1.1" 200 -
Problem was that Bootstrap overrides type="submit" button functionality if it's in a form group. So my Jquery was doing everything right, but something about the html of the button was screwing everything up and trying to make a post request in a different way

302 redirection error before 200 success code IN FLASK

I am running an FLASK app where I check the JWT in the index endpoint. The problem was I get 2 response when I execute this end point .
127.0.0.1 - - [06/Dec/2018 17:38:21] "GET / HTTP/1.1" 302 -
127.0.0.1 - - [06/Dec/2018 17:38:21] "GET /home HTTP/1.0" 200 -
My code is
#app.route('/')
def index():
try:
encoded_jwt=request.headers.get('jwt')
print(encoded_jwt)
secret = "-----BEGIN PUBLIC KEY----- ........"
claims = jwt.decode(encoded_jwt, secret)
print(type(claims))
import json
json_parse = json.loads(json.dumps(claims))
email = json_parse['http://wso2.org/claims/emailaddress']
print(email)
roles = json_parse['http://wso2.org/claims/role']
print(roles)
session['email'] = email
if ROLETAGOFADMIN in roles:
role="admin"
elif "" in roles:
role = "employee"
else:
role=None
session['role'] = role
if 'email' in session and (session['role'] == "admin" or session['role'] == "employee" )and request.method == "GET":
if 'lastpage' in session:
lastpage=session['lastpage']
print(lastpage)
session.pop('lastpage')
return redirect(lastpage)
else:
return redirect(url_for('home'))
else:
return "Sorry. Unfortunately You have no access."
except Exception as e:
return redirect(url_for('error'))
My Identity server is redirecting because of the first response. I couldn't find the way to fix that. I am not aware of that the error is occurred because of the try catch . Please help me.
You are using Flask's redirect to issue a redirect which is going to send a 302 response to the client with a Location header instructing the client to go to /home instead. Then the client has to issue the request to this new URL where the client finally gets the 200 response code. That is why you are seeing two requests and the 302 and 200 response codes in the server logs.
This particular line is causing the redirect:
return redirect(url_for('home'))
It seems like you expected redirect to simply render the content of /home and return that as the response with the original request to / (e.g. a single 200 response). If that's what you actually want, you could instead use render_template (or whatever you use in /home to render your content) to directly render that page. However, I would recommend keeping the redirect behavior as you have it.

spin up a simple web server that prints post data to console

I've found a number of simple web servers in python that will spin up a quick server and respond to requests simply. What I need is a server that won't just print the IP address, timestamp, method, and response code of each request (127.0.0.1 - - [28/Aug/2017 10:42:11] "POST / HTTP/1.1" 200 -), but also, if a POST request, I need it to print out the POST data.
So for example, if I send a POST request with {"foo":"bar"} in the body of the message, I want the server to print 127.0.0.1 - - [28/Aug/2017 10:42:11] "POST / HTTP/1.1" 200 - {"foo":"bar"} to the console before responding.
I'm not sure how to modify any of the linked options above to do this. If there's another simple option, that would work as well.
To just print out whatever JSON gets sent to a server, build yourself a basic catch-all endpoint and just print the JSON from it. In flask, this looks like the following:
import logging
from flask import Flask, request
app = Flask(__name__)
#app.route('/', methods=['POST', 'GET'], defaults={'path': ''})
#app.route('/<path:path>', methods=['POST', 'GET'])
def index(path):
print("HTTP {} to URL /{} received JSON {}".format(request.method, path, request.get_json()))
return "True"
Here's my calls to the server:
In [15]: requests.post('http://localhost:5000/', json={'a': 1})
Out[15]: <Response [200]>
In [16]: requests.post('http://localhost:5000/some/endpoint', json={'a': 1})
Out[16]: <Response [200]>
In [17]: requests.get('http://localhost:5000/', json={'a': 1})
Out[17]: <Response [200]>
Here's the server output:
In [7]: app.run(host='0.0.0.0')
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
HTTP POST to URL / received JSON {'a': 1}
127.0.0.1 - - [28/Aug/2017 11:57:49] "POST / HTTP/1.1" 200 -
HTTP POST to URL /some/endpoint received JSON {'a': 1}
127.0.0.1 - - [28/Aug/2017 11:57:51] "POST /some/endpoint HTTP/1.1" 200 -
HTTP GET to URL / received JSON {'a': 1}
127.0.0.1 - - [28/Aug/2017 11:57:55] "GET / HTTP/1.1" 200 -
Original Answer
A simple decorator should do the trick:
from flask import Flask, request
app = Flask(__name__)
def print_if_post(*args, **kwargs):
def inner_decorator(f):
def inner(*args, **kwargs):
if request.method == 'POST':
json = request.get_json()
print("JSON Data: {}".format(json))
return f(*args, **kwargs)
return app.route(*args, **kwargs)(inner)
return inner_decorator
This decorator will function exactly like app.route, but will print any JSON data sent to its endpoint:
#print_if_post('/', methods=['POST', 'GET'])
def index():
return "True"
Called with the following code:
In [4]: requests.get('http://localhost:5000/')
Out[4]: <Response [200]>
In [5]: requests.post('http://localhost:5000/', json={'a': 1})
Out[5]: <Response [200]>
Server outputs:
In [2]: app.run(host='0.0.0.0')
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [28/Aug/2017 11:03:11] "GET / HTTP/1.1" 200 -
JSON Data: {'a': 1}
127.0.0.1 - - [28/Aug/2017 11:03:23] "POST / HTTP/1.1" 200 -

Categories