Python Flask 0.10: fix exception being thrown - python

Flask introduced the following change in 0.10:
Flask will now raise an error if you attempt to register a new function on an already used endpoint.
I've used the following code on my home page:
# ...
# ...
# some endpoints registered there
#theApp.route("/<path:filename>")
def static(filename):
if (os.path.isfile("templates/" + filename)):
return render_template(filename)
elif (os.path.isfile("static/" + filename)):
return theApp.send_static_file(filename)
else:
return (render_template("404.html"), 404)
This handler is used to handle everything that exists, no matter static or template.
Now this gives me an exception during startup.
How I can avoid an exception without registering too detailed handlers?

If you dont pass endpoint to route, by default the endpoint is the decorated function name. Flask already has a static endpoint, used for serving files from your static dir. Renaming the function or passing endpoint='mystatic' to the route decorator should fix it.
URL Route Registrations

Related

Hard reload of a FastAPI app page calls my endpoint twice and gives a strange result

I have a FastAPI app that worked fine until now. Here is the main router:
#router.get('/{inst_name}')
def chart(request: Request, inst_name: str):
# Initializing an instance of class 'LiveData'
lv = tse.LiveData(inst_name, 1)
data = lv.get_data_for_charts('1min')
return templates.TemplateResponse('chart.html',
{
'request': request,
'title': inst_name,
'data': data
})
Today I changed Javascript code that renders chart.html and tried to re-run the app. No matter how much I refreshed the page, I could not see the changes made. So, I decided to do a hard reload on the page, but I got a strange result. The router function was run twice ; the first one did everything as I expected, and the second one failed on the assertion check in the class initialization. Here is the __init__ method of the class:
def __init__(self, inst, avg_volume: int):
print('inst name:', inst)
self._inst_name = inst
inst_id = AllInstCodes().get_inst_id_by_name(inst)
assert inst_id is not None, 'Invalid instrument name.'
I put a print statement inside above method to see what causes the assertion to fail. The inst parameter in the second initialization of the class is favicon.ico !!!
Why the router method runs twice and where does favicon.ico come from?
The GET /favicon.ico request is a request your browser makes to retrieve the site's icon that gets shown on the tab in your browser (or in your bookmarks, etc.)
You can ignore this error; it's a request your browser makes in the background and unless you do something very specific with it (i.e. change internal state, etc.) it shouldn't affect anything. You can create an explicit endpoint for it if you want to handle it properly and return a 404 error instead.
The reason why the endpoint runs twice is that there are two requests - your own and the browser's request for favicon.ico.
Most common REST API designs uses the resource group name as the initial part of the path to avoid conflicts with other groups that gets added later, so you'd have:
#router.get('/instruments/{inst_name}')
instead. This will automagically give a 404 error when anyone attempts to request an undefined path.

Hook for processing (not catching) exception in Flask

I have a Flask server.
Whenever the code inside my handler throws an exception, Flask catches it, and returns an HTML page to the client with a 5XX error.
The problem is that I don't notice this. I just got an email from someone using my API saying that they were getting 504 errors, and I didn't know about it until they told me.
In other non-Flask parts of my application I wrote a custom decorator to catch all exceptions, send an email to me, then re-throw. I would like something similar for my Flask app.
I want to find a way to have Flask call a function of mine every time my handler code throws an exception, before it returns a response to the client. I do not wish to modify the response that gets sent to the client. I don't want to change how Flask handles errors, or how it catches them. I just want some way of being notified, and then Flask can continue doing the default error handling behavior.
I suppose I could put a decorator over every single route handler to catch and rethrow exceptions before Flask sees them, but that's messy. I just know I'll forget one of them, especially when I add new ones in the future.
MWE
A buggy application:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
assert False, "buggy code here"
return "hello"
def error_handler(exc_type, exc_val, exc_tb):
send_email(exc_type, exc_val, exc_tb)
# This is the part I don't know
# I want something along the lines of:
app.config['ERROR_HOOK'] = error_handler
from flask import Flask
app = Flask(__name__)
app.debug = False
app.config['PROPAGATE_EXCEPTIONS'] = True
#app.errorhandler(Exception)
def all_exception_handler(error):
print(str(error))
#app.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404
you can define a function for each specific error you want to catch #app.my_custom_errorhandler(code_or_exception)
The argument to your error handler function will be an Exception.

