BeanieODM / beanie

Asynchronous Python ODM for MongoDB
http://beanie-odm.dev/
Apache License 2.0
1.94k stars 203 forks source link

[BUG] `Link` type annotations don't play well with static type checkers (pylance, mypy) #820

Open ldorigo opened 6 months ago

ldorigo commented 6 months ago

Describe the bug

Links to related documents annotated as Link[xx] get the type Link[xx], which gives type errors when trying to access e.g. obj.linkedobj.

To Reproduce

class ConversationMessage(Document):
    id: UUID4 = Field(default_factory=uuid4)
    conversation: Link["Conversation"]

# ... initialize db etc...

 async def get_message(
        message_id: UUID4, conversation_id: UUID4
    ) -> Optional[ConversationMessage]:
        return await ConversationMessage.find_one(
            ConversationMessage.id == message_id,
            ConversationMessage.conversation.id == conversation_id, # Gives Cannot access member "id" for type Link[Conversation]":  Member "id" is unknown
            fetch_links=True,
        )
roman-right commented 6 months ago

Hi @ldorigo ,

Thank you for the catch!

ldorigo commented 6 months ago

Is there any way or workaround to currently type it properly? I'm having to add #type: ignore all over our codebase 😞

jafonso-learnwise commented 5 months ago

Yeah in general I would like the typing system to look at a Link[T] and recognize it's actually more like a Union[T, Link[T]].

kratsg commented 3 months ago

Just to mention that I've run into this issue as well, so I think the only workaround for now is to ignore the type check on these links or to do casting? That is, either

MyDocument.linked_type # type: ignore[attr-defined]

or

from typing import cast
cast(MyDocument.linked_type, UnderlyingType)
parnell commented 1 month ago

I'd rather have a more robust solution that was built in with Beanie. But I've had success doing the following as a temporary measure.

from typing import TypeAlias
from beanie import Link as BeanieLink

if TYPE_CHECKING:
    _T = TypeVar("_T", bound=Document)
    Link: TypeAlias = _T
else:
    Link = BeanieLink

What it does is when type checking it aliases Link to be the Document class it's wrapping.