How to use application context to mock flask request - python

I am trying to properly unit test my Flask 1.1.2 routes, but do not really understand the documentation around mocking the application context.
Here is my class Launch.py:
from flask import Flask, request
from LaunchHelper import runController
#Create app
app = Flask(__name__)
#app.route('/write-database',methods=['POST'])
def writeDatabase():
return runController(request)
Here is my new TestLaunch.py after following the comment of #Felipe Emirem:
import unittest
from mock import patch, MagicMock
from flask import request #added
from Launch import app #added
class TestLaunch(unittest.TestCase):
#patch('Launch.runController')
def test_writeDatabase(self,runController):
resp = MagicMock()
runController.return_value = resp
with app.test_client() as c:
ret = c.post('/write-database')
#assert ret == resp
runController.assert_called_with(request)
if __name__ == '__main__':
unittest.main()
I can now assert that runController was called with flask.request by importing the app directly from Launch.py, which makes sense. But I am not sure how to confirm that the response is returning correctly.
Additionally I am now getting the following error:
ERROR in app: Exception on /write-database [POST]
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1953, in full_dispatch_request
return self.finalize_request(rv)
File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 1968, in finalize_request
response = self.make_response(rv)
File "C:\ProgramData\Anaconda3\lib\site-packages\flask\app.py", line 2117, in make_response
rv = self.response_class.force_type(rv, request.environ)
File "C:\ProgramData\Anaconda3\lib\site-packages\werkzeug\wrappers\base_response.py", line 269, in force_type
response = BaseResponse(*_run_wsgi_app(response, environ))
File "C:\ProgramData\Anaconda3\lib\site-packages\werkzeug\wrappers\base_response.py", line 26, in _run_wsgi_app
return app_iter, response[0], Headers(response[1])
IndexError: list index out of range
Even though the test is passing.
*Edit: Here is my old test class TestLaunch.py before I changed to use app.test_context():
import unittest
from mock import patch, MagicMock
from Launch import writeDatabase
class TestLaunch(unittest.TestCase):
#patch('Launch.runController')
#patch('Launch.request')
def test_writeDatabase(self,request,runController):
resp = MagicMock()
runController.return_value = resp
ret = writeDatabase()
assert ret == resp
runController.assert_called_with(request)
if __name__ == '__main__':
unittest.main()
I ran this on older versions of flask, and it worked fine, but now I am getting Working out of request context error due to the #patch('Launch.request') line. I have tried to read through other stackoverflow posts and the flask documentation, but I can't really find anything that applies to my current use case.

Ok, I figured it out. Just needed to use the test_request_context which I read about here: link.
Here is my new TestLaunch.py:
import unittest
from mock import patch, MagicMock
from flask import request
from Launch import app, writeDatabase
class TestLaunch(unittest.TestCase):
#patch('Launch.runController')
def test_writeDatabase(self,runController):
resp = MagicMock()
runController.return_value = resp
with app.test_request_context() as c:
ret = writeDatabase()
assert ret == resp
runController.assert_called_with(request)
if __name__ == '__main__':
unittest.main()
I am not testing the URL (which I wasn't originally anyway), but I am able to test that the flask request object is being handled properly.

Related

Possible Python Flask Memory Leak

Hoping someone can help me address this issue.
I've got a very simple Flask server running in a docker container which accepts a new frame every time the /api/feed_image endpoint is called. The image passed through that endpoint then streams to the index.html from /video_feed.
The application works as expected initially, however, when I watch the resources being consumed using docker stats the memory usage slowly climbs until the entire system crashes.
Does anyone know what I could have missed or have any suggestions?
Thanks!
import os
import sys
from azure.iot.device.aio import IoTHubModuleClient
from flask import Response
from flask import request
from flask import Flask
from flask import render_template
import datetime
import time
import numpy as np
import cv2
import queue
frameQueue = queue.Queue()
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/video_feed")
def video_feed():
print(len(frameQueue))
return Response(generate(), mimetype="multipart/x-mixed-replace; boundary=frame")
#app.route("/api/feed_image", methods=['POST'])
def read_image():
r = request
nparr = np.fromstring(r.data, np.uint8)
frameQueue.put(cv2.imdecode(nparr, cv2.IMREAD_COLOR))
def generate():
global frameQueue
while True:
outputFrame = frameQueue.get()
if outputFrame is None:
continue
(flag, encodedImage) = cv2.imencode(".jpg", outputFrame)
if not flag:
continue
yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(encodedImage) + b'\r\n')
if __name__ == "__main__":
app.run(host='0.0.0.0', port='5001', debug=True, threaded=True, use_reloader=False)
After a long time I solved my own problem.
Turns out because I wasn't returning a Response object at the end of read_image(), Flask was generating an exception, which must have been caching in the background:
172.18.0.4 - - [22/Apr/2021 00:09:25] "POST /api/feed_image HTTP/1.1" 500 -
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1953, in full_dispatch_request
return self.finalize_request(rv)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1968, in finalize_request
response = self.make_response(rv)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2098, in make_response
"The view function did not return a valid response. The"
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
Adding a return statement solved the problem.
#app.route("/api/feed_image", methods=['POST'])
def read_image():
...
return Response(status=201)
Perhaps someone would have an explanation as to why this would happen?
Either way, I hope this helps someone!

