2025, Oct 03 21:31

FastAPI में SQLAlchemy सेशन डिपेंडेंसी का सही टाइप‑हिंट: Pylance चेतावनी का समाधान

FastAPI में SQLAlchemy sessionmaker के टाइप‑हिंट में Pylance की Variable not allowed in type expression चेतावनी का समाधान: Generator[Session] एनोटेशन से टाइपिंग।

FastAPI‑शैली की सेटअप में SQLAlchemy सेशन डिपेंडेंसी को टाइप‑हिंट करते समय अक्सर Pylance यह चेतावनी दिखाता है: “Variable not allowed in type expression.” यह तब होता है जब आप रिटर्न टाइप को sessionmaker से बने मान से एनोटेट करते हैं, न कि वास्तव में yield होने वाले ऑब्जेक्ट के वास्तविक प्रकार से। जैसे ही आप देखते हैं कि फ़ंक्शन सच में क्या लौटाता है, समाधान साफ दिख जाता है।

समस्या

एक न्यूनतम डिपेंडेंसी पर विचार करें, जो डेटाबेस सेशन खोलती है और उसे yield करती है:

from sqlalchemy.orm import sessionmaker
SessionBuilder = sessionmaker(db_engine, **sa_session_cfg)
def open_session() -> SessionBuilder:
    with SessionBuilder() as conn:
        yield conn

Pylance "-> SessionBuilder" एनोटेशन को “Variable not allowed in type expression.” के साथ फ़्लैग करता है।

मुद्दा क्या है

आप टाइप चेकर से यह खुलववा सकते हैं कि फ़ैक्टरी से वास्तव में किस प्रकार का इंस्टेंस निकलता है। reveal_type का प्रयोग दिखाता है कि sessionmaker इंस्टेंस को कॉल करने पर Session बनता है:

from typing import reveal_type
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
tmp_engine = create_engine("sqlite:///:memory:")
BuildSession = sessionmaker(tmp_engine)
reveal_type(BuildSession())

$ pyright stackoverflow.py
/path/to/example/stackoverflow.py
/path/to/example/stackoverflow.py:8:13 - information: Type of "BuildSession()" is "Session"
0 errors, 0 warnings, 1 information

अगर फ़ंक्शन सामान्य होता (yield नहीं), तो रिटर्न टाइप बस Session होता। लेकिन चूँकि इसमें yield है, यह एक जनरेटर लौटाता है, जिसका yielded मान Session है। इसलिए एनोटेशन को फ़ैक्टरी वाले वेरिएबल पर नहीं, बल्कि yielded प्रकार पर रखना चाहिए।

समाधान

फ़ंक्शन को ऐसे एनोटेट करें कि वह Session को yield करने वाला जनरेटर लौटाता है। इससे चेतावनी हट जाती है और मंशा भी सटीक व्यक्त होती है:

from typing import Generator
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
core_engine = create_engine("sqlite:///:memory:")
SessionFactory = sessionmaker(core_engine)
def provide_session() -> Generator[Session]:
    with SessionFactory() as handle:
        yield handle

यहाँ फ़ंक्शन Session को yield करता है, इसलिए Generator[Session] सही एनोटेशन है। अगर यह जनरेटर न होता और सीधे ऑब्जेक्ट लौटाता, तो टाइप Session होता।

यह क्यों मायने रखता है

सटीक एनोटेशन से टाइप चेकर को नियंत्रण‑प्रवाह समझने में मदद मिलती है और ऊपरी सतह की चेतावनियों के बजाय असली समस्याएँ पकड़ी जाती हैं। इससे यह भी स्पष्ट होता है कि डिपेंडेंसी अपने उपभोक्ताओं को क्या देती है: फ़ैक्टरी नहीं, बल्कि yield किया हुआ Session।

मुख्य बातें

पहले टाइप चेकर से पता करें कि फ़ैक्टरी क्या लौटाती है, फिर उसी प्रकार से एनोटेट करें। जिन डिपेंडेंसीज़ में संसाधन yield किया जाता है, उनके लिए रिटर्न टाइप को उस संसाधन के Generator के रूप में लिखें। इस मामले में सही रूप Generator[Session] है, न कि वह वेरिएबल जिसमें sessionmaker मौजूद है।

यह लेख StackOverflow के एक प्रश्न (लेखक: postcoital-solitaire) और daveruinseverything के उत्तर पर आधारित है।