简体   繁体   中英

Why doesn't finally block return the latest exception when an exception occurs within an except block?

I had written a piece of code where I'm using traceback to return the exception stacktrace. But I forgot to import traceback.

Filename - trial_main.py

from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn


app = FastAPI()

class RequestJson(BaseModel):
    data: str


@app.post("/trial", status_code=200)
async def trial(req:RequestJson):
    try:
        status = "default"
        resp = {
            "status": status,
            "reason": "default reason"
        }
        
        # error point
        a = 10/0
        
        status = "complete"
        reason = "Finished converting from base64 to image"
        
        resp = {
            "status": status,
            "reason": reason
        }
    except Exception as e:
        status = "incomplete"
        resp =  {
            "status": status,
            "reason": traceback.format_exc(),
        }
    finally:
        return resp


if __name__ == '__main__':
    uvicorn.run("trial_main:app", host="0.0.0.0", port=5001, log_level="info")

What's confusing me is why isn't the code exiting with an exception since traceback module is not found. Rather it returns the default response that was set earlier.

This is the API response that I'm getting -

{
    "status": "default",
    "reason": "default reason"
}

And this is the output on the terminal where I run the uvicorn server -

INFO:     Started server process [14744]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:5001 (Press CTRL+C to quit)
INFO:     127.0.0.1:61809 - "POST /trial HTTP/1.1" 200 OK

Api endpoint -

[POST] http://127.0.0.1:5001/trial

Input to re-create the situation -

{"data": "randomString"}

No mystery is happens here.

In the try block, you successfully assign value to resp variable, then Exception is raised and execution goes to except block. There you're trying to assign new value to resp , but ImportError happens in the right part of statement, so resp still contains old value, which would returned in the finally block. Exception is not propagated further because there is return in the finally block, it's just suppress exception.

To not get distracted by fastapi boilerplate all of this could be illustrated with more simple example

def always_return():
    """return 1"""
    try:
        res = 1
        raise Exception()
    except Exception:
        res = 1 / 0
    finally:
        return res


def never_return():
    """raises ZeroDivisionError"""

    try:
        res = 1
        raise Exception()
    except Exception:
        res = 1 / 0
    finally:
        print("finally block exists, but no return here")
    return res
>>> print(always_return())
1
>>> print(never_return())
finally block exists, but no return here
ZeroDivisionError: division by zero... traceback

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM