I was going through a tutorial on fast api and I came across something like below
def get_db():
try:
db = SessionLocal()
yield db
finally:
print("from finally block")
db.close()
@app.get("/")
async def read_all(db: Session = Depends(get_db)):
res = db.query(models.Todos).all()
print("from endpoint")
return res
result
INFO: 127.0.0.1:39088 - "GET /openapi.json HTTP/1.1" 200 OK
from endpoint
INFO: 127.0.0.1:39088 - "GET / HTTP/1.1" 200 OK
from finally block
why does Depends(get_db) seem to act like somekind of contextmanager?.
the "from finally block"
print statement does not get executed until the end of the read_all
method
doing something like
class SomeDependency:
def __enter__(self):
print("entering")
def __exit__(self, exc_type, exc_val, exc_tb):
print("exited")
def hello():
try:
yield SomeDependency()
finally:
print("yolo")
if __name__ == "__main__":
next(hello())
the finally
block gets executed immediately after the call to next
.
what why does the finally
block of the get_db
not execute immediately when passed to Depends
?
CodePudding user response:
You don't seem to clearly understand the concept of the context manager and the generator. Basically they are totally unrelated different concepts. I'll briefly explain them.
The context manager is a part of the with
statement feature, which is a syntax sugar for boiler plate code of try/finally
block ensuring to finalize things. See the reference for detail. In your second example, no context manager is involved because there's no with
statement.
And the generator is a syntax sugar for boiler plate code of defining an iterator.(But it's a quite complex feature called coroutine from the implementation point of view.) See the reference for detail. In your second example, the finally
clause will be executed when an iterator returned by the hello()
is finalized(when the reference count reaches zero).
Although I said the two are totally unrelated before, there exists a little relation actually - a syntax sugar for boiler plate code of defining a context manager class, defining a context manager via a generator, like the following.(It's scrapped from the reference and edited).
from contextlib import contextmanager
@contextmanager
def managed_resource(...):
resource = acquire_resource(...)
try:
yield resource
finally:
release_resource(resource)
with managed_resource(...) as resource:
...
And returning the focus to the FastAPI Depends
, if you give a generator as an argument, it will internally create a context manager using the above feature. You can see the implementation.