Issue
I have a working Flask endpoint decorated with flasgger.swag_from
decorator:
from flasgger import swag_from
@app.route("/jobs", methods=["GET"])
@swag_from('swagger/get_jobs.yml')
def get_jobs(request_id):
# ...
This works as expected.
I'm using DataDog and would like to time every call to this endpoint. Based on this documentation, this can easily be achieved by adding the timed
decorator:
from flasgger import swag_from
from datadog import statsd
@app.route("/jobs", methods=["GET"])
@swag_from('swagger/get_jobs.yml')
@statsd.timed("api_call.duration")
def get_jobs(request_id):
# ...
However - once I do that, I get an error when trying to render the Swagger page:
The log shows the error is a FileNotFoundError
caused by Swagger looking for the yml file in the wrong location:
FileNotFoundError: [Errno 2] No such file or directory: '/usr/local/lib/python3.7/site-packages/datadog/dogstatsd/swagger/backup_sources.yml'
Based on this post, this error sometimes happens when Flask's "static_folder" is configured incorrectly. I've tried changing it, using Flask(__name__, static_folder='./')
, but the same issue persists.
Solution
Found the issue.
Turns out that if you provide a relative path to @swag_from
(as I do above: swagger/get_jobs.yml
), Flassger "guesses" the root path by calling:
filename = os.path.abspath(obj.__globals__['__file__'])
return os.path.dirname(filename)
When I added the @statsd.timed
decorator below the @swag_from
decorator, I'm basically running the statsd decorator "before" the Flassger one - i.e. the Flassger decorator runs over a wrapper function generated by statsd, which means the obj.__globals__['__file__']
is the statsd source file, which will make Flassger look for the swagger file under /usr/local/lib/python3.7/site-packages/datadog/dogstatsd
.
The simple workaround is to reverse the decorator order, i.e. running the Flasgger decorator "before" the statsd one, by placing it second:
@app.route("/jobs", methods=["GET"])
@statsd.timed("api_call.duration") # must be above swag_from
@swag_from('swagger/get_jobs.yml') # must be below statsd.timed
def get_jobs(request_id):
# ...
There might be other solutions - providing an absolute path in swag_from
or somehow setting a root_path
attribute on the get_jobs
method (not sure how to do that).
Answered By - Tzach Zohar
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.