Search code examples
pythonflaskdecorator

"TypeError: got an unexpected keyword argument" when trying to use Flask.route() in python


I'm running the following this code for a codding exercise:

from flask import Flask
from random import randint

rand_n = randint(0, 9)
app = Flask(__name__)


def decorator_header(function):
    def wrapper():
        return f"<h1>{function()}</h1>"
    return wrapper


def check_decorator(function):
    def wrapper_function(*args):
        result = function(args[0])
        if result == "You found me!":
            return f"<b style='color:green;'>{result}</b>"
        if result == "Too low, try again!":
            return f"<b style='color:red;'>{result}</b>"
        return f"<b style='color:purple;'>{result}</b>"
    return wrapper_function


@app.route('/')
@decorator_header
def guess_number():
    return 'Guess a number between 0 and 9'


@app.route('/<number>', endpoint='check_number')
@check_decorator
def check_number(number):
    if int(number) < rand_n:
        return "Too low, try again!"
    if int(number) > rand_n:
        return "Too high, try again!"
    return "You found me!"


if __name__ == "__main__":
    app.run()

If I run the code like this, at first it works: I can load the page that says "Guess a number between 0 and 9" on my browser. Now, when I try to add the number in the url, like URL/1 for example, I get the following error:

[2023-02-19 21:16:23,710] ERROR in app: Exception on /1 [GET]
Traceback (most recent call last):
  File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 2528, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\pauLo\venv\lib\site-packages\flask\app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
TypeError: wrapper_function() got an unexpected keyword argument 'number'
127.0.0.1 - - [19/Feb/2023 21:16:23] "GET /1 HTTP/1.1" 500 -

If I just change the argument of the wrapper function, like so:

    def wrapper_function(number):
        result = function(number)

then it works fine. In fact, if I use anything else other than "number", it gives the error above.

Also, if I run the code without using Flask, with the decorator functions like the original and the rest like so:

@decorator_header
def guess_number():
    return 'Guess a number between 0 and 9'


@check_decorator
def check_number(number):
    if int(number) < rand_n:
        return "Too low, try again!"
    if int(number) > rand_n:
        return "Too high, try again!"
    return "You found me!"


guess = input(guess_number())
print(check_number(guess))

It also works just fine. So why do I have to keep the same variable name inside the decoration function? Is Flask.route() returning number=number or something like that? I tried to look into the code for this function, but couldn't figure thigns out.


Solution

  • The problem is that Flask calls your functions with keyword arguments (like check_number(number="1")), whereas your check_decorator wrapper expects only positional arguments (def wrapper_function(*args)).

    You could rewrite it like this and it would work:

    def check_decorator(function):
        def wrapper_function(*args, **kwargs):
            result = function(kwargs['number'])
            if result == "You found me!":
                return f"<b style='color:green;'>{result}</b>"
            if result == "Too low, try again!":
                return f"<b style='color:red;'>{result}</b>"
            return f"<b style='color:purple;'>{result}</b>"
        return wrapper_function
    

    But for what you're doing it doesn't make any sense to implement this logic in a decorator -- it should just be part of your check_number method.