I am getting a weird error from Flask. At least weird for me :) I have seen many examples of this error here on stackoverflow but none of them remotely similar to what I have been getting. So, here it goes. I have this multilingual educational software and Flask is the restful api part of it. When I code the blueprint router like below...
@lessons_bp.route("/banner/<lang>", methods=["GET"])
def get_banner(lang):
I don't get any errors. When I add the token required decorator,
@lessons_bp.route("/banner/<lang>", methods=["GET"])
@token_required
def get_banner(lang):
I get "TypeError: get_banner() got multiple values for argument 'lang'" error.
Did any body see something like this? and token required decorator just gets the current user looks sth like..
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None
if "Authorization" in request.headers:
token = request.headers["Authorization"]
if not token:
return make_response(
{"message":"Access token required!"},
401
)
try:
data = jwt.decode(token, JWT_SECRET_KEY, ALGORITHM)
current_user = User.query.filter_by(UserId = data["id"]).first()
except Exception as e:
print(e)
return make_response(
{
"message": "Unable to verify token!"
},
401
)
return f(current_user, *args, **kwargs)
return decorated
and if I add a dummy param to function like
@lessons_bp.route("/banner/<lang>", methods=["GET"])
@token_required
def get_banner(self, lang):
everything works again without errors. Any ideas? What am I doing wrong?
The issue you're encountering is a common pitfall when using decorators in Flask, especially those that modify the signature of the wrapped view function. The root cause of the "TypeError: get_banner() got multiple values for argument 'lang'" error is the way your token_required decorator is constructed and how it calls the decorated function (get_banner in this case).
When you decorate get_banner with @token_required, the decorator adds an additional positional argument (current_user) to the call to get_banner. However, Flask also tries to pass the lang parameter from the URL to get_banner, resulting in two values being passed to the lang parameter: one from the URL and one from the decorator, hence the "multiple values for argument 'lang'" error.
How to Fix It
The key to resolving this issue is to adjust the token_required decorator so that it correctly handles Flask view function arguments. Flask view functions expect keyword arguments for route parameters, so your decorator should inject additional arguments as keyword arguments, not positional arguments.
Here's how you can modify your token_required decorator:
from functools import wraps
from flask import request, make_response
def token_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# Your token validation logic
token = request.headers.get('Authorization')
if not token:
return make_response({"message": "Access token required!"}, 401)
try:
data = jwt.decode(token, JWT_SECRET_KEY, algorithms=[ALGORITHM])
current_user = User.query.filter_by(UserId=data["id"]).first()
except Exception as e:
print(e)
return make_response({"message": "Unable to verify token!"}, 401)
# Inject current_user into kwargs instead of args
kwargs['current_user'] = current_user
return f(*args, **kwargs)
return decorated_function
Adjusting the View Function
Once you've made this adjustment, your view function should be modified to accept current_user as a keyword argument. This means you can define your view function like this:
@lessons_bp.route("/banner/<lang>", methods=["GET"])
@token_required
def get_banner(lang, current_user=None): # current_user is received as a keyword argument
# Your function implementation
This approach keeps your URL parameters and any additional data injected by decorators nicely separated and avoids the type of conflict you were experiencing. It also has the benefit of being more explicit about what parameters your view function expects, improving readability and maintainability.