Search code examples
pythonflaskwerkzeug

Flask - 'NoneType' object is not callable


I am working on my first Flask application. Taking some code directly out of this, I am trying to make sure that a value is present in the user's cookies.

def after_this_request(f):
    if not hasattr(g, 'after_request_callbacks'):
        g.after_request_callbacks = []
    g.after_request_callbacks.append(f)
    return f

@app.after_request
def call_after_request_callbacks(response):
    for callback in getattr(g, 'after_request_callbacks', ()):
        response = callback(response)
    return response

@app.before_request
def detect_unique_id():
    unique_id = request.cookies.get('unique_id')
    if unique_id is None:
        unique_id = generate_unique_id()
        @after_this_request
        def remember_unique_id(response):
            response.set_cookie('unique_id', unique_id)
    g.unique_id = unique_id

I keep getting this error:

Traceback (most recent call last):
  File "/..../env/lib/python2.7/site-packages/flask/app.py", line 1701, in __call__
    return self.wsgi_app(environ, start_response)
  File "/..../env/lib/python2.7/site-packages/flask/app.py", line 1690, in wsgi_app
    return response(environ, start_response)
TypeError: 'NoneType' object is not callable

I am trying to understand the reason for this error. Please help.


Solution

  • The issue

    remember_unique_id does not return the response object, but call_after_request_callbacks assigns the result of calling each callback added via the after_this_request decorator to result and then returns it. That is to say:

    # This
    for callback in getattr(g, 'after_request_callbacks', ()):
        response = callback(response)
    
    # translates to this
    for callback in [remember_unique_id]:
        response = callback(response)
    
    # which translates to this
    response = remember_unique_id(response)
    
    # which translates to this
    response = None
    

    The solution

    Either:

    • Update remember_unique_id to return the modified response object
    • Update call_after_request_callbacks to check the returned object and make sure it is not None:

      for callback in getattr(g, 'after_request_callbacks', ()):
          result = callback(response)
          if result is not None:
              response = result
      

    Why does this happen?

    Flask is a WSGI application under the covers and it expects response to be a WSGI application (that is, a callable object). When it is handling responses from view templates it runs some checks to make sure that it is being something it can use as a response object and if the returned value is not a WSGI application it converts it to one. It does not check that the response object hasn't been altered by the after_request decorators, and so when it tries to call the response object (which it assumes is a valid WSGI application at this point) you get the TypeError.