How do I run an action for all requests in Flask? - python

I have some code I want to run for every request that comes into Flask-- specifically adding some analytics information. I know I could do this with a decorator, but I'd rather not waste the extra lines of code for each of my views. Is there a way to just write this code in a catch all that will be applied before or after each view?

Flask has dedicated hooks called before and after requests. Surprisingly, they are called:
Flask.before_request()
Flask.after_request()
Both are decorators:
#app.before_request
def do_something_whenever_a_request_comes_in():
# request is available
#app.after_request
def do_something_whenever_a_request_has_been_handled(response):
# we have a response to manipulate, always return one
return response

Related

How to call a Python Bottle API from within that application

I have created an API using the Bottle Library in Python. It endpoints look like this
#app.get('/api/users/<id>', name='user')
def get_user(model, id):
user = model.get_user(id)
if not user:
return HTTPError(404, 'User not found')
else:
response.content_type = "application/json"
return json.dumps(user)
I want to call the API in other functions within the same app
#app.route('/users/<id>')
def users (id=1):
user = request.get("http://localhost:8001/api/user/1")
return template('user', user=user)
However, this is showing no results. The request get timed out each time
So my question is, how to call a bottle API from within that app using Requests library or through any other means.
Are you running Bottle in single-threaded mode (the default)? If so, then your internal get request will hang forever. This is because your server can serve only one request at a time, and you are asking it to handle two at once: the first call to /users/<id>, and then the second call to /api/users/<id>.
A band-aid fix would be to run the server in asynchronous mode. Try this method and see if your timeouts go away:
run(host='0.0.0.0', port=YOUR_PORT_NUMBER, server='gevent')
However: You shouldn't be designing your application this way in the first place. Instead, refactor your code so that both methods can call a function that returns the JSON representation of a user. Then one api call can return that raw json object, while the other api call can present it as HTML. NOTE: that's not how I would design this API, but it's the answer that's the shortest distance from how you've structured your application so far.

How do I replace a decorator at runtime?

I'm trying to adapt another StackOverflow answer on conditionally applying a decorator to only require login for a specific environment (eventually a staging environment, but development until I get this working). Toward that end, I started with the following
auth = HTTPDigestAuth()
def login_required(dec, condition):
def decorator(func):
if not condition:
return func
return dec(func)
return decorator
#bp.route('/auth')
#login_required(auth.login_required, current_app.config['ENV'] != 'development')
def auth_route():
return current_app.config['ENV']
When I launch the server, I get a RuntimeError: Working outside of application context error. After trying a few suggestions from an earlier version of this question, I got the RuntimeError to disappear, but the decorator still isn't being correctly applied when I want. Here's the current version:
def login_required(dec):
def decorator(func):
if not os.environ.get('ENV') != 'development':
return func
return dec(func)
return decorator
#bp.route('/auth')
#login_required(auth.login_required)
def auth_route():
return current_app.config['ENV']
This never returns the auth.login_reqired function. It always lets the browser in without authentication.
So, I tried changing the condition to
if not os.environ.get('ENV') is not None:
and then the authentication shows up.
Yes, I've done an export ENV=development in the shell and confirmed it with the env command. But even then it's not reading the environment variable as I would expect.
Perhaps this is simply the wrong way to go about it? My final goal is to require authentication on one particular environment. Is this possible with the path I'm on? Is it possible at all?
current_app is a context local proxy that only has meaning during a request. This means you can't use it before a request, i.e. as part of a decorator.
Using current_app is generally good practice because Flask allows multiple apps to be configured. In your specific case however, it isn't actually necessary. For instance, the following would work, because it uses the app object directly instead of the current_app proxy:
from yourpackage import app
#bp.route('/auth')
#login_required(auth.login_required, app.config['ENV'] != 'development')
def auth():
return current_app.config['ENV']
Let me paste something from documentation of Flask
Lifetime of the Context
The application context is created and destroyed as necessary. When a Flask application begins handling a request, it pushes an application context and a request context. When the request ends it pops the request context then the application context. Typically, an application context will have the same lifetime as a request.
Now let’s consider how decorators work. It is just a syntactic sugar see this answer.
So the login_required decorator is called while the module is loaded and the current app is not available yet because it’s not handling request.
I would do this way, move condition to the decorator function (relating to your example). It will be called while the request will be handled so you should have access to current_app.

Do I need to use methods=['GET', 'POST'] in #app.route()?