Tornado routing to a "base" handler

I use tornado 4.5.2 with routing implementation.
My server have two versions of API, let them call base and fancy. So a client is able to use both of them:
GET /base/foo
GET /base/baz
GET /fancy/foo
GET /fancy/baz
However, some fancy handlers may not be implemented; In this case a base one should be used.
In example:
application = web.Application([
(r"/base/foo", handlers.BaseFooHandler, {"some": "settings"}),
(r"/base/baz", handlers.BaseBazHandler, {"some": "settings"}),
(r"/fancy/foo", handlers.FancyFooHandler, {"some": "settings"}),
])
when cilent requests GET /fancy/baz the BaseBazHandler should do the job.
How can I achieve that with tornado routing?
Since you're registering your routes using a decorator, you can create a custom router that will respond to all the unmatched/unregistered /fancy/.* routes. For this to work correctly, you'll have to register your router at the end.
That way your custom router will be matched only if there isn't already a /fancy/... route registered. So, that means the custom router class will need to do these things:
Check if a fallback BaseBazHandler exists or not.
If exists, forward the request to it.
Else, return a 404 error.
Before proceeding any further, you'll have to create a custom class to handle 404 requests. This is necessary because if not handler is found, then this is the easiest way to return a 404 error.
class Handle404(RequestHandler):
def get(self):
self.set_status(404)
self.write('404 Not Found')
Okay, now let's write the custom router:
from tornado.routing import Router
class MyRouter(Router):
def __init__(self, app):
self.app = app
def find_handler(self, request, **kwargs):
endpoint = request.path.split('/')[2] # last part of the path
fallback_handler = 'Base%sHandler' % endpoint.title()
# fallback_handler will look like this - 'BaseBazHandler'
# now check if the handler exists in the current file
try:
handler = globals()[fallback_handler]
except KeyError:
handler = Handle404
return self.app.get_handler_delegate(request, handler)
Finally, after you've added all other routes, you can register your custom router:
from tornado.routing import PathMatches
application.add_handlers(r'.*', # listen for all hosts
[
(PathMatches(r"/fancy/.*"), MyRouter(application)),
]
)
I should point out that MyRouter.find_handler, only check checks for handlers in the current module (file). Modify the code to search for handlers in different modules, if you want.

Flask redirect from outside API functions

I have a helper method in a Flask app that is used by several endpoints to get a resource. To avoid having multiple redirect_url calls everywhere, I want to be able to redirect from just this helper method. Throwing a RequestRedirect exception as shown here correctly returns a 301 response, but doesn't set the Content-Location error. To get around this, I added a after_this_request hook that sets the url for that response.
This seems to work correctly, but I was wondering if theres a more elegant way to go about it.
Anonymized helper method:
def get_resource(resource_id):
try:
# Some logic
except:
#after_this_request
def add_header(response):
response.headers['Content-Location'] = url
return response
raise RequestRedirect(new_url='/')
If it is outside of your api, typically you would redirect to a 404 page. Here's a sample:
#app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404

redirect in before_app_request

mod = Blueprint('users', __name__, url_prefix='/users')
#mod.route('/welcome')
def www():
return '<h1>Hello</h1>'
#mod.before_app_request
def before_request():
return redirect(url_for('www'))
This code give me this error:
raise BuildError(endpoint, values, method)
BuildError: ('www', {}, None)
If I try this one, I will get an infinite loop redirect in the browser.
return redirect(url_for('users.www'))
My question is, how can I redirect the before_app_request to another action?
EDIT: log after changes, Now the redirect works, but the error still exists.
http://sharetext.org/dDje
With the way you set this up pretty much every request for this blueprint, including one to 'users.www' - hence the infinite loop redirect. You need some kind of conditions so that it would learn not to redirect and load the /welcome endpoint.
Also, url_for('www') will not work as you intend as you need . prefix to reference the current blueprint. Check the documentation for url_for
Maybe something like:
#mod.before_app_request
def before_request():
if request.endpoint != 'users.www':
return redirect(url_for('.www'))
Do note that you might want to print out the request.endpoint and grab the exact one, I can't remember how they are flask handles/names them internally for blueprints.
Reference: flask before request - add exception for specific route

Categories