beanie icon indicating copy to clipboard operation
beanie copied to clipboard

[BUG] Incorrect type annotations for Document's `insert`, `save` and `replace`

Open CMHopeSunshine opened this issue 10 months ago • 13 comments

Describe the bug when using Document.insert, save or replace vscode pylance report an error like this:

Argument missing for parameter "self" Pylance(reportGeneralTypeIssues)
(method) save: _Wrapped[..., Unknown, (self: Unknown, *args: Unknown, skip_actions: List[ActionDirections | str] | None = None, **kwargs: Unknown), Coroutine[Any, Any, Unknown]]

I found that it was due to the incorrect type annotation of the decorator, such as wrap_with_actions, save_state_after decorator.

If I remove its type comment, it's correct.

For example: https://github.com/roman-right/beanie/blob/main/beanie/odm/utils/state.py#L63C31

def save_state_after(f: Callable):  # remove the 'Callable' annotation
    @wraps(f)
    async def wrapper(self: "DocType", *args, **kwargs):
        result = await f(self, *args, **kwargs)
        self._save_state()
        return result

    return wrapper

This is probably the easiest way. If we want to use annotations, I guess we need to use ParamSpec and TypeVar for more detailed annotations. For example:

from functools import wraps
from typing import Callable, TypeVar
from typing_extensions import ParamSpec

P = ParamSpec("P")
R = TypeVar("R")


def example(some_arg):
    def decorator(f: Callable[P, R]) -> Callable[P, R]:
        @wraps(f)
        def wrapper(*args: P.args, **kwargs: P.kwargs):
            ...
            return f(*args, **kwargs)

        return wrapper

    return decorator

To Reproduce

import asyncio

from beanie import Document, init_beanie
from motor.motor_asyncio import AsyncIOMotorClient


class Foo(Document):
    name: str


async def main():
    client = AsyncIOMotorClient("mongodb://127.0.0.1:27017/")
    await init_beanie(
        database=client.testmango,
        document_models=[Foo],
    )

    foo = Foo(name="test")
    await foo.insert()  # Argument missing for parameter "self"
    await foo.save()  # Argument missing for parameter "self"
    await foo.replace()  # Argument missing for parameter "self"


if __name__ == "__main__":
    asyncio.run(main())

Expected behavior no error report

Additional context

CMHopeSunshine avatar Aug 28 '23 05:08 CMHopeSunshine