My Forms send the age parameter via GET, and it worked with just this:
#app.route("/foo")
def foo():
age = request.args['age']
I did not bother with
#app.route('/foo', methods=['GET', 'POST'])
Does it matter?
It does not matter, in the sense that it will work. However usually, you would like to have several functions doing different things like. POST to /foo, means that you add an element, GET to /foo means that you retrieve the element(s) and DELETE to /foo means that you delete an element.
If you don't specify a methods argument to app.route(), then the default is to only accept GET and HEAD requests (*).
You only need to explicitly set methods if you need to accept other HTTP methods, such as POST, otherwise Flask will respond with a 405 Method Not Allowed HTTP response code when a client uses a HTTP method you didn't list, and your route function is simply not called.
So if your route should handle both GET and POST requests, but you forgot to add methods=['GET', 'POST'] to #route(), then you have a bug as POST requests result in a 405 response instead of your route handling the request.
In your case, however, you should not use methods=['GET', 'POST'], and instead let clients that try to use POST anyway know your route doesn't handle that method. Better to be explicit about the error than let it silently pass.
(*) HEAD is added whenever you use register a route that handles GET, and in case of a HEAD request, your route is called and only the headers are then served to the client. Flask automatically handles OPTIONS for you, the route is not called in that case.
As always, the answer is: it depends.
If you don't provide "methods" arguments, then Flask assumes the HTTP method is GET (and also accepts HEAD). So long as that assumption is valid, your code will work just fine.
If, however, your web page is communicated as a POST method (or DELETE, etc.), Flask will fail and complain that the POST (or DELETE, etc.) request is not allowed.
Think of this requirement as a redundancy check. Flask could have been written to adapt to whatever method is used in the HTTP request. Instead, Flask insists that you specify the method as a signal that the form of communication is intentional. This requirement makes the Flask implementation a little simpler at the cost of imposing the responsibility of coordinating the client-server interface on the programmer.

Mocking a helper function in django view test

I have a view function that initiates user login requests
it looks something like this:
def initiate_login(request):
# get request parameters
return check_user_and_send_otp(login_id)
The request is then processed by another function
def check_user_and_send_otp(login_id):
# check if user exits
return send_otp_to_user(phone_number)
And then another function
def send_otp_to_user(phone_number):
# sends a message to user
return response
The problem is while testing my code, I don't want to send messages to a phone number while testing.
My login test function looks somewhat like this, is it possible to mock it without changing my code?
def test_login_initiator(self):
response = self.client.post(self.login_url, data=self.login_data, content_type="application/json", **self.headers)
self.assertEqual(response.status_code, 200)
All these functions that were called by others are located in seperate modules
If you don't want to actually receive the message on a physical phone, you can use online sms receivers. Check out this blog.
Additionally, you can send messages through other free online services like Way2sms. You just have to google them up. To do this from within Python, you need to use web parsing using urllib2/requests and beautifulsoup, which is a totally new question.
Or you can skip this function by simply commenting out the message sender code or returning true from the function.
If you wanna live dangerously, think about making a config file which can help you to make switches that tell whether to execute something or not.
The right thing to do would be use something like magic mock and structure the code into proper classes so that we can create mock objects for each of them.

is there a way to partially return results from Python web service....?

I am new to python. I am using Flask for creating a web service which makes lots of api calls to linkedin. The problem with this is getting the final result set lot of time and frontend remains idle for this time. I was thinking of returning partial results found till that point and continuing api calling at server side. Is there any way to do it in Python? Thanks.
Flask has the ability to stream data back to the client. Sometimes this requires javascript modifications to do what you want but it is possible to send content to a user in chunks using flask and jinja2. It requires some wrangling but it's doable.
A view that uses a generator to break up content could look like this (though the linked to SO answer is much more comprehensive).
from flask import Response
#app.route('/image')
def generate_large_image():
def generate():
while True:
if not processing_finished():
yield ""
else:
yield get_image()
return Response(generate(), mimetype='image/jpeg')
There are a few ways to do this. The simplest would be to return the initial request via flask immediately and then use Javascript on the page you returned to make an additional request to another URL and load that when it comes back. Maybe displaying a loading indicator or something.
The additional URL would look like this
#app.route("/linkedin-data")
def linkedin():
# make some call to the linked in api which returns "data", probably in json
return flask.jsonify(**data)
Fundamentally, no. You can't return a partial request. So you have to break your requests up into smaller units. You can stream data using websockets. But you would still be sending back an initial request, which would then create a websocket connection using Javascript, which would then start streaming data back to the user.

Categories

Resources