TypeError while running python webapp, what anamoly in python code could have caused it? [duplicate]

This question already has answers here:
Flask view return error "View function did not return a response"
(3 answers)
Closed 2 years ago.
I wrote the framework for a web app using python flask and PostgreSQL as the back end. It should allow one to submit text to be saved. But when I tried running it, I get the following TypeError:
TypeError: The view function did not return a valid response. The
function either returned None or ended without a return statement.
The traceback to the error page:
Traceback (most recent call last) File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 2464,
in call return self.wsgi_app(environ, start_response) File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 2450,
in wsgi_app response = self.handle_exception(e) File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 1867,
in handle_exception reraise(exc_type, exc_value, tb) File
"/opt/anaconda3/lib/python3.8/site-packages/flask/_compat.py", line
39, in reraise raise value File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 2447,
in wsgi_app response = self.full_dispatch_request() File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 1953,
in full_dispatch_request return self.finalize_request(rv) File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 1968,
in finalize_request response = self.make_response(rv) File
"/opt/anaconda3/lib/python3.8/site-packages/flask/app.py", line 2097,
in make_response raise TypeError( TypeError: The view function did not
return a valid response. The function either returned None or ended
without a return statement. The debugger caught an exception in your
WSGI application. You can now look at the traceback which led to the
error. To switch between the interactive traceback and the plaintext
one, you can click on the "Traceback" headline. From the text
traceback you can also create a paste of it. For code execution
mouse-over the frame you want to debug and click on the console icon
on the right side.
You can execute arbitrary Python code in the stack frames and there
are some extra helpers available for introspection:
dump() shows all variables in the frame dump(obj) dumps all that's
known about the object Brought to you by DON'T PANIC, your friendly
Werkzeug powered traceback interpreter.
My code is the following, I looked for a long time without finding any problems:
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
ENV = 'dev'
if ENV == 'dev':
app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:Gray#localhost/Phict'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Feedback(db.Model):
__tablename__ = 'feedback'
id = db.Column(db.Integer, primary_key=True)
comments = db.Column(db.Text())
def __init__(self, comments):
self.comments = comments
#app.route('/')
def index():
return render_template('index.html')
#app.route('/submit', methods=['POST'])
def submit():
if request.method == 'POST':
comments = request.form['fileToUpload']
data = Feedback(comments)
db.session.add(data)
db.session.commit()
if __name__ == '__main__':
app.run()
as stated by #snakecharmerb in his comment above
The submit function doesn't return anything. It must return something, even if it's just an empty string.
so in your case, you may need to return redirection object to the home page for e.g
#app.route('/submit', methods=['POST'])
def submit():
if request.method == 'POST':
comments = request.form['fileToUpload']
data = Feedback(comments)
db.session.add(data)
db.session.commit()
# you can add a 'flash' message and then redirect to the index page
return redirect(url_for('index')) # -- HERE --
refer to this topic https://flask.palletsprojects.com/en/1.1.x/api/#flask.redirect

How to Mock/Patch App Engine's Oauth Decorator?

App Engine's python client library has made oauth flow really easy with the following decorator.
#decorator.oauth_required
But it's really not straightforward to mock/patch for unit testing. For example in the following get handler, I need to stub out oauth decorator.
from auth import decorator
class ListUsersHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
self.response.write(_RenderUserListTemplate())
I have tried something like below.
from mock import patch
patch('decorator.oauth_required', lambda x: x).start()
import user
class MyTest(unittest.TestCase):
def setUp(self):
app = webapp2.WSGIApplication([('/', user.ListUsersHandler)])
self.testapp = webtest.TestApp(app)
def testListUsersHandler(self):
response = self.testapp.get('/')
self.assertTrue(('list tokens' in response))
But, what I'm seeing this error, which doesn't seem to give much clue.
Traceback (most recent call last):
File "user_test.py", line 44, in testAbc
response = self.testapp.get('/')
File "/usr/local/lib/python2.7/dist-packages/webtest/app.py", line 322, in get
expect_errors=expect_errors)
File "/usr/local/lib/python2.7/dist-packages/webtest/app.py", line 605, in do_request
res = req.get_response(app, catch_exc_info=True)
File "/google_appengine/lib/webob-1.2.3/webob/request.py", line 1292, in send
application, catch_exc_info=True)
File "/google_appengine/lib/webob-1.2.3/webob/request.py", line 1269, in call_application
return (captured[0], captured[1], app_iter, captured[2])
IndexError: list index out of range

Testing Tornado app for 4xx status code

