#Flask #Python #Fullstack
Being a FullStack developer is a wonderful experience as I have to juggle between the front-end and back-end tasks simultaneously. With Angular 9 in the front-end and Flask framework in the back-end along with the Oracle DB, I am architecting and developing a project.
Recently, I have encountered a bug from the server-side Flask API (A micro-framework of Python) which falls in lines of — “typeerror parse() got an unexpected keyword argument ‘locations’ in flask-apispec wrapper.py line 46.”
I started debugging the code concerning server logs stack-trace. I have peeked the wrapper.py file in the flask-apispec library package. I have been through the code to understand what it is trying to achieve.
It took me and a couple of other developers, a couple of days to figure out the root cause of the issue — The issue was originating because of the @use_kwargs(LoginSchema()) decorator
We had to follow a trial-and-error approach to rule out the contention of other packages/libraries/code-developed. The following options were identified as potential causes initially
- Docker env
- Latest commits into the develop branch
- Current code in dev etc
- Flask_apispec version (bump-up/down)
Let us create an ‘authentication’ mock project to have a better understanding of the code context. The folder structure to organize the code goes as follows:
The main.py file — Hosts the flask instance of the app and all other packages (Flask_ApiSpec etc) we register our app against
The ‘args’ (arguments) folder hosts the Schemas for our endpoints
The ‘endpoint’ folder hosts the code for the endpoint-view function
The ‘impl’ (implementation) folder hosts the code for the implementation of biz logic for a particular endpoint
Create a Flask app -
- Import the Flask class from the flask package
- Import the FlaskApiSpec from the flask_apispec package
- Register the flask app against app variable against the Flask_apispec
- Register the login_blueprint variable in the FlaskApiSpec object to help create the swagger documentation (Open API spec)
In the below logic, we create a schema for the Login request
- Imports the base ‘Schema’ class from the marshmallow package and extend our LoginSchema class against the Schema class
- Imports the fields and Validation class to perform the necessary operations to register the fields and validate them respectively
- The Validation fn gives us the capability to perform min and max length check operations
- Required = True, makes a field necessary. If the field is not provided in the request body, the marshmallow gives the client 422 HTTP code which stands for an unprocessable entity. It will not allow the request to enter the actual logic
To register a route to a particular endpoint, we develop the following logic in the endpoints folder’s login.py file as follows:
- Import the Blueprint from flask, to register all the endpoints of an app against it
- Import use_kwargs, to read the request.body params of the POST request (‘/login’)
- Register a blueprint named ‘login’
- Decorate the endpoints with @doc(type=[‘endpoint-type’]) to generate the swagger documentation at location http://localhost:5000//swagger or http://localhost:5000/swagger-ui
- Import the verify_login_credentials method from the /impl folder to perform the verification logic
From the Flask-ApiSpec documentation, it is clear that the: “flask-apispec automatically generates Swagger 2.0 documentation for view functions and classes using apispec.:”
Take a look at the verify_login_credentials logic:
- We have the actual business logic in this file ‘login.py’ under the impl (implementation) folder
- In this case, we need to query the users table to get the username and password details from the DB using the SqlAlchemy ORM
- In the interest of time, I am not going to write the actual logic to perform a DB query
- Assuming that the end-user has provided the valid username and password; the verify_login_credentials returns True
Coming to the error that I have encountered from the @use_kwargs(LoginSchmea()) in the endpoints/login.py file — after hours of debugging, we figured out that the issue was coming from one of the child package dependencies of the flask_apispec package called ‘webargs’
On Feb-27–2019, webargs released their latest V6.0.0 which was a major release from the previous stable V5.5.3
FYI, the below is the changelog history of the webargs package from PyPI
In the flask_apispec meta, the package versions we mentioned as webargs>=5.5.3, which led to the installation of 6.0.0 version
To resolve the issue, we had to bump-down the version of webargs to 5.5.3 using webargs==5.53 in the requirements.txt file to command Docker to install the exact version
As the context to find the root-cause was limited, I had to dig through different parts of code and try out different ways to get a better context and eliminate other possibilities.
I wanted to share this article along with the code context for a better understanding of the issue at hand. Moreover, there are no StackOverflow questions for this issue yet as it is a relatively new one.
I want to conclude by saying that an error is not always because of the code you write, it can be because of any component in the project. In this case, it is because of metadata of a third-party library.
I hope this article will save some precious dev time and rescue you from the situation I was in.
Thanks for your time to read the complete article. Hope you have a great time ahead