yield वाली Dependencies¶
🌐 एआई और मनुष्यों द्वारा किया गया अनुवाद
यह अनुवाद मनुष्यों के मार्गदर्शन में एआई द्वारा किया गया है। 🤝
इसमें मूल अर्थ को गलत समझने या अप्राकृतिक लगने आदि जैसी गलतियाँ हो सकती हैं। 🤖
आप हमें एआई LLM को बेहतर मार्गदर्शन करने में मदद करके इस अनुवाद को बेहतर बना सकते हैं।
FastAPI ऐसी dependencies को support करता है जो पूरा होने के बाद अतिरिक्त steps करती हैं।
ऐसा करने के लिए, return की जगह yield का उपयोग करें, और अतिरिक्त steps (code) उसके बाद लिखें।
सुझाव
हर dependency में yield का उपयोग केवल एक बार करना सुनिश्चित करें।
तकनीकी विवरण
कोई भी function जो इनके साथ उपयोग करने के लिए valid है:
वह FastAPI dependency के रूप में उपयोग करने के लिए valid होगी।
वास्तव में, FastAPI internally इन दोनों decorators का उपयोग करता है।
yield वाली database dependency¶
उदाहरण के लिए, आप इसका उपयोग database session बनाने और पूरा होने के बाद उसे close करने के लिए कर सकते हैं।
yield statement से पहले और उसे शामिल करते हुए केवल वही code response बनाने से पहले execute होता है:
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
yield किया गया value वही होता है जिसे path operations और अन्य dependencies में inject किया जाता है:
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
yield statement के बाद वाला code response के बाद execute होता है:
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
सुझाव
आप async या regular functions का उपयोग कर सकते हैं।
FastAPI हर एक के साथ सही काम करेगा, ठीक normal dependencies की तरह।
yield और try वाली dependency¶
अगर आप yield वाली dependency में try block का उपयोग करते हैं, तो dependency का उपयोग करते समय throw की गई कोई भी exception आपको मिलेगी।
उदाहरण के लिए, अगर बीच में किसी point पर, किसी दूसरी dependency में या किसी path operation में, कुछ code ने database transaction को "rollback" किया या कोई अन्य exception बनाई, तो आपको अपनी dependency में वह exception मिलेगी।
इसलिए, आप except SomeException के साथ dependency के अंदर उस specific exception को देख सकते हैं।
इसी तरह, आप finally का उपयोग यह सुनिश्चित करने के लिए कर सकते हैं कि exit steps execute हों, चाहे exception आई हो या नहीं।
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
yield वाली Sub-dependencies¶
आपके पास किसी भी size और shape की sub-dependencies और sub-dependencies के "trees" हो सकते हैं, और उनमें से कोई भी या सभी yield का उपयोग कर सकती हैं।
FastAPI यह सुनिश्चित करेगा कि yield वाली हर dependency में "exit code" सही क्रम में run हो।
उदाहरण के लिए, dependency_c की dependency dependency_b पर हो सकती है, और dependency_b की dependency_a पर:
from typing import Annotated
from fastapi import Depends
async def dependency_a():
dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()
async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):
dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)
async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):
dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends
async def dependency_a():
dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()
async def dependency_b(dep_a=Depends(dependency_a)):
dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)
async def dependency_c(dep_b=Depends(dependency_b)):
dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)
और वे सभी yield का उपयोग कर सकती हैं।
इस case में dependency_c को अपना exit code execute करने के लिए dependency_b से मिला value (यहाँ dep_b नाम दिया गया है) अभी भी उपलब्ध चाहिए।
और, बदले में, dependency_b को अपने exit code के लिए dependency_a से मिला value (यहाँ dep_a नाम दिया गया है) उपलब्ध चाहिए।
from typing import Annotated
from fastapi import Depends
async def dependency_a():
dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()
async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):
dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)
async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):
dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends
async def dependency_a():
dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()
async def dependency_b(dep_a=Depends(dependency_a)):
dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)
async def dependency_c(dep_b=Depends(dependency_b)):
dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)
इसी तरह, आपके पास कुछ dependencies yield वाली और कुछ अन्य dependencies return वाली हो सकती हैं, और उनमें से कुछ बाकी कुछ पर depend कर सकती हैं।
और आपके पास एक single dependency हो सकती है जिसे yield वाली कई अन्य dependencies required हों, आदि।
आप dependencies के कोई भी combinations रख सकते हैं।
FastAPI यह सुनिश्चित करेगा कि सब कुछ सही क्रम में run हो।
तकनीकी विवरण
यह Python के Context Managers की वजह से काम करता है।
FastAPI इसे हासिल करने के लिए internally उनका उपयोग करता है।
yield और HTTPException वाली Dependencies¶
आपने देखा कि आप yield वाली dependencies का उपयोग कर सकते हैं और ऐसे try blocks रख सकते हैं जो कुछ code execute करने की कोशिश करते हैं और फिर finally के बाद कुछ exit code run करते हैं।
आप raise की गई exception को catch करने और उसके साथ कुछ करने के लिए except का भी उपयोग कर सकते हैं।
उदाहरण के लिए, आप कोई अलग exception raise कर सकते हैं, जैसे HTTPException।
सुझाव
यह थोड़ी advanced technique है, और ज्यादातर cases में आपको वास्तव में इसकी ज़रूरत नहीं होगी, क्योंकि आप अपने application code के बाकी हिस्से के अंदर से exceptions (जिसमें HTTPException भी शामिल है) raise कर सकते हैं, उदाहरण के लिए, path operation function में।
लेकिन अगर आपको इसकी ज़रूरत हो तो यह उपलब्ध है। 🤓
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item
अगर आप exceptions को catch करके उसके आधार पर custom response बनाना चाहते हैं, तो Custom Exception Handler बनाएँ।
yield और except वाली Dependencies¶
अगर आप yield वाली dependency में except का उपयोग करके exception catch करते हैं और उसे फिर से raise नहीं करते (या कोई नई exception raise नहीं करते), तो FastAPI यह notice नहीं कर पाएगा कि कोई exception हुई थी, ठीक वैसे ही जैसे regular Python में होता:
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("Oops, we didn't raise again, Britney 😱")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("Oops, we didn't raise again, Britney 😱")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
इस case में, client को HTTP 500 Internal Server Error response दिखेगा, जैसा कि होना चाहिए, क्योंकि हम HTTPException या उसके जैसी कोई चीज़ raise नहीं कर रहे हैं, लेकिन server के पास कोई logs नहीं होंगे या error क्या था इसका कोई अन्य संकेत नहीं होगा। 😱
yield और except वाली Dependencies में हमेशा raise करें¶
अगर आप yield वाली dependency में exception catch करते हैं, तो जब तक आप कोई दूसरी HTTPException या similar चीज़ raise नहीं कर रहे हैं, आपको original exception को फिर से raise करना चाहिए।
आप raise का उपयोग करके उसी exception को फिर से raise कर सकते हैं:
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("We don't swallow the internal error here, we raise again 😎")
raise
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("We don't swallow the internal error here, we raise again 😎")
raise
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
अब client को वही HTTP 500 Internal Server Error response मिलेगा, लेकिन server के logs में हमारा custom InternalError होगा। 😎
yield वाली dependencies का Execution¶
Execution का sequence कमोबेश इस diagram जैसा है। Time ऊपर से नीचे की ओर चलता है। और हर column code के साथ interact करने या execute करने वाले parts में से एक है।
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
नोट
Client को केवल एक response भेजा जाएगा। यह error responses में से एक हो सकता है या path operation से आया response होगा।
उन responses में से एक भेजे जाने के बाद, कोई अन्य response नहीं भेजा जा सकता।
सुझाव
अगर आप path operation function के code में कोई भी exception raise करते हैं, तो उसे yield वाली dependencies को pass किया जाएगा, जिसमें HTTPException भी शामिल है। ज्यादातर cases में आप चाहेंगे कि yield वाली dependency से वही exception या कोई नई exception फिर से raise करें ताकि यह सुनिश्चित हो सके कि उसे सही तरीके से handle किया गया है।
Early exit और scope¶
आम तौर पर yield वाली dependencies का exit code client को response भेजे जाने के बाद execute होता है।
लेकिन अगर आपको पता है कि path operation function से return करने के बाद आपको dependency का उपयोग करने की ज़रूरत नहीं होगी, तो आप Depends(scope="function") का उपयोग करके FastAPI को बता सकते हैं कि उसे dependency को path operation function के return करने के बाद, लेकिन response भेजे जाने से पहले close करना चाहिए।
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_username():
try:
yield "Rick"
finally:
print("Cleanup up before response is sent")
@app.get("/users/me")
def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]):
return username
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
app = FastAPI()
def get_username():
try:
yield "Rick"
finally:
print("Cleanup up before response is sent")
@app.get("/users/me")
def get_user_me(username: str = Depends(get_username, scope="function")):
return username
Depends() एक scope parameter receive करता है जो यह हो सकता है:
"function": request handle करने वाले path operation function से पहले dependency शुरू करें, path operation function खत्म होने के बाद dependency खत्म करें, लेकिन client को response वापस भेजे जाने से पहले। यानी, dependency function path operation function के around execute होगा।"request": request handle करने वाले path operation function से पहले dependency शुरू करें ("function"उपयोग करने जैसा), लेकिन client को response वापस भेजे जाने के बाद खत्म करें। यानी, dependency function request और response cycle के around execute होगा।
अगर specify नहीं किया गया है और dependency में yield है, तो उसका scope default रूप से "request" होगा।
Sub-dependencies के लिए scope¶
जब आप scope="request" (default) वाली dependency declare करते हैं, तो किसी भी sub-dependency का scope भी "request" होना चाहिए।
लेकिन "function" के scope वाली dependency के पास "function" और "request" scope वाली dependencies हो सकती हैं।
ऐसा इसलिए है क्योंकि किसी भी dependency को sub-dependencies से पहले अपना exit code run करने में सक्षम होना चाहिए, क्योंकि उसे अपने exit code के दौरान अभी भी उनका उपयोग करने की ज़रूरत हो सकती है।
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
yield, HTTPException, except और Background Tasks वाली Dependencies¶
yield वाली Dependencies समय के साथ अलग-अलग use cases cover करने और कुछ issues fix करने के लिए evolve हुई हैं।
अगर आप देखना चाहते हैं कि FastAPI के अलग-अलग versions में क्या बदला है, तो आप इसके बारे में advanced guide में, Advanced Dependencies - yield, HTTPException, except और Background Tasks वाली Dependencies में और पढ़ सकते हैं।
Context Managers¶
"Context Managers" क्या हैं¶
"Context Managers" उन Python objects में से कोई भी हैं जिनका उपयोग आप with statement में कर सकते हैं।
उदाहरण के लिए, आप file पढ़ने के लिए with का उपयोग कर सकते हैं:
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
अंदर से, open("./somefile.txt") एक object बनाता है जिसे "Context Manager" कहा जाता है।
जब with block खत्म होता है, तो यह file को close करना सुनिश्चित करता है, भले ही exceptions आई हों।
जब आप yield वाली dependency बनाते हैं, तो FastAPI internally उसके लिए एक context manager बनाएगा, और उसे कुछ अन्य related tools के साथ combine करेगा।
yield वाली dependencies में context managers का उपयोग करना¶
चेतावनी
यह कमोबेश एक "advanced" idea है।
अगर आप अभी FastAPI शुरू ही कर रहे हैं, तो शायद आप इसे अभी skip करना चाहेंगे।
Python में, आप दो methods वाली class बनाकर: __enter__() और __exit__() Context Managers बना सकते हैं।
आप dependency function के अंदर with या async with statements का उपयोग करके इन्हें yield वाली FastAPI dependencies के अंदर भी उपयोग कर सकते हैं:
class MySuperContextManager:
def __init__(self):
self.db = DBSession()
def __enter__(self):
return self.db
def __exit__(self, exc_type, exc_value, traceback):
self.db.close()
async def get_db():
with MySuperContextManager() as db:
yield db
सुझाव
Context manager बनाने का एक और तरीका है:
इनका उपयोग single yield वाले function को decorate करने के लिए करना।
FastAPI internally yield वाली dependencies के लिए यही उपयोग करता है।
लेकिन आपको FastAPI dependencies के लिए decorators का उपयोग करने की ज़रूरत नहीं है (और आपको नहीं करना चाहिए)।
FastAPI internally आपके लिए यह कर देगा।