Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error with @inject typing and mypy #36

Open
zulrang opened this issue Dec 27, 2022 · 14 comments
Open

Error with @inject typing and mypy #36

zulrang opened this issue Dec 27, 2022 · 14 comments
Assignees
Labels
async bug Something isn't working mypy

Comments

@zulrang
Copy link

zulrang commented Dec 27, 2022

This code works with mypy:

class MyClass:
    pass


di[MyClass] = MyClass()


async def my_async_func(my_class: MyClass) -> str:
    return MyClass.__class__.__name__


async def calling_func() -> str:
    return await my_async_func(di[MyClass])

While this code results in an error:

class MyClass:
    pass


di[MyClass] = MyClass()


@inject
async def my_async_func(my_class: MyClass) -> str:
    return MyClass.__class__.__name__


async def calling_func() -> str:
    return await my_async_func()

mypy error: error: Incompatible types in "await" (actual type "Union[Any, Callable[..., Any]]", expected type "Awaitable[Any]")

Aside from ignoring the calling line, is there any way to make mypy happy with the type returned from the @Inject decorator?

@dkraczkowski
Copy link
Contributor

@zulrang Thanks for raising this issue, I think @Inject decorator return types might need a fix. Will update you later on today on this.

@dkraczkowski dkraczkowski self-assigned this Jan 2, 2023
@dkraczkowski dkraczkowski added async bug Something isn't working mypy labels Jan 2, 2023
@seanlossef-kce
Copy link

Any update on this?

1 similar comment
@andrewbutlerboudakiankce

Any update on this?

@zulrang
Copy link
Author

zulrang commented Apr 22, 2023

I think this will be necessary: https://peps.python.org/pep-0612/

@skewty
Copy link

skewty commented Aug 16, 2023

This is also problematic in PyCharm 2023.,2.

@zulrang were you able to figure out typing using PEP612 that seems to work in PyCharm / VSCode?

Perhaps something more simple (and backwards compatible) is possible using typing.overload?

@andrewbutlerboudakiankce
Copy link

andrewbutlerboudakiankce commented Aug 16, 2023

@overload
def inject(
        _service: None = None,
        alias: Any | None = None,
        bind: Dict[str, Any] | None = None,
        container: Container = di,
        use_factory: bool = False) -> Callable[[ServiceDefinition], ServiceResult]: ...


@overload
def inject(
        _service: ServiceDefinition,
        alias: Any | None = None,
        bind: Dict[str, Any] | None = None,
        container: Container = di,
        use_factory: bool = False) -> ServiceResult: ...

^Try this

@skewty
Copy link

skewty commented Aug 16, 2023

Those overloads are not working for me in PyCharm.. Are they working for you in VSCode?

# Python version 3.11.4
from typing import Any, Callable, Type, TypeVar, cast, overload

from kink import Container as KinkContainer, di as kink_di, inject as kink_inject
from kink.inject import ServiceDefinition, ServiceResult

T = TypeVar("T")


class Container(KinkContainer):
    @overload
    def __getitem__(self, key: str) -> Any:
        ...

    @overload
    def __getitem__(self, key: Type[T]) -> T:
        ...

    def __getitem__(self, key):
        return super().__getitem__(key)


di = cast(Container, kink_di)


@overload
def inject(
    _service: None = None,
    alias: Any | None = None,
    bind: dict[str, Any] | None = None,
    container: Container = di,
    use_factory: bool = False,
) -> Callable[[ServiceDefinition], ServiceResult]:
    ...


@overload
def inject(
    _service: ServiceDefinition,
    alias: Any | None = None,
    bind: dict[str, Any] | None = None,
    container: Container = di,
    use_factory: bool = False,
) -> ServiceResult:
    ...


def inject(
    _service: ServiceDefinition = None,
    alias: Any = None,
    bind: dict[str, Any] = None,
    container: Container = di,
    use_factory: bool = False,
):
    return kink_inject(_service, alias, bind, container, use_factory)


@inject
class TestA:
    def as_dict(self) -> dict:
        return self.__dict__


@inject()
class TestB:
    def as_dict(self) -> dict:
        return self.__dict__


di[bytes].|    # auto-complete  -- but inject decorator not used
di[TestA].|    # no auto-complete
di[TestB].|    # no auto-complete

@seanlossef-kce
Copy link

Those overloads are working for me in vscode

@seanlossef-kce
Copy link

To clarify, those overloads fix the issue where mypy cannot resolve the return type of an injected function because the injected parameters are not being explicitly passed in.

It seems like the issue you're talking about might be different. I also do not get intellisense on the di container itself when doing di[TestA].

@andrewbutlerboudakiankce

Adding inject overloads as well as overloads in your snippet to __getitem__, as well as an additional overload to __setitem__ as follows appears to work in VS Code for autocomplete:

    @overload
    def __setitem__(self, key: Type[T], value: T) -> None:
        ...

    @overload
    def __setitem__(self, key: str, value: Any) -> None:
        ...

@skewty
Copy link

skewty commented Sep 19, 2023

Played around in PyCharm a bit and this is what actually got type hinting working:

T = TypeVar("T")

@overload
def inject(
    _service: None = None,
    alias: Any | None = None,
    bind: dict[str, Any] | None = None,
    container: Container = di,
    use_factory: bool = False,
) -> Callable[[T], T]:
    ...


# @overload
# def inject(
#     _service = None,
#     *,
#     alias: Any | None = None,
#     bind: dict[str, Any] | None = None,
#     container: Container = di,
#     use_factory: bool = False,
# ) -> Callable[[T], T]:
#     ...


def inject(*args, **kwargs):
    return kink_inject(*args, **kwargs)

If you un-comment the middle overload it breaks di[TestA] type hinting.

@dkraczkowski
Copy link
Contributor

@seanlossef-kce The Issue with intellisense from the container as of 0.7.0 should be fixed now (also added auto-wiring for optional types).

With inject solution I would like to play a bit with this. Unless anyone is willing to prepare a PR with tests, which would help me a lot as time is the essence ;)

Thanks for your understanding and support!

@skewty
Copy link

skewty commented Feb 17, 2024

Any progress on the type hints for @Inject? This issue is now over a year old.

For me, the typing hinting issues and mypy issues make kink unusable in projects and I am guessing many others feel the same.
Nobody likes linter overrides all over their code.

@dkraczkowski
Copy link
Contributor

@skewty I'd like to shed some light on the management and contribution process for this, and potentially other open-source projects. It's important to note that there's a limited number of contributors, particularly those offering improvements on the technical side. I'm not compensated anyway for the work I am doing here. My contributions are done in my personal time, outside of professional obligations and amidst other demands of life, which have been lately considerable.

This means my capacity to address problems, roll out new features, or respond quickly might be limited. However, I'm always open to contributions from the community. Since this is an open-source project, you're more than welcome to contribute by submitting your PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
async bug Something isn't working mypy
Projects
None yet
Development

No branches or pull requests

5 participants