Consider the following Tornado (v 4.0.2) application, which is a little bit modified version of official hello world example:
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.set_status(400)
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
As you can see, the only difference here is set_status call in MainHandler. Now, I save this code into app.py, then I open tests.py and I put there this simple unit test:
import tornado.ioloop
from tornado.httpclient import HTTPRequest
from tornado.testing import AsyncHTTPTestCase, gen_test
from app import application
class SimpleTest(AsyncHTTPTestCase):
def get_app(self):
return application
def get_new_ioloop(self):
return tornado.ioloop.IOLoop.instance()
#gen_test
def test_bad_request(self):
request = HTTPRequest(url=self.get_url('/'))
response = yield self.http_client.fetch(request)
self.assertEqual(response.code, 400)
When I run this test with python -m tornado.test.runtests tests I get the following result:
E
======================================================================
ERROR: test_bad_request (tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/tornado/testing.py", line 118, in __call__
result = self.orig_method(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/tornado/testing.py", line 494, in post_coroutine
timeout=timeout)
File "/usr/local/lib/python2.7/dist-packages/tornado/ioloop.py", line 418, in run_sync
return future_cell[0].result()
File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 109, in result
raise_exc_info(self._exc_info)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 631, in run
yielded = self.gen.throw(*sys.exc_info())
File "tests.py", line 18, in test_bad_request
response = yield self.http_client.fetch(request)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 628, in run
value = future.result()
File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 111, in result
raise self._exception
HTTPError: HTTP 400: Bad Request
----------------------------------------------------------------------
Ran 1 test in 0.022s
FAILED (errors=1)
[E 140929 12:55:59 testing:687] FAIL
Obviously this is correct, because the handler sets 400 status code. But how can I test my application for such case? I think 4xx codes are useful, so I don't want to give them up. However I'm new to Tornado and I wasn't able to find a way to test them. Is there any?
Try this:
#gen_test
def test_bad_request(self):
request = HTTPRequest(url=self.get_url('/'))
with self.assertRaises(tornado.httpclient.HTTPError) as context:
yield self.http_client.fetch(request)
self.assertEqual(context.exception.code, 400)
See the documentation for assertRaises.

request.environ['HTTP_REFERER'] is None

i want to get HTTP_REFERER in python flask framework.
My route is this:
#app.route('/login')
def login():
if authenticateForPanel():
return redirect(url_for("panel"))
else:
ref = request.environ['HTTP_REFERER']
return render_template('login.html',blogOptions = g.blogOptions,ref=ref)
When i execute this,i got KeyError: 'HTTP_REFERER' with the traceback:
Traceback (most recent call last):
File "/Users/ozcan/flask/flask/app.py", line 1823, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/ozcan/flask/flask/app.py", line 1811, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/ozcan/flask/flask/app.py", line 1809, in wsgi_app
response = self.full_dispatch_request()
File "/Users/ozcan/flask/flask/app.py", line 1482, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/ozcan/flask/flask/app.py", line 1480, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/ozcan/flask/flask/app.py", line 1466, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/ozcan/Documents/python/app.py", line 318, in login
ref = request.environ['HTTP_REFERER']
KeyError: 'HTTP_REFERER'
When i first wrote this code it was working.I do not directly call this url.I call localhost:5000/panel and it redirects me to the login method.So basically there should be a referer,am i wrong?
When i print the request.environ['HTTP_REFERER'] it prints None
I also tried with the
ref = request.referrer but it is None
Why it can be happen?Thanks a lot.
The werkzeug Request wrapper, which flask uses by default, does this work for you: request.referrer
from flask import Flask, request
import unittest
app = Flask(__name__)
#app.route('/')
def index():
return unicode(request.referrer)
class Test(unittest.TestCase):
def test(self):
c = app.test_client()
resp = c.get('/', headers={'Referer': '/somethingelse'})
self.assertEqual('/somethingelse', resp.data)
if __name__ == '__main__':
unittest.main()
But in the more general case, to retrieve the value of a dictionary key specifying a default if the key is not present, use dict.get
When i first wrote this code it was working.I do not directly call
this url.I call localhost:5000/panel and it redirects me to the login
method.So basically there should be a referer,am i wrong?
This is not correct. A redirect does not guarantee a referrer. Whether a browser will provide a referrer is browser-specific. Perhaps you were using a different browser that had this behavior at one point? The code you have now is working as best as it can be expected to.
See Will a 302 redirect maintain the referer string?
The only way I could get request.referrer to not be None is to load the page through . Redirecting and making regular old HTTP requests did not include a referrer attribute.
#dm03514 has the right idea, but if you're insistent upon using the environ dictionary, you should be using the get method. Also, how are you testing that it is None, i.e., are you using the interactive interpreter to print the value or are you adding print statements in the body?
An example of how you would do this reasonably:
# snip
else:
ref = request.environ.get('HTTP_REFERER')
# You can specify a default return value other than None like so:
# ref = request.environ.get('HTTP_REFERER', '/')
# snip

